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  

转载于:https://www.cnblogs.com/bhuztez/archive/2012/04/04/2432343.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值