先把孙鑫老师的《VC++深入详解》中课程1的内容复习一下,以下为创建一个windows窗口的大致过程.
其中绝大部分代码和注释都是《VC++深入详解》一书中的原话.
#include
#include
LRESULT CALLBACK WinSunProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
//设计一个窗口类
WNDCLASS wndcls;
wndcls.cbClsExtra = 0;
//windows为系统中的每一个窗口类管理一个WNDCLASS结构.它可让Windows系统为WNDCALSS结构分配和
//追加一定字节数的附加内存空间,称为类附加内存,类附加内存空间用于存储类的附加信息.
//windows系统把这部分内存初始化为0.一般我们将这个参数设为0.
wndcls.cbWndExtra = 0;
//windows系统为每一个窗口管理一个内部数据结构,在注册一个窗口类时,应用程序能够指定
//一定字节数的附加内存空间,称为窗口附加内存.一般设为0.
wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
//指定窗口类的背景画刷句柄,当窗口发生重绘时,系统使用这里指定的画刷来擦除窗口的背景.
//我们既可以为hbrBackground成员指定一个画刷的句柄,也可以为其指定一个标准的系统颜色值.
//可调用GetStockObject函数来获得系统的标准画刷.
wndcls.hCursor = LoadCursor(NULL, IDC_CROSS);
//指定窗口类的光标句柄,其必须是一个光标资源的句柄,如果这个成员为NULL,那么无论何时
//鼠标进入到应用程序窗口中,应用程序都必须明确地设置光标的形状.
//在为hCursor赋值时,可用LoadCursor函数加载一个光标资源,返回系统分配给该光标的句柄.
wndcls.hIcon = NULL;
//指定窗口类的图标句柄,其必须是一个图标资源的句柄,如果这个成员为NULL,那么系统将提供
//一个默认的图标.可用LoadCursor函数加载一个图标资源,返回系统分配给该图标的句柄.
wndcls.hInstance = hInstance;
//指定包含窗口过程的程序的实例句柄,应用程序实例句柄由WinMain函数传进来.
wndcls.lpfnWndProc = WinSunProc;
//lpfnWndProc为一个函数指针,指向窗口过程函数.
wndcls.lpszClassName = "firstwind";
//一个以空中止的字符串,指定窗口类的名字.
wndcls.lpszMenuName = NULL;
//一个以空中止的字符串,指定菜单资源的名字.如果设为NULL,那么基于这个窗口类创建的窗口
//将没有默认的菜单.注意,菜单并不是一个窗口.
wndcls.style = CS_HREDRAW | CS_VREDRAW;
//窗口在水平或垂直方向变化时将重绘窗口.
RegisterClass(&wndcls);
//如果在调用CreateWindow函数之前,没有用RegisterClass函数注册过名称为firstwind的窗口类型,
//操作系统将无法得知这一类型窗口的相关信息,从而导致创建窗口失败.
//创建窗口,定义一个变量用来保存成功创建窗口后返回的句柄
HWND hwnd;
hwnd = CreateWindow("firstwind", "blueboy", WS_OVERLAPPEDWINDOW, 500, 200, 600, 400, NULL,
NULL, hInstance, NULL);
/*HWND CreateWindow(
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HANDLE hInstance,
LPVOID lpParam
)
lpClassName为设计窗口类中为WNDCLASS的lpszClassName成员指定的名称.
lpWindowName指定窗口的名字,若窗口样式指定了标题栏,则显示在标题栏上.
dwStyle指定窗口的样式,如同一型号的车可以有不同颜色,同一型号的窗口也可以有不同的外观样式
注意区分WNDCLASS中的style成员与CreateWindow函数中的dwStyle参数,前者指定窗口类的样式,
基于该窗口类创建的窗口都具有这些样式,后者指定某个具体的窗口样式.
x,y,nWidth,nHeight为窗口左上角坐标和宽度高度.
hWndParent指定被创建窗口的父窗口句柄.对父窗口的操作会影响子窗口,具体见孙鑫老师的
《VC++深入详解》中第12页表格.
hMenu指定窗口菜单句柄.
hInstance指定窗口所属的应用程序实例的句柄.
lpParam作为WM_CREATE消息的附加参数lParam传入的数据指针.多数窗口将其设为NULL.
若窗口创建成功,CreateWindow函数返回系统为该窗口分配的句柄,否则返回NULL.*/
//显示及刷新窗口
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
//UpdateWindow函数通过发送一个WM_PAINT消息来刷新窗口,其将WM_PAINT消息直接发送给
//了窗口过程函数进行处理,而没有放到消息队列里.
//定义消息结构体,开始消息循环
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
/*
BOOL GetMessage(
LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax
);
lpMsg指向一个消息(MSG)结构体,GetMessage从线程的消息队列中取出的消息信息将保存在该结构体对象中
hWnd指定接收属于哪一个窗口的消息.通常设为NULL,用于接收属于调用线程的所有窗口的窗口消息.
wMsgFilterMin指定要获取的消息的最小值,通常设为0.
wMsgFilterMax指定要获取的消息的最大值.如果wMsgFilterMin和wMsgFilterMax都设为0,则接收所有消息.
GetMessage函数接收到除WM_QUIT外的消息均返回非零值.对于WM_QUIT消息,该函数返回0.若出现错误,该函数
返回-1.例如的当hWnd是无效的窗口句柄或lpMsg是无效的指针时.
TranslateMessage函数用于将虚拟按键消息转换为字符消息.字符消息被投递到调用线程的消息队列中,
当下一次调用GetMessage时被取出.
DispatchMessage分派一个消息到窗口过程,由窗口过程函数对消息进行处理.DispatchMessage实际上
是将消息回传给操作系统,由操作系统调用窗口过程函数对消息进行处理(响应).*/
return msg.wParam;
}
//编写窗口过程函数
LRESULT CALLBACK WinSunProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch(uMsg)
{
case WM_CHAR:
char szChar[50];
sprintf(szChar,"char code is %d",wParam);
MessageBox(hwnd,szChar,"char",0);
break;
case WM_LBUTTONDOWN:
MessageBox(hwnd,"mouse clicked","message",0);
HDC hdc;
hdc=GetDC(hwnd);//不能在响应WM_PAINT消息时调用
TextOut(hdc,0,50,"happyblue",strlen("happyblue"));
//ReleaseDC(hwnd,hdc);
break;
case WM_PAINT:
HDC hDC;
PAINTSTRUCT ps;
hDC=BeginPaint(hwnd,&ps);
TextOut(hDC,0,0,"blueboy",strlen("blueboy"));
EndPaint(hwnd,&ps);
break;
case WM_CLOSE:
if(IDYES==MessageBox(hwnd,"是否真的结束?","message",MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
}
了解了窗口创建的大致过程之后,我们来编写dll,代码如下:
#include
#include
HANDLE ben;
DWORD WINAPI Proc1(
LPVOID lpParameter // thread data
);
LRESULT CALLBACK WinSunProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // handle to DLL module
DWORD fdwReason, // reason for calling function
LPVOID lpReserved )
{
switch( fdwReason )
{
case DLL_PROCESS_ATTACH:
{
ben = GetModuleHandle(NULL);
HANDLE thread1;
thread1=CreateThread(NULL,0,Proc1,NULL,0,NULL);
}
break;
case DLL_THREAD_ATTACH:
// Do thread-specific initialization.
break;
case DLL_THREAD_DETACH:
// Do thread-specific cleanup.
break;
case DLL_PROCESS_DETACH:
// Perform any necessary cleanup.
break;
}
return TRUE; // Successful DLL_PROCESS_ATTACH.
}
DWORD WINAPI Proc1(LPVOID lpParameter)
{
WNDCLASS wndcls;
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wndcls.hCursor = LoadCursor(NULL, IDC_CROSS);
wndcls.hIcon = NULL;
wndcls.hInstance = (HINSTANCE)ben;
wndcls.lpfnWndProc = WinSunProc;
wndcls.lpszClassName = "firstwind";
wndcls.lpszMenuName = NULL;
wndcls.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndcls);
HWND hwnd;
hwnd = CreateWindow("firstwind", "blueboy", WS_OVERLAPPEDWINDOW, 500, 200, 600, 400, NULL,
NULL, (HINSTANCE)ben, NULL);
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WinSunProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
switch(uMsg)
{
case WM_CHAR:
char szChar[50];
sprintf(szChar,"char code is %d",wParam);
MessageBox(hwnd,szChar,"char",0);
break;
case WM_LBUTTONDOWN:
MessageBox(hwnd,"mouse clicked","message",0);
HDC hdc;
hdc=GetDC(hwnd);//不能在响应WM_PAINT消息时调用
TextOut(hdc,0,50,"happyblue",strlen("happyblue"));
//ReleaseDC(hwnd,hdc);
break;
case WM_PAINT:
HDC hDC;
PAINTSTRUCT ps;
hDC=BeginPaint(hwnd,&ps);
TextOut(hDC,0,0,"blueboy",strlen("blueboy"));
EndPaint(hwnd,&ps);
break;
case WM_CLOSE:
if(IDYES==MessageBox(hwnd,"是否真的结束?","message",MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
}
首先要注意一点,由于要把窗口创建的步骤搬到dll中,原来的WinMain函数不能再用了,那么就简单的把WinMain函数中的代码全部拿出来.由于原来WinMain函数是传入了一些参数的,有个别参数此处用不到就不作处理,但是HINSTANCE hInstance这个参数是接下来设计窗口类和创建窗口的时候需要用到的,其为当前程序的实例句柄.在这里我们用GetModuleHandle(NULL)来获得,获得之后需要做一个强制类型转换,转换为HINSTANCE.
第二点是在利用dll创建窗口的最为重要的一部分.在搜集了一些资料后,此处简略的综合一下.若要利用dll创建窗口,可在DllMain函数中case Attach:情况下创建一个线程,然后利用线程的入口函数来创建窗口.
但需要注意的是,窗口在任何线程中都可以创建,但消息循环必须要和创建窗口在同一线程,否则窗口将无法从DispatchMessage获得任何消息.此处的信息来源于:http://www.docin.com/p-333120157.html
最后,随便写一个很简单的控制台程序,引用此处的dll,即可创建出我们设计好的窗口.
以下为控制台程序代码:
#include
#include
int main()
{
HINSTANCE hInst;
hInst = LoadLibrary("inmaindll.dll");
printf("next!\n");
getchar();
return 0;
}
效果如下图: