WindowsAPI编程核心技术-基础扫盲

预备知识

常见参数说明符前缀:

1
2
3
4
5
6
7
8
9
10
11
12
b BOOL 布尔值
g_ Global 全局的
h Handle 句柄
hwnd HWND类型
i Integer 整数
l Long 长整数
lp Long-pointer 长指针
n Short-int 短整型
p Pointer 指针
sz Zero-terminated-String 以0结尾的字符串
u Unsigned-int 无符号整数
w WORD 无符号短整数

常用参数类型:

1
2
3
4
5
6
7
8
9
10
11
HWND 窗口句柄
HINSTANCE 实例句柄
HANDLE 句柄
INT_PTR 约等于int
LPARAM 不好说,一般传地址
LPSTR 字符串指针
LPTSTR 定义了_T宏的同上
LPCTSTR 常量,同上
UINT 32位无符号整型
VOID 等于void
WPARAM 不好说,一般传各种参数

常用宏定义:

1
2
3
4
WINAPI/APIENTRY/CALLBACK __stdcall
_In_ 输入参数,不会改变其值
_In_opt_ 可选的输入参数,不会改变其值
_Out_ 不关心本身内容,写入数据

控制台程序写法:

1
2
3
4
5
6
#include <iostream>
#include <windows.h>
#include <tchar.h>
INT _tmain(INT argc,PTCHAR argv[]){
return 0;
};

Windows窗口程序写法:

1
2
3
4
#include <windows.h>
INT_PTR WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance, LPSTR lpCmdLine, INT nCmdShow) {
return 0;
};

API后缀:

1
2
3
4
A 使用ANSI编码作为标准输入输出流
W 使用Unicode作为编码
Ex 拓展一些参数之类的
ExA ExW 略

基础技术

运行单一实例

CreateMutex

1
2
3
4
5
6
7
8
HANDLE WINAPI CreateMutex(
_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
//如果为NULL则不能由子进程继承
_In_ BOOL bInitialOwner,
//当为TRUE时,调用线程获得互斥锁对象的初始所有权
_In_opt_ LPCTSTR lpName
//对象名称
)//成功则返回新创建的句柄 否则返回NULL且GetLastError返回ERROR_ALREADY_EXIST

例子

1
2
3
4
5
6
7
8
BOOL IsAlreadyRun(VOID) {
HANDLE hMutex = NULL;
hMutex = ::CreateMutex(NULL, FALSE, L"TEST");
if (hMutex)
if (ERROR_ALREADY_EXISTS == ::GetLastError())
return TRUE;
return FALSE;
};

DLL延时加载

工程属性->配置属性->链接器->输入->延迟加载的DLL。

资源释放

从解决方案资源管理器中直接导入即可。

FindResource

确定指定类型和名称的资源所在位置。

1
2
3
4
5
6
7
8
HRSRC FindResource(
_In_ HMODULE hModule,
//处理包含资源的可执行文件模块 当为NULL时从当前进程模块中装载资源
_In_ LPCWSTR lpName,
//指定资源名称
_In_ LPCWSTR lpType
//指定资源类型
)//成功时返回指定资源信息块句柄 失败返回NULL

SizeofResource

获取指定资源的字节数。

1
2
3
4
5
6
DWORD SizeofResource(
_In_ HMODULE hModule,
//同上
_In_ HRSRC hResInfo
//资源句柄 需要FindResource或FindResourceEx来创建
)//成功返回资源字节数 失败返回0

LoadResource

装载指定资源到全局存储器。

1
2
3
4
5
6
HGLOBAL LoadResource(
_In_ HMODULE hModule,
//同上
_In_ HRSRC hResInfo
//同上
)//成功返回相关资源数据句柄 失败返回NULL

LockResource

锁定资源并得到资源在内存中第一个字节的指针,防止其他程序访问。

1
2
3
4
LPVOID LockResource(
_In_ hResData
//装载资源的句柄 通过LoadResource返回
)//当装载资源被锁住时返回值是资源第一个字节的指针 否则为NULL

例子

新版SDK在FindResource的参数_In_ LPCWSTR lpType不能由PCHAR转换而来,可以用`PWCHAR*传参。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <cstdio>
BOOL FreeMyResource(UINT uiResourceName, PWCHAR lpszResourceType, PCHAR lpszSaveFileName) {
HRSRC hRsrc = ::FindResource(NULL, MAKEINTRESOURCE(uiResourceName), lpszResourceType);
if (NULL == hRsrc)
return FALSE;
DWORD dwSize = ::SizeofResource(NULL, hRsrc);
if (0 >= dwSize)
return FALSE;
HGLOBAL hGlobal = ::LoadResource(NULL, hRsrc);
if (NULL == hGlobal)
return FALSE;
LPVOID lpVoid = ::LockResource(hGlobal);
if (NULL == lpVoid)
return FALSE;
PFILE fp = NULL;
fopen_s(&fp, lpszSaveFileName, "wb+");
if (NULL == fp)
return FALSE;
fwrite(lpVoid, sizeof(char), dwSize, fp);
fclose(fp);
return TRUE;
};
int main(int argc,char* argv[]) {
return 0;
};

服务

服务程序编写

服务程序在Vista之后进入Session 0运行,所以普通的对话框UI在Session 0看不到,这里用WTSSendMessage搞一个对话框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <Windows.h>
#include <WtsApi32.h>
#include <tchar.h>
#pragma comment(lib, "WtsApi32.lib")
WCHAR g_szServiceName[MAX_PATH] = TEXT("MyServiceTest");
SERVICE_STATUS g_ServiceStatus = { 0 };
SERVICE_STATUS_HANDLE g_ServiceStatusHandle = { 0 };
VOID APIENTRY ServiceCtrlHandle(DWORD dwOperateCode) {
switch (dwOperateCode) {
case SERVICE_CONTROL_PAUSE: {
// 暂停
g_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
};
case SERVICE_CONTROL_CONTINUE: {
// 继续
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
};
case SERVICE_CONTROL_STOP: {
// 停止
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
break;
};
case SERVICE_CONTROL_INTERROGATE:
// 询问
break;
default:
break;
};
return;
};
VOID APIENTRY ServiceMain(DWORD dwArgc, PCHAR lpszArgv) {
g_ServiceStatus.dwServiceType = SERVICE_WIN32;
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwServiceSpecificExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
g_ServiceStatusHandle = ::RegisterServiceCtrlHandler(g_szServiceName, ServiceCtrlHandle);
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
TCHAR szTitle[] = TEXT("ttestabc");
TCHAR szContent[] = TEXT("ttestabc");
DWORD dwResponse = 0;
DWORD dwSessionId = ::WTSGetActiveConsoleSessionId();
::WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, dwSessionId, szTitle, ::lstrlen(szTitle) << 1, szContent, ::lstrlen(szContent) << 1, 0, 0, &dwResponse, FALSE);
return;
};
int _tmain(_In_ DWORD argc, _In_ PTCHAR argv[]) {
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
SERVICE_TABLE_ENTRY stDispatchTable[] = { {g_szServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},{NULL,NULL} };
::StartServiceCtrlDispatcher(stDispatchTable);
return 0;
};

服务控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <Windows.h>
#include <cstdio>
#include <tchar.h>
#include <Shlwapi.h>
#pragma comment(lib,"shlwapi.lib")
BOOL SystemServiceOperate(PWCHAR lpszExePath, DWORD iOperateType) {
BOOL bRet = TRUE;
WCHAR szName[MAX_PATH] = { 0 };
::lstrcpyA((LPSTR)szName, (LPSTR)lpszExePath);
::PathStripPath(szName);
SC_HANDLE shOSCM = NULL, shCS = NULL;
SERVICE_STATUS ss = { 0 };
DWORD dwErrorCode = 0;
BOOL bSuccess = FALSE;
shOSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (!shOSCM)
return FALSE;
if (0 != iOperateType)
shCS = ::OpenService(shOSCM, szName, SERVICE_ALL_ACCESS);
switch (iOperateType) {
case 0: {
shCS = ::CreateService(shOSCM, szName, szName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, lpszExePath, NULL, NULL, NULL, NULL, NULL);
if (!shCS)
bRet = FALSE;
break;
};
case 1: {
if (!::StartService(shCS, 0, NULL))
bRet = FALSE;
break;
};
case 2: {
if (!::DeleteService(shCS))
bRet = FALSE;
break;
};
default:
break;
};
if (shCS) {
::CloseServiceHandle(shCS);
shCS = NULL;
};
if (shOSCM) {
::CloseServiceHandle(shOSCM);
shOSCM = NULL;
};
return bRet;
};
int _tmain(_In_ DWORD argc, _In_ PTCHAR argv[]) {
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
BOOL bRet = FALSE;
WCHAR szExePath[] = TEXT("D:\\tmp.exe");
bRet = SystemServiceOperate(szExePath, 0);
if (bRet)
printf("INSTALL OK\n");
else
printf("INSTALL FAIL\n");
bRet = SystemServiceOperate(szExePath, 1);
if (bRet)
printf("START OK\n");
else
printf("START FAIL\n");
bRet = SystemServiceOperate(szExePath, 2);
if (bRet)
printf("STOP OK\n");
else
printf("STOP FAIL\n");
bRet = SystemServiceOperate(szExePath, 3);
if (bRet)
printf("UNISNTALL OK\n");
else
printf("UNINSTALL FAIL\n");
return 0;
};

字符串

获取字符串长度

1
2
3
#include <tchar.h>
TCHAR szStr[] = TEXT("你好");
_tprintf(TEXT("_tcslen(szStr)=%d\n"), _tcslen(szStr));

查找字符串中出现指定字符

1
2
3
4
5
6
7
#include <tchar.h>
#include <locale.h>
TCHAR szStr[] = TEXT("xxx");
LPTSTR lp = _tcschr(szStr, TEXT('x'));
setlocale(LC_ALL, "chs"); //需要输出中文字符时
_tprintf(TEXT("szStr地址%p\nlp地址%p\n"), szStr, lp);
_tprintf(TEXT("szStr:%s\nlp:%s\n"), szStr, lp);

字符串中查找另一字符串

1
2
3
TCHAR szStr[] = TEXT("xxx");
TCHAR szStrSearch[] = TEXT("xxx");
_tprintf(TEXT("%s\n"), _tcsstr(szStr, szStrSearch));

字符串中查找另一字符串上任一字符

1
2
3
4
5
6
7
8
TCHAR szStr[] = TEXT("xxx");
TCHAR szStrCharset[] = TEXT("xxx");
LPTSTR lpSearch = NULL;
lpSearch = _tcspbrk(szStr, szStrCharset);
_tprintf(TEXT("%s\n"), lpSearch);
lpSearch++;
lpSearch = _tcspbrk(lpSearch, szStrCharset);
_tprintf(TEXT("%s\n"), lpSearch);

大小写转换

1
2
3
4
_tsetlocale(LC_ALL, TEXT(".UTF8")); //设置本地环境为UTF-8
TCHAR szStr[] = TEXT("xxx");
_tcslwr_s(szStr, _countof(szStr));
_tcsupr_s(szStr, _countof(szStr));

还有字符大小写转换的,分别是totuppertotlower

字符串拼接

1
2
3
4
5
_tsetlocale(LC_ALL, TEXT(".UTF8"));
TCHAR szStrDest[64] = TEXT("xxx");
TCHAR szStrSour[] = TEXT("xxx");
_tcscat_s(szStrDest, _countof(szStrDest), szStrSour); //第二个参数需要缓冲区大小
_tprintf(TEXT("%s\n"), szStrDest);

字符串比较

比较ASCII码值。如果需要比较汉字时,本地环境改为chs,并将_tcscmp直接替换为_tcscoll

1
2
3
4
5
6
7
8
9
10
_tsetlocale(LC_ALL, TEXT(".UTF8"));
TCHAR szStr1[] = TEXT("xxx");
TCHAR szStr2[] = TEXT("xxx");
DWORD n = _tcscmp(szStr1, szStr2);
if (n > 0)
_tprintf(TEXT("szStr1 大于 szStr2\n"));
else if(n<0)
_tprintf(TEXT("szStr1 小于 szStr2\n"));
else
_tprintf(TEXT("szStr1 等于 szStr2\n"));

字符串分割

1
2
3
4
5
6
7
8
9
TCHAR strToken[] = TEXT("xxx");
TCHAR strDelimit[] = TEXT(", \t"); //3种分隔符
LPTSTR lpToken = NULL;
LPTSTR lpTokenNext = NULL;
lpToken = _tcstok_s(strToken, strDelimit, &lpTokenNext); //初次分割用法
while (lpToken != NULL) {
_tprintf(TEXT("%s\n"), lpToken);
lpToken = _tcstok_s(NULL, strDelimit, &lpTokenNext); //第三个参数是上次剩余
};

字符串快速排序

1
2
3
4
5
6
7
8
9
10
11
int CALLBACK compare(LPCVOID arg1, LPCVOID arg2) {
return _tcscoll(*(LPTSTR*)arg1, *(LPTSTR*)arg2);
};
LPCTSTR arrStr[] = {
TEXT("xxx"),
TEXT("xxx"),
TEXT("xxx")
};
qsort(arrStr, _countof(arrStr), sizeof(LPTSTR), compare);
for (int i = 0; i < _countof(arrStr); i++)
_tprintf(TEXT("%s "), arrStr[i]);

二分查找用bsearch函数。

字符串与数值型互转

字符串转数值时,转double用_ttof,转int用_ttoi,转int64/long long用_ttoi64_ttoll。一般这些会跳过开头空格并在非数值处停止,如果字符串开头就不是数字则直接不转换返回0。有安全版本,需要指定缓冲区大小和要转换的进制数(2、8、10、16进制),这里不讲。

1
2
3
4
5
6
7
8
9
#include <stdint.h>
TCHAR str1[] = L"123.45";
double num1 = _ttof(str1);
TCHAR str2[] = L"123";
int num2 = _ttoi(str2);
TCHAR str3[] = L"123456789012345";
int64_t num3 = _ttoi64(str3);
TCHAR str4[] = L"1234567890123456789";
long long num4 = _ttoll(str4);

数值型转字符串:

1
2
3
4
5
6
7
8
9
10
11
12
int num1 = 123;
TCHAR str1[10];
_itow(num1, str1, 10);
wprintf(TEXT("str1: %ls"), str1);
int64_t num2 = 123456789012345;
TCHAR str2[20];
_i64tow(num2, str2, 20);
wprintf(TEXT("str2: %ls"), str2);
uint64_t num3 = 1234567890123456789;
TCHAR str3[20];
_ui64tow(num3, str3, 20);
wprintf(TEXT("str3: %ls"), str3);

字符串格式化

1
2
3
4
5
6
7
8
9
10
11
#include <strsafe.h>
TCHAR szName[] = TEXT("xxx");
TCHAR szSchool[] = TEXT("xxx");
int age = 21;
TCHAR szBuf[128] = { 0 };
HRESULT hResult = E_FAIL;
hResult = StringCchPrintf(szBuf, _countof(szBuf), TEXT("xxx%sxxx%sxxx%d\n"), szName,szSchool,age);
if(SUCCEEDED(hResult))
//...
else
//...