滴水逆向笔记系列-win32总结6-54.事件-55.信号量-56.线程同步与互斥(总结和大作业)
第五十四课 win32 事件
1.什么是内核对象
2.事件内核对象的创建
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性 NULL时为系统默认
BOOL bManualReset, // TRUE 需要手动设置为未通知
BOOL bInitialState, // TRUE 默认已通知状态 FALSE默认未通知状态
LPCTSTR lpName // 对象名称 以NULL结尾的字符串
);
HANDLE g_hEvent = CreateEvent(NULL, TRUE, FALSE, "XYZ");
HANDLE g_hMutex = CreateMutex(NULL,FALSE, "XYZ");
3.事件内核对象的获取
HANDLE OpenEvent(
DWORD dwDesiredAccess, // access
BOOL bInheritHandle, // inheritance option
LPCTSTR lpName // object name
);
HANDLE g_hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "XYZ");
HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ");
4.内核对象的销毁
BOOL CloseHandle(HANDLE hobj);
实验一:
进程一创建内核对象,进程二获取内核对象,进程一销毁内核对象,进程三依旧可以获取内核对象,通俗个人理解:因为有两个人在使用,一个人销毁了,还有另外一个人可以找
- 首先要明白计数器的概念,在高2g内存有一个结构体存储着这些参数,进程一创建对象,计数器+1,进程二获取对象,计数器+1,进程一销毁对象,计数器-1,所以当进程三去获取内核对象时,计数器里面还剩1个可以获取
5.事件对象的控制(已通知、未通知)
BOOL SetEvent(HANDLE hEvent);
//将对象设置为已通知
设置成未通知时,就相当于不会发出信号,当其他线程WaitForSingleObject堵塞时,只有事件对象变成已通知,线程才会继续运行
6.事件对象的实验
实验一:第二个参数TRUE
三个线程都一样,先堵塞,等待事件变成已通知
主要看CreateEvent()第二个参数,TRUE表示需要手动设置未通知状态
结果是三个线程都可以读取到,因为主线程里修改对象状态为已通知时,第一个线程wait到了,执行完后状态依旧是已通知(因为我们第二个参数为TRUE,需要手动使用设置成未通知),所以三个编辑框都可以读取到
实验二:第二个参数FALSE
只要有一个线程wait到了对象状态已经变成已通知,执行完后状态自动变成未通知
实现互斥
总结:
7.线程同步(事件对象的使用)
让线程按顺序执行,比如现在有个线程需要读出数据,另外两个线程对读出来的数据进行修改,这时候无序的线程执行则是不行的
实验:我们想让两个线程交替运行
临界区和互斥体都是只能解决互斥,并不能实现线程同步
//临界区
CRITICAL_SECTION g_cs;
int g_Max = 10;
int g_Number = 0;
//生产者线程函数
DWORD WINAPI ThreadProduct(LPVOID pM)
{
for (int i = 0; i < g_Max; i++)
{
//互斥的访问缓冲区
EnterCriticalSection(&g_cs);
g_Number = 1;
DWORD id = GetCurrentThreadId();
printf("生产者%d将数据%d放入缓冲区\n",id, g_Number);
LeaveCriticalSection(&g_cs);
}
return 0;
}
//消费者线程函数
DWORD WINAPI ThreadConsumer(LPVOID pM)
{
for (int i = 0; i < g_Max; i++)
{
//互斥的访问缓冲区
EnterCriticalSection(&g_cs);
g_Number = 0;
DWORD id = GetCurrentThreadId();
printf("----消费者%d将数据%d放入缓冲区\n",id, g_Number);
LeaveCriticalSection(&g_cs);
}
return 0;
}
int main(int argc, char* argv[])
{
InitializeCriticalSection(&g_cs);
HANDLE hThread[2];
hThread[0] = ::CreateThread(NULL, 0, ThreadProduct, NULL, 0, NULL);
hThread[1] = ::CreateThread(NULL, 0, ThreadConsumer, NULL, 0, NULL);
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
//销毁
DeleteCriticalSection(&g_cs);
return 0;
}
//互斥体
HANDLE hMutex;
int g_Max = 10;
int g_Number = 0;
//生产者线程函数
DWORD WINAPI ThreadProduct(LPVOID pM)
{
for (int i = 0; i < g_Max; i++)
{
//互斥的访问缓冲区
WaitForSingleObject(hMutex,INFINITE);
g_Number = 1;
DWORD id = GetCurrentThreadId();
printf("生产者%d将数据%d放入缓冲区\n",id, g_Number);
ReleaseMutex(hMutex);
}
return 0;
}
//消费者线程函数
DWORD WINAPI ThreadConsumer(LPVOID pM)
{
for (int i = 0; i < g_Max; i++)
{
//互斥的访问缓冲区
WaitForSingleObject(hMutex,INFINITE);
g_Number = 0;
DWORD id = GetCurrentThreadId();
printf("----消费者%d将数据%d放入缓冲区\n",id, g_Number);
ReleaseMutex(hMutex);
}
return 0;
}
int main(int argc, char* argv[])
{
//创建一个互斥体
hMutex = CreateMutex(NULL,FALSE,NULL);
HANDLE hThread[2];
hThread[0] = ::CreateThread(NULL, 0, ThreadProduct, NULL, 0, NULL);
hThread[1] = ::CreateThread(NULL, 0, ThreadConsumer, NULL, 0, NULL);
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
//销毁
CloseHandle(hMutex);
return 0;
}
还有一种办法,使用一个全局变量flag,当flag=1时运行线程1同时把flag改成2,flag=2时运行线程2同时把flag改成1,但是这样效率太低
使用事件对象实现同步
//事件和临界区
HANDLE g_hSet, g_hClear;
int g_Max = 10;
int g_Number = 0;
//生产者线程函数
DWORD WINAPI ThreadProduct(LPVOID pM)
{
for (int i = 0; i < g_Max; i++)
{
WaitForSingleObject(g_hSet, INFINITE);
g_Number = 1;
DWORD id = GetCurrentThreadId();
printf("生产者%d将数据%d放入缓冲区\n",id, g_Number);
SetEvent(g_hClear);
}
return 0;
}
//消费者线程函数
DWORD WINAPI ThreadConsumer(LPVOID pM)
{
for (int i = 0; i < g_Max; i++)
{
WaitForSingleObject(g_hClear, INFINITE);
g_Number = 0;
DWORD id = GetCurrentThreadId();
printf("----消费者%d将数据%d放入缓冲区\n",id, g_Number);
SetEvent(g_hSet);
}
return 0;
}
int main(int argc, char* argv[])
{
HANDLE hThread[2];
g_hSet = CreateEvent(NULL, FALSE, TRUE, NULL);
g_hClear = CreateEvent(NULL, FALSE, FALSE, NULL);
hThread[0] = ::CreateThread(NULL, 0, ThreadProduct, NULL, 0, NULL);
hThread[1] = ::CreateThread(NULL, 0, ThreadConsumer, NULL, 0, NULL);
WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
//销毁
CloseHandle(g_hSet);
CloseHandle(g_hClear);
return 0;
}
第五十五课 win32 信号量
1.信号量函数
创建信号量
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);
函数说明:
第一个参数表示安全控制,一般直接传入NULL。
第二个参数表示初始资源数量。0时不发送信号
第三个参数表示最大并发数量。lInitialCount<=lMaximumCount
第四个参数表示信号量的名称,传入NULL表示匿名信号量。
打开信号量
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
函数说明:
第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。详细解释可以查看MSDN文档。
第二个参数表示信号量句柄继承性,一般传入FALSE即可。
第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。
递增信号量计数器
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);
函数说明:
第一个参数是信号量的句柄。
第二个参数表示增加个数,必须大于0且不超过最大资源数量。
第三个参数返回当前资源数量的原始值,设为NULL表示不需要传出。
2.信号量的应用
#include <Windows.h>
#include <stdio.h>
#include "resource.h"
HANDLE hSemaphore;
HANDLE hThread[5];
HWND hEditSet;
HWND hEdit1;
HWND hEdit2;
HWND hEdit3;
HWND hEdit4;
HWND hEdit5;
HWND hEdit6;
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD dwTimmer = 0;
WaitForSingleObject(hSemaphore, INFINITE);
while (dwTimmer < 100)
{
Sleep(100);
memset(szBuffer, 0, 10);
GetWindowText(hEdit1, szBuffer, 10);
sscanf(szBuffer, "%d", &dwTimmer);
dwTimmer++;
memset(szBuffer, 0, 10);
sprintf(szBuffer, "%d", dwTimmer);
SetWindowText(hEdit1, szBuffer);
}
//ReleaseSemaphore(hSemaphore, 1, NULL);
return 0;
}
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD dwTimmer = 0;
WaitForSingleObject(hSemaphore, INFINITE);
while (dwTimmer < 100)
{
Sleep(100);
memset(szBuffer, 0, 10);
GetWindowText(hEdit2, szBuffer, 10);
sscanf(szBuffer, "%d", &dwTimmer);
dwTimmer++;
memset(szBuffer, 0, 10);
sprintf(szBuffer, "%d", dwTimmer);
SetWindowText(hEdit2, szBuffer);
}
//ReleaseSemaphore(hSemaphore, 1, NULL);
return 0;
}
DWORD WINAPI ThreadProc3(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD dwTimmer = 0;
WaitForSingleObject(hSemaphore, INFINITE);
while (dwTimmer < 100)
{
Sleep(100);
memset(szBuffer, 0, 10);
GetWindowText(hEdit3, szBuffer, 10);
sscanf(szBuffer, "%d", &dwTimmer);
dwTimmer++;
memset(szBuffer, 0, 10);
sprintf(szBuffer, "%d", dwTimmer);
SetWindowText(hEdit3, szBuffer);
}
//ReleaseSemaphore(hSemaphore, 1, NULL);
return 0;
}
DWORD WINAPI ThreadProc4(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD dwTimmer = 0;
WaitForSingleObject(hSemaphore, INFINITE);
while (dwTimmer < 100)
{
Sleep(100);
memset(szBuffer, 0, 10);
GetWindowText(hEdit4, szBuffer, 10);
sscanf(szBuffer, "%d", &dwTimmer);
dwTimmer++;
memset(szBuffer, 0, 10);
sprintf(szBuffer, "%d", dwTimmer);
SetWindowText(hEdit4, szBuffer);
}
//ReleaseSemaphore(hSemaphore, 1, NULL);
return 0;
}
DWORD WINAPI ThreadProc5(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD dwTimmer = 0;
WaitForSingleObject(hSemaphore, INFINITE);
while (dwTimmer < 100)
{
Sleep(100);
memset(szBuffer, 0, 10);
GetWindowText(hEdit5, szBuffer, 10);
sscanf(szBuffer, "%d", &dwTimmer);
dwTimmer++;
memset(szBuffer, 0, 10);
sprintf(szBuffer, "%d", dwTimmer);
SetWindowText(hEdit5, szBuffer);
}
//ReleaseSemaphore(hSemaphore, 1, NULL);
return 0;
}
DWORD WINAPI ThreadBegin(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD dwMoney = 0;
hSemaphore = CreateSemaphore(NULL, 0, 3, NULL);
hThread[0] = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);
hThread[1] = ::CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
hThread[2] = ::CreateThread(NULL, 0, ThreadProc3, NULL, 0, NULL);
hThread[3] = ::CreateThread(NULL, 0, ThreadProc4, NULL, 0, NULL);
hThread[4] = ::CreateThread(NULL, 0, ThreadProc5, NULL, 0, NULL);
//开始准备红包
while (dwMoney < 1000)
{
memset(szBuffer, 0, 10);
GetWindowText(hEditSet, szBuffer, 10);
sscanf(szBuffer, "%d", &dwMoney);
dwMoney++;
memset(szBuffer, 0, 10);
sprintf(szBuffer, "%d", dwMoney);
SetWindowText(hEditSet, szBuffer);
}
ReleaseSemaphore(hSemaphore, 2, NULL);
::WaitForMultipleObjects(3, hThread, TRUE, INFINITE);
::CloseHandle(hSemaphore);
return 0;
}
BOOL CALLBACK MainDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BOOL bRet = FALSE;
switch (uMsg)
{
case WM_CLOSE:
{
EndDialog(hDlg, 0);
break;
}
case WM_INITDIALOG:
{
hEditSet = GetDlgItem(hDlg, IDC_EDIT1);
hEdit1 = GetDlgItem(hDlg, IDC_EDIT2);
hEdit2 = GetDlgItem(hDlg, IDC_EDIT3);
hEdit3 = GetDlgItem(hDlg, IDC_EDIT4);
hEdit4 = GetDlgItem(hDlg, IDC_EDIT5);
hEdit5 = GetDlgItem(hDlg, IDC_EDIT6);
SetWindowText(hEditSet, "0");
SetWindowText(hEdit1, "0");
SetWindowText(hEdit2, "0");
SetWindowText(hEdit3, "0");
SetWindowText(hEdit4, "0");
SetWindowText(hEdit5, "0");
break;
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
{
CreateThread(NULL, 0, ThreadBegin, NULL, 0, NULL);
return TRUE;
}
}
break;
}
return bRet;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, MainDlgProc);
return 0;
}
3.总结(重点)
CreateSemaphore(NULL, 0, 3, NULL)
- 第二个参数是并发数的计数器,0就算初始默认并发的数量为0,相当于未通知
- 第三个参数是3,相当于最大并发的线程数量为3
ReleaseSemaphore(hSemaphore, 2, NULL);
- 主线程里这段递增信号量的代码,意思是让计数器自增2次,所以现在有两个线程可以捕捉到已通知信号,有两个线程可以同时运行,但是一个线程运行一次后,信号量的计数器便会减一
ReleaseSemaphore(hSemaphore, 1, NULL);
- 每个抢红包线程的最后都有这个代码,加上后的效果就是一直会有两个线程并发在抢红包,因为主线程里的计数器虽然为2,并且已被最开始的两个抢红包线程执行后减一,但是两个抢红包线程各自会再给计数器+1,使程序一直有两个线程在抢红包
最开始我以为两个并发线程跑完后面的又会接着跑是因为第三个参数为3,会跑完3个线程,尝试了这五个线程后才发现接着跑是因为抢红包线程最后又有一个递增计数器的操作。第三个参数单纯只是限制并发参数不大于3
4.使用数组和传参创建多个重复线程
第五十六课 win32 线程同步与互斥(总结和大作业)
1.互斥
两者区别:
1、临界区只能用于进程内的线程互斥,性能较好.
2、互斥体属于内核对象,可以用于进程间的线程互斥,性能较差.
3、线程在没有正常退出互斥区而意外终结时,互斥体可以复位,但临界区不行.
2.线程同步
两者区别:
1、都是内核对象,使用完毕后应该关闭句柄.
2、信号量可以用于相当复杂的线程同步控制.
3.大作业
思路:
- 一共起五个线程,第一个线程负责读取资源并写入缓冲区,后面四个线程则是下面四个吃货
- 需要理清楚这五个线程之间互斥和同步的情况
- 读取资源是不需要使用互斥的,因为所有线程都是读取资源而没有写入操作是不需要互斥的,且只有一个线程去写入也是不需要互斥
- 吃货的写入也不需要互斥,写入的线程只会有一个,也没有人去读取吃货
- 只有缓冲区需要互斥,可能有人在读取也有人在写入
过程:
先把基础功能完善
//生产者线程
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
TCHAR szBuffer[256];
TCHAR sCmpBuffer[2];
TCHAR sTmpBuffer[2];
memset(szBuffer, 0, 256);
//读取资源
GetWindowText(hEdit1,szBuffer,256);
DWORD Len = strlen(szBuffer);
for (DWORD i = 0;i < Len;i++)
{
memset(sTmpBuffer,0,2);
memcpy(sTmpBuffer, &szBuffer[i], 1);
for (DWORD k = 0; k < 2; k++)
{
GetWindowText(hEParray[k], sCmpBuffer, 2);
if (strcmp(sCmpBuffer,"0")==0)
{
SetWindowText(hEParray[k], sTmpBuffer);
break;
}
}
}
return 0;
}
//消费者线程
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
TCHAR sCmpBuffer[2];
//TCHAR sTmpBuffer[2];
DWORD dwIndex = (DWORD)lpParameter;
while (TRUE)
{
for (DWORD k = 0; k < 2; k++)
{
GetWindowText(hEParray[k], sCmpBuffer, 2);
if (strcmp(sCmpBuffer, "0") != 0)
{
SetWindowText(hECarray[dwIndex], sCmpBuffer);
SetWindowText(hEParray[k], 0);
break;
}
}
}
return 0;
}
这时候两个线程处于无序状态,现在必须先让他有序起来,使用两个信号量
最终代码
#include <Windows.h>
#include <stdio.h>
#include "resource1.h"
HANDLE hSemaphoreEmpty;
HANDLE hSemaphoreFull;
CRITICAL_SECTION cs;
HANDLE hProcess;
HANDLE hThread[5];
HWND hEdit1;
HWND hEdit2;
HWND hEdit3;
HWND hEdit4;
HWND hEdit5;
HWND hEdit6;
HWND hEdit7;
HWND hECarray[4];
HWND hEParray[2];
//生产者线程
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
TCHAR szBuffer[256];
TCHAR sCmpBuffer[2];
TCHAR sTmpBuffer[2];
memset(szBuffer, 0, 256);
//读取资源
GetWindowText(hEdit1,szBuffer,256);
DWORD Len = strlen(szBuffer);
for (DWORD i = 0;i < Len;i++)
{
memset(sTmpBuffer,0,2);
memcpy(sTmpBuffer, &szBuffer[i], 1);
WaitForSingleObject(hSemaphoreEmpty, INFINITE);
for (DWORD k = 0; k < 2; k++)
{
EnterCriticalSection(&cs);
GetWindowText(hEParray[k], sCmpBuffer, 2);
if (strcmp(sCmpBuffer,"0")==0)
{
SetWindowText(hEParray[k], sTmpBuffer);
LeaveCriticalSection(&cs);
break;
}
LeaveCriticalSection(&cs);
}
Sleep(1000);
ReleaseSemaphore(hSemaphoreFull, 1, NULL);
}
return 0;
}
//消费者线程
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
TCHAR sCmpBuffer[2];
TCHAR sCustomerBuffer[256];
memset(sCustomerBuffer,0,256);
TCHAR sNewBuffer[256];
memset(sNewBuffer, 0, 256);
DWORD dwIndex = (DWORD)lpParameter;
while (TRUE)
{
DWORD dwExitCode = WaitForSingleObject(hSemaphoreFull, 10000);
if (dwExitCode == 0x102)
return -1;
EnterCriticalSection(&cs);
for (DWORD k = 0; k < 2; k++)
{
GetWindowText(hEParray[k], sCmpBuffer, 2);
if (strcmp(sCmpBuffer, "0"))
{
GetWindowText(hECarray[dwIndex], sCustomerBuffer, 256);
sprintf(sNewBuffer,"%s-%s", sCustomerBuffer, sCmpBuffer);
SetWindowText(hECarray[dwIndex], sNewBuffer);
//Sleep(500);
SetWindowText(hEParray[k], "0");
LeaveCriticalSection(&cs);
break;
}
LeaveCriticalSection(&cs);
}
Sleep(1000);
ReleaseSemaphore(hSemaphoreEmpty, 1, NULL);
}
return 0;
}
//DWORD WINAPI ThreadProc3(LPVOID lpParameter)
//{
//
// return 0;
//}
DWORD WINAPI ThreadBegin(LPVOID lpParameter)
{
hSemaphoreEmpty = ::CreateSemaphore(NULL, 2, 2, NULL);
hSemaphoreFull = ::CreateSemaphore(NULL, 0, 2, NULL);
InitializeCriticalSection(&cs);
//创建生产者线程
hThread[4] = ::CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);
//创建消费者线程
hThread[0] = ::CreateThread(NULL, 0, ThreadProc2, (void*)0, 0, NULL);
hThread[1] = ::CreateThread(NULL, 0, ThreadProc2, (VOID*)1, 0, NULL);
hThread[2] = ::CreateThread(NULL, 0, ThreadProc2, (VOID*)2, 0, NULL);
hThread[3] = ::CreateThread(NULL, 0, ThreadProc2, (VOID*)3, 0, NULL);
//等待
WaitForMultipleObjects(5,hThread,TRUE,INFINITE);
//关闭句柄
CloseHandle(hThread);
DeleteCriticalSection(&cs);
return 0;
}
BOOL CALLBACK MainDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
{
EndDialog(hDlg, 0);
break;
}
case WM_INITDIALOG:
{
hEdit1 = GetDlgItem(hDlg, IDC_EDIT1);
hEdit2 = GetDlgItem(hDlg, IDC_EDIT2);
hEdit3 = GetDlgItem(hDlg, IDC_EDIT3);
hEdit4 = GetDlgItem(hDlg, IDC_EDIT4);
hEdit5 = GetDlgItem(hDlg, IDC_EDIT5);
hEdit6 = GetDlgItem(hDlg, IDC_EDIT6);
hEdit7 = GetDlgItem(hDlg, IDC_EDIT7);
hECarray[0] = hEdit4;
hECarray[1] = hEdit5;
hECarray[2] = hEdit6;
hECarray[3] = hEdit7;
hEParray[0] = hEdit2;
hEParray[1] = hEdit3;
SetWindowText(hEdit3, "0");
SetWindowText(hEdit1, "0");
SetWindowText(hEdit2, "0");
SetWindowText(hEdit4, "");
SetWindowText(hEdit5, "");
SetWindowText(hEdit6, "");
SetWindowText(hEdit7, "");
break;
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_BUTTON1:
{
CreateThread(NULL, 0, ThreadBegin, NULL, 0, NULL);
return TRUE;
}
}
break;
}
return 0;
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, MainDlgProc);
return 0;
}
注意:(重点)
- SetWindowText函数第二个参数只能是用一个字符串数组缓冲区来传,强转会出错,&szBuffer[i]会把第i个索引后面的字符都打印出来