WIN32界面开发之一:初试加载背景界面

前言:在实际开发中,虽然有已经成形的界面库DUILIB,但DUILIB无法加载异形窗体,对PNG图片的支持不到位,最终我下决心,自己开发一套界面库,利用GDI+完成2D和3D功能,在这里,我将要记录下,我所做的界面开发的过程,并将它们整理成博客分享给大家,一步步来学习吧。

内容概要:这篇主要实现的功能是加载一个背景界面,向大家介绍下,如何用GDI+加载背景界面的问题,然后就是如何在指定位置托动窗体。

一、创建WIN32工程

1、因为是GDI+绘图,所以对于GDI+的初始化是必须的,在stdafx.h里加入下面代码:

#include <gdiplus.h>
#pragma comment(lib,"gdiplus.lib");
using namespace Gdiplus;
2、首先看一下WINMAIN函数的写法:
ULONG_PTR gdiplusToken = 0;
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd){
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

	Register(WindowProc,hInstance,L"transparent");
	HWND hwnd=Create(L"transparent",L"TEST",hInstance);
	Display(hwnd);
	Message();

	Gdiplus::GdiplusShutdown(gdiplusToken);
	return 0;
}
讲解:
1、首先是GDI+的初始化;
2、注册窗口类(Register函数)
3、创建窗口(Create函数)
4、显示窗口(Display函数)
5、消息循环(Message函数)
6、关闭GDI+应用,释放资源

3、注册窗口类(Register函数)写法
BOOL Register(WNDPROC fWndProc,HINSTANCE hInstance,LPCTSTR szClassName){
	WNDCLASSEX wce={0};
	wce.cbSize=sizeof(wce);
	wce.style=CS_HREDRAW|CS_VREDRAW;
	wce.lpfnWndProc=fWndProc;
	wce.cbClsExtra=0;
	wce.cbWndExtra=0;
	wce.hInstance=hInstance;
	wce.hIcon=NULL;
	wce.hCursor=LoadCursor(NULL,IDC_ARROW);
	wce.hbrBackground=(HBRUSH)(6);
	wce.lpszMenuName=NULL;
	wce.lpszClassName=szClassName;
	wce.hIconSm=NULL;
	ATOM nAtom=RegisterClassEx(&wce);
	if(nAtom==0) return false;
	return true;
}
讲解:注册窗口类也没什么好讲的,就是注册WNDCLASSEX结构体,首先对其各项进行赋值,然后调用RegisterClassEx()对其注册,但这里注意的两个地方,fWndPorc是传进来的消息处理函数的指针,我们就是在这个函数里截获窗体有关的各种消息,然后对其进行处理。szClassName是要注册窗体的类名;不同的窗口类,类名不能相同;

4、WndProc消息处理函数的写法;

LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
	switch(uMsg){
	case WM_DESTROY:
		PostQuitMessage(100);
		break;
	case WM_CREATE:
		{
		LONG styleValue = ::GetWindowLong(hwnd, GWL_STYLE);
		styleValue &= ~WS_CAPTION;
		styleValue &= ~WS_MAXIMIZEBOX; 
		styleValue &= ~WS_MINIMIZEBOX; 
		styleValue &= ~WS_THICKFRAME; 
		styleValue &= ~WS_BORDER; 
		styleValue &= ~WS_CAPTION;
		::SetWindowLong(hwnd, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
		}
		break;
	case WM_PAINT:
		break;
	case WM_LBUTTONDOWN:
		break;
	}
	return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
5、创建窗体(Create函数)写法
HWND Create(LPCTSTR lpClassName,LPCTSTR lpWindowName,HINSTANCE hInstance){
	HWND m_hWnd = ::CreateWindowEx(WS_EX_LAYERED, L"transparent", _T(""),WS_POPUPWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT, 
		CW_USEDEFAULT, CW_USEDEFAULT, 
		NULL, NULL, (HINSTANCE)::GetModuleHandle(NULL), 0);

	if(m_hWnd == NULL || !::IsWindow(m_hWnd))
		return NULL;

	return m_hWnd;

}
6、显示窗体(Display函数)
void Display(HWND hwnd){
	ShowWindow(hwnd,SW_SHOWNORMAL);
	UpdateWindow(hwnd);
}
7、消息循环(Message()函数)
void Message(){
	MSG msg={0};
	while(GetMessage(&msg,NULL,0,0)){
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

二、背景绘图

1、添加消息响应,在WM_PAINT消息中,添加消息响应SetBackground(hwnd,L"C:\\bg.png");

	case WM_PAINT:
		SetBackground(hwnd,L"C:\\bg.png");
		break;
2、SetBackground函数实现
void SetBackground(HWND m_hWnd,const TCHAR* pBackImgFullPath)
{
	//加载图片
	Gdiplus::Image* pImage = Gdiplus::Image::FromFile(pBackImgFullPath);
	if (pImage==NULL)
	{
		assert(false && _T("背景图片打开失败!"));
	}

	RECT windowRect;
	GetWindowRect(m_hWnd,&windowRect);
	SIZE sizeWindow;
	if (windowRect.left==windowRect.right)
	{
		sizeWindow.cx=pImage->GetWidth();
		sizeWindow.cy=pImage->GetHeight();
	}else
	{
		sizeWindow.cx=windowRect.right-windowRect.left;
		sizeWindow.cy=windowRect.bottom-windowRect.top;
	}

	HDC hDC = ::GetDC(m_hWnd);
	HDC hdcMemory = CreateCompatibleDC(hDC);
	RECT rcWindow;
	GetWindowRect(m_hWnd,&rcWindow);

	BITMAPINFOHEADER stBmpInfoHeader = { 0 };   
	int nBytesPerLine = ((sizeWindow.cx * 32 + 31) & (~31)) >> 3;
	stBmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);   
	stBmpInfoHeader.biWidth = sizeWindow.cx;   
	stBmpInfoHeader.biHeight = sizeWindow.cy;   
	stBmpInfoHeader.biPlanes = 1;   
	stBmpInfoHeader.biBitCount = 32;   
	stBmpInfoHeader.biCompression = BI_RGB;   
	stBmpInfoHeader.biClrUsed = 0;   
	stBmpInfoHeader.biSizeImage = nBytesPerLine * sizeWindow.cy;   

	PVOID pvBits = NULL;   
	HBITMAP hbmpMem = ::CreateDIBSection(NULL, (PBITMAPINFO)&stBmpInfoHeader, DIB_RGB_COLORS, &pvBits, NULL, 0);
	assert(hbmpMem != NULL);

	HGDIOBJ hbmpOld = ::SelectObject( hdcMemory, hbmpMem);
	POINT ptWinPos = { rcWindow.left, rcWindow.top };

	Gdiplus::Graphics graph(hdcMemory);
	graph.SetSmoothingMode(Gdiplus::SmoothingModeNone);
	graph.DrawImage(pImage, 0, 0, sizeWindow.cx, sizeWindow.cy);
	graph.FillRectangle(&SolidBrush(Color::Gray),0,0,100,50); //用GDI+在画布上画图

	HMODULE hFuncInst = LoadLibrary(_T("User32.DLL"));
	typedef BOOL (WINAPI *MYFUNC)(HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD);          
	MYFUNC UpdateLayeredWindow;
	UpdateLayeredWindow = (MYFUNC)::GetProcAddress(hFuncInst, "UpdateLayeredWindow");
	POINT ptSrc = { 0, 0};
	BLENDFUNCTION blendFunc;
	blendFunc.BlendOp = 0;
	blendFunc.BlendFlags = 0;
	blendFunc.AlphaFormat = 1;
	blendFunc.SourceConstantAlpha = 255;//AC_SRC_ALPHA
	//不会发送 WM_SIZE和WM_MOVE消息
	if(!UpdateLayeredWindow(m_hWnd, hDC, &ptWinPos, &sizeWindow, hdcMemory, &ptSrc, 0, &blendFunc, ULW_ALPHA))
	{
		assert(L"UpdateLayeredWindow 调用失败");
		TCHAR tmp[255] = {_T('\0')};
	}


	delete pImage;
	graph.ReleaseHDC(hdcMemory);
	::SelectObject( hdcMemory, hbmpOld); 

	::DeleteObject(hFuncInst);
	::DeleteObject(hbmpOld);
	::DeleteObject(hbmpMem); 
	::DeleteDC(hdcMemory);
	::DeleteDC(hDC);

}
讲解:两个关键部分注意一下:
1、
	BITMAPINFOHEADER stBmpInfoHeader = { 0 };   
	int nBytesPerLine = ((sizeWindow.cx * 32 + 31) & (~31)) >> 3;
	stBmpInfoHeader.biSize = sizeof(BITMAPINFOHEADER);   
	stBmpInfoHeader.biWidth = sizeWindow.cx;   
	stBmpInfoHeader.biHeight = sizeWindow.cy;   
	stBmpInfoHeader.biPlanes = 1;   
	stBmpInfoHeader.biBitCount = 32;   
	stBmpInfoHeader.biCompression = BI_RGB;   
	stBmpInfoHeader.biClrUsed = 0;   
	stBmpInfoHeader.biSizeImage = nBytesPerLine * sizeWindow.cy;   

	PVOID pvBits = NULL;   
	HBITMAP hbmpMem = ::CreateDIBSection(NULL, (PBITMAPINFO)&stBmpInfoHeader, DIB_RGB_COLORS, &pvBits, NULL, 0);
这是创建一个32位的位图,这部分代码必须有,否则会出现问题,我们的绘图就是在这个位图上画,然后再显示的桌面上;
2、
	HMODULE hFuncInst = LoadLibrary(_T("User32.DLL"));
	typedef BOOL (WINAPI *MYFUNC)(HWND, HDC, POINT*, SIZE*, HDC, POINT*, COLORREF, BLENDFUNCTION*, DWORD);          
	MYFUNC UpdateLayeredWindow;
	UpdateLayeredWindow = (MYFUNC)::GetProcAddress(hFuncInst, "UpdateLayeredWindow");
	POINT ptSrc = { 0, 0};
	BLENDFUNCTION blendFunc;
	blendFunc.BlendOp = 0;
	blendFunc.BlendFlags = 0;
	blendFunc.AlphaFormat = 1;
	blendFunc.SourceConstantAlpha = 255;//AC_SRC_ALPHA
	//不会发送 WM_SIZE和WM_MOVE消息
	if(!UpdateLayeredWindow(m_hWnd, hDC, &ptWinPos, &sizeWindow, hdcMemory, &ptSrc, 0, &blendFunc, ULW_ALPHA))
	{
		assert(L"UpdateLayeredWindow 调用失败");
		TCHAR tmp[255] = {_T('\0')};
	}
讲解:这是UpdateLayeredWindow的标准写法,UpdateLayeredWindow支持ALPHA通道,所以支持PNG图片的显示,但需要注意的是UpdateLayeredWindow不会发送WM_PAINT消息,所以我们要在WM_CREATE消息中,添加一行
PostMessageW(hwnd,WM_PAINT,NULL,NULL);
最终的效果:
WIN32界面开发之一:初试加载背景界面_第1张图片
三、移动窗体
对于移动窗体就比较简单了,主要是发送SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);消息,我们这里是只要在窗体上鼠标按下,就发送这个消息,所以在WM_LBUTTONDOWN消息中添加响应即可,代码如下:
	case WM_LBUTTONDOWN:
		SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, 0);
		break;
至此,这篇的实现就全部讲完了,与往常一样,代码地址: http://download.csdn.net/detail/harvic880925/5757093
转载的标明出处哦!地址: http://blog.csdn.net/harvic880925/article/details/9331701


你可能感兴趣的:(WIN32界面开发之一:初试加载背景界面)