利用dll创建窗口的一些学习过程

先把孙鑫老师的《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;
}
效果如下图:

利用dll创建窗口的一些学习过程_第1张图片

你可能感兴趣的:(dll)