Win32 SDK 開發簡介 [0]


寫一個系列,主要目的是面向主要是向會用 C 語言,會寫一般的 GUI 程序,但不熟悉 Win32 SDK 的開發人員簡單介紹一下 Win32 SDK 開發,同時也是順便把之前沒用到的部分都了解一下。只會涉及到比較常用的幾個部分:

  • 常用 Win32 API
  • COM 接口調用
  • JScript
  • GUI
  • 密碼學 API

這里約定發行版使用 Fedora ,編譯程序用 MinGW ,運行程序用 WINE1。代碼盡量只用 C 語言。另外,源代碼目錄直接運行 mingw32-make,就會完成編譯。

先來看一下如何編譯、鏈接。

Hello World

從經典的 Hello World 開始。Hello World 的代碼沒有任何特別之處。

#include <stdio.h>

int main() {
    printf("%s\n", "Hello, world!");
    return 0;
}

這里用 mingw32-env,僅僅因為它會設置 CC 等環境變量,這樣輸入的命令看起來可以和 makefile 里的比較接近。輸出文件記得加上后綴 .exe,Windows 下可執行文件的文件名通常以 .exe 結尾2

$ mingw32-env
$ ${CC} -o 'hello.exe' 'hello.c'
$ wine 'hello.exe'
Hello, world!

靜態鏈接

靜態鏈接的情況和 Hello World 類似。在 hello.c 中定義 hello 函數。

#include <stdio.h>
#include "hello.h"

void hello() {
    printf("%s\n", "Hello, world!");
}

在頭文件里聲明 hello 函數。

#ifndef __HELLO_H__
#define __HELLO_H__

void hello();

#endif /* __HELLO_H__ */

main.c 里調用一下。

#include "hello.h"

int main() {
    hello();
    return 0;
}

除了輸出文件需要加上后綴 .exe 以外,靜態鏈接的時候並無特殊之處。如果你不是經常用靜態鏈接,可以參考 Program Library HOWTO

$ mingw32-env
$ ${CC} -c -o 'main.o' 'main.c'
$ ${CC} -c -o 'hello.o' 'hello.c'
$ ${AR} r 'libhello.a' 'hello.o'
$ ${CC} -o 'main.exe' -static -L'.' 'main.o' -lhello
$ wine 'main.exe'
Hello, world!

動態鏈接

動態鏈接就有些不同了3。在 main.c 里調用的寫法和靜態鏈接一樣。

#include "hello.h"

int main() {
    hello();
    return 0;
}

頭文件要根據不同情況,把函數分別聲明為 __declspec(dllexport)__declspec(dllimport)

#ifndef __HELLO_H__
#define __HELLO_H__

#ifdef __BUILD_DLL__
#define DLLEXPORT __declspec(dllexport)
#else /* __BUILD_DLL__ */
#define DLLEXPORT __declspec(dllimport)
#endif /* __BUILD_DLL__ */

DLLEXPORT void hello();

#endif /* __HELLO_H__ */

hello.c 里的 hello 函數要和頭文件里的聲明保持一致。

#include <stdio.h>
#include "hello.h"

DLLEXPORT void hello() {
    printf("%s\n", "Hello, world!");
}

編譯 DLL4時,設置宏 __BUILD_DLL__ 。注意 .a 文件要指定-Wl,--out-implib 這個參數才會輸出的,動態鏈接庫的后綴是 .dll

$ mingw32-env
$ ${CC} -c -o 'main.o' 'main.c'
$ ${CC} -c -o 'libhello.o' -D__BUILD_DLL__ 'hello.c'
$ ${CC} -shared -o 'libhello.dll' -Wl,--out-implib='libhello.dll.a' 'libhello.o'
$ ${CC} -o 'main.exe' -L'.' 'main.o' -lhello
$ wine 'main.exe'
Hello, world!

運行期調用

運行期調用動態鏈接庫中的函數,使用的函數5有點特別。用 LoadLibrary 來載入動態鏈接庫,用 GetProcAddress 來找到函數地址。

#include <windows.h>

int main() {
    HMODULE hModule = LoadLibrary("hello.dll");
    if (!hModule) return 1;
    FARPROC hello = GetProcAddress(hModule, "hello");
    hello();
    FreeLibrary(hModule);
    return 0;
}

hello.c 和上一個例子一樣。

#include <stdio.h>

__declspec(dllexport)
void hello() {
    printf("%s\n", "Hello, world!");
}

除了后綴,編譯的命令沒什么特別的。

$ mingw32-env
$ ${CC} -o 'main.exe' 'main.c'
$ ${CC} -shared -o 'hello.dll' 'hello.c'
$ wine 'main.exe'
Hello, world!

資源文件

Windows 可執行文件格式6中比較特殊的一點是資源文件7。其中,字符串資源處理起來要特別小心8。下面就來看一下字符串資源。以下是資源文件 resource.rc

#include <afxres.h>
#include "resource.h"

STRINGTABLE
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
BEGIN
IDS_HELLO    L"Hello, world!"
END

STRINGTABLE
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
BEGIN
IDS_HELLO    L"\x54c8\x56c9\xff0c\x4e16\x754c\xff01"
END

其中的 IDS_HELLO 是在 resource.h 中定義的。

#ifndef _RESOURCE_H_
#define _RESOURCE_H_

#define ID_BASE_RES                     0x100
#define IDS_HELLO                       (ID_BASE_RES+1)

#endif /* _RESOURCE_H_ */

因為歷史原因,為了載入對應的字符串,你唯一能做的就是逐一跳過在它之前的所有字符串。這里偷懶,就把取出來的字符串直接全存到 hStringHeap 里去了。這里用 WinMain9,僅僅是想讓 hInstance 好看點,即使直接用 NULL 是可以的。

#include <stdio.h>
#include <windows.h>

#include "resource.h"

typedef struct {
    UINT uID;
    LPTSTR *table;
} STRING_TABLE;

HANDLE hStringHeap = NULL;

STRING_TABLE *pStringTables = NULL;


LPTSTR* FindStringTable(UINT uID) {
    SIZE_T size = (pStringTables) ?
        HeapSize(hStringHeap, 0, pStringTables)/sizeof(STRING_TABLE):
        0;

    for (int i=0; i<size; i++)
        if (pStringTables[i].uID == uID/16 + 1)
            return pStringTables[i].table;

    if (pStringTables)
        pStringTables = HeapReAlloc(
                hStringHeap,
		HEAP_ZERO_MEMORY,
		pStringTables,
		sizeof(STRING_TABLE) * (size+1));
    else
        pStringTables = HeapAlloc(hStringHeap, HEAP_ZERO_MEMORY, sizeof(STRING_TABLE));

    pStringTables[size].uID = uID/16 + 1;
    pStringTables[size].table = HeapAlloc(
            hStringHeap,
	    HEAP_ZERO_MEMORY,
	    sizeof(LPCTSTR) * 16);
    return pStringTables[size].table;
}


LPCTSTR LoadResourceString(HINSTANCE hInstance, UINT uID) {
    LPTSTR *table = FindStringTable(uID);
    if (table[uID&15])
        return table[uID&15];

    HRSRC hRsrc = FindResource(hInstance, MAKEINTRESOURCE(uID/16+1), RT_STRING);
    HGLOBAL hGlobal = LoadResource(hInstance, hRsrc);
    LPVOID pResource = LockResource(hGlobal);

    LPCWSTR pString = pResource;

    for (int i=0; i<(uID&15); i++)
        pString += 1 + *((WORD *)pString);

    WORD length = *((WORD *)pString);
    pString += 1;

    int buflen = WideCharToMultiByte(CP_OEMCP, 0, pString, length, NULL, 0, NULL, NULL);
    table[uID&15] = HeapAlloc(hStringHeap, HEAP_ZERO_MEMORY, sizeof(CHAR) * (buflen+1));
    WideCharToMultiByte(CP_OEMCP, 0, pString, length, table[uID&15], buflen, NULL, NULL);

    UnlockResource(pResource);
    FreeResource(hGlobal);

    return table[uID&15];
}


int WINAPI WinMain(
        HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR lpCmdLine,
        int nCmdShow) {
    hStringHeap = HeapCreate(0,0,0);
    if (!hStringHeap)
        return 1;

    printf("%s\n", LoadResourceString(hInstance, IDS_HELLO)); 
    HeapDestroy(hStringHeap);
    return 0;
}

windres10 命令生成 .res 文件,這樣在鏈接的時候就可以被加到 .exe 里。

$ mingw32-env
$ ${WINDRES} -i 'resource.rc' --input-format=rc -O coff -o 'resource.res'
$ ${CC} -c -o 'hello.o' -std=c99 'hello.c'
$ ${CC} -o 'hello.exe' 'hello.o' 'resource.res'
$ wine 'hello.exe'
Hello, world!

  1. 你可以檢查一下,看看 mingw32-binutils、mingw32-cpp、mingw32-filesystem、mingw32-gcc、mingw32-runtime、mingw32-w32api、wine、wine-devel 這幾個包是不是都已經裝上了。 

  2. EXE  

  3. HOWTO Create and Deploy a Sample DLL using MinGW  

  4. Dynamic-link library  

  5. Using Run-Time Dynamic Linking  

  6. Portable Executable  

  7. Resource Reference  

  8. The format of string resources  

  9. WinMain entry point  

  10. MS resource compiler  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM