本文只是写给初学者,其中一些代码很随意,望高手们不要见笑。
许多学习C语言的人,一段时间后,为了更进一步,开始学习C++,然而有关类的一些东西,搞的头昏脑胀。其实类就是源代码编好后封装,别人使用时到类的接口,类再利用API接下口。说白了,类就是一个中介,不过编写MFC类的人掌握了一些微软没公开的接口。这就是微软的经商之道。
其实初学C语言时,只要掌握一些已经公开的API函数,绕过中介,也可以编出一些不错的小程序,下面就以网上流传的《送你一颗圣诞树》讲一下如何用纯C编写源代码。
第一步:编写主框架。
首先,《送你一颗圣诞树》是windows程序,其次是任务栏不显示,说明是对话框程序。下面是一段源代码,可以满足这两点;
***************
// shu.c  //windows主程序,名字可任意,此处意思是树
#include <windows.h>
HWND hwnd,hdlgmdles;
HANDLE hInst;
HDC hdc,hmemdc;
HBITMAP hBitmap1,hBitmap2;
LRESULT CALLBACK  WndProc(HWND hWnd,UINT message,WPARAM wParam, LPARAM lParam);
BOOL CALLBACK DialogProc(HWND hmDlg,UINT message,WPARAM wParam,LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,int cmdShow)
{
WNDCLASS ;
MSG msg;
if(!hPrevInstance)
{  myClass.style=NULL;
myClass.lpfnWndProc=WndProc;
myClass.cbClsExtra=0;
myClass.cbWndExtra=0;
myClass.hInstance=hInstance;
myClass.hIcon=NULL;
myClass.hCursor=NULL;
myClass.hbrBackground=NULL;
myClass.lpszClassName="mydlg";
myClass.lpszMenuName=NULL;
RegisterClass(&myClass);
}
hInst=hInstance;
hwnd=CreateWindow("mydlg","", WS_POPUP,
0,0,0,0,NULL,NULL,hInstance,NULL);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);DispatchMessage(&msg);}
return(msg.wParam);
}
LRESULT CALLBACK  WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
hdlgmdles=CreateDialog(hInst,"mdless",hWnd,DialogProc);
break;
default:  return DefWindowProc(hWnd,message,wParam,lParam);
}return 0;
}
BOOL CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
switch (message)
{  case WM_INITDIALOG:
hBitmap1 =LoadBitmap(hInst,"bmp1");
hBitmap2 =LoadBitmap(hInst,"bmp2");
hmemdc = CreateCompatibleDC(hdc);
break;
case WM_COMMAND:
break;
case WM_TIMER:
break;
case WM_CTLCOLORDLG:
return (BOOL)((HBRUSH)GetStockObject(NULL_BRUSH));
case WM_PAINT:
hdc = GetDC(hDlg);
SelectObject(hmemdc, hBitmap1);
BitBlt(hdc,0,0,200,300,hmemdc,0,0,SRCCOPY);
ReleaseDC(hDlg, hdc);
break;
case WM_RBUTTONDOWN:
break;
case WM_LBUTTONDOWN:
SendMessage( hDlg, WM_NCLBUTTONDOWN, HTCAPTION, 0);
break;
case WM_KEYDOWN:
switch (wParam)
{case VK_ESCAPE:
DeleteDC(hmemdc);
PostQuitMessage(0);break;
}break;
}
return FALSE;
}
// 以上的代码还必须加上*.RC文件,才能编译成功,可以用记事本编写RC文件,写完后将.txt改为.RC即可。
//    //最好与.C文件同名,现在只包含一个对话框,和两张位图
#include <windows.h>
mdless DIALOG 100,100,100,150
STYLE WS_POPUP | WS_VISIBLE
FONT 0, ""
BEGIN
END
bmp1 BITMAP "shu1.bmp"
bmp2 BITMAP "shu2.bmp"
****************************
以上代码写好后,下一步需要做的就是准备两张.bmp图,不一样就行,名字同.RC文件里的名,背景最好是黑,因为黑要设置成透明,要(0,0,0)的纯黑,画面里的黑改成不是纯0的。打开编译软件IDE,创建工程文件,加入以上的.C和.RC两个文件,其它多余的不要,直接编译即可。如用VC6.0
或VC的其他版本,对话框可能加载不上,可用VC编译器修改,修改.RC文件成以上代码,不然是带标题栏的,还需把VC编的.RC文件里的#define ***_**等声明代码加上,还需加上.H(用VC很麻烦的)。
下面讲一下以上代码。
WinMan函数:由于不显示主窗口,所以没有ShowWindow和UpdateWindow两个函数,myClass的值除必须有的,其他都是NULL。必须传下去的只有hInst。
WndProc函数:本例中功能只有一个,创建对话框,创建时必须用CreateDialog。之后就没用了。
DialogProc函数:代替WndProc的所有功能,case项空着的,是以后添加代码,
case WM_INITDIALOG:  此处一次性加载位图,以后有十张图均可此处一次加载。VC的某些版本可能加载位图时需要调用类和宏。
case WM_CTLCOLORDLG:  return (BOOL)((HBRUSH)GetStockObject(NULL_BRUSH));设定没有画刷(其实不是透明画刷),是为了以后的透明窗口。开始可以改成任何颜,以后根据具体情况,可以不要这段。
case VK_ESCAPE:  ESC是左上角的退出键。由于程序没有退出按钮,靠ESC键退出。必须用PostQuitMessage(0),不能用DestroyWindow,不然虽然看不见,程序却继续运行。
case WM_LBUTTONDOWN:鼠标左键拖动窗口。
case WM_COMMAND:以后右键菜单时用,
case WM_TIMER:  设置时间
case WM_RBUTTONDOWN:弹出菜单
顺便说一下,C语言很宽容,只要大写的定义不错,其他都可以随便起名,如:HBITMAP hBitmap,map1,x
xp; 加载对话框也不必写成IDD_ **,mdless也可。
如果以上编译没有问题,就可以进行下一步了。
********************
第二步:先设置透明背景窗口,关于SetLayeredWindowAttributes函数,网上可以到很多说明和用法,此处仅就XP系统说一下,如果你的编译器不认这个函数,那就动态调用,将下面这段代码放在shu.c文件的头部全局变量处,
typedef BOOL (FAR WINAPI *LAYERFUNC)(HWND,COLORREF,BYTE,DWORD);
BOOL SetLayeredWindowAttributes(HWND hwnd,COLORREF crKey,BYTE bAlpha,DWORD dwFlags)
{    BOOL bReturn;
LAYERFUNC SetLayer;
HMODULE hmod = LoadLibrary("user32.dll");
SetLayer=(LAYERFUNC)GetProcAddress(hmod,"SetLayeredWindowAttributes");
bReturn = SetLayer(hwnd,crKey,bAlpha,dwFlags);
FreeLibrary(hmod);
return bReturn;
}
一般XP系统都没问题,没必要去升级你的编译软件,费时费力。之后将static int transparent = 200;这段代码写到DialogProc里开始处,200是初始透明度,
编译时设置不同值看看效果。
将以下代码放在case WM_INITDIALOG下;
SetWindowLong(hDlg, GWL_EXSTYLE, GetWindowLong(hDlg, GWL_EXSTYLE) | 0x80000);
SetLayeredWindowAttributes(hDlg,RGB(0,0,0), transparent, 0x00000002|0x00000001);
SetWindowPos( hDlg,HWND_TOPMOST,200,200,200,300,SWP_SHOWWINDOW );
其中的0x80000=WS_EX_LAYERED,0x00000001=LWA_COLORKEY,0x00000002=LWA_ALPHA,建议直接写数值,不然你还得#define一下。RGB(0,0,0)也可以换成0。SetWindowPos函数只是为了HWND_TOPMOST,就是将窗口放在最前面。这时你可以编译看一下,效果是没有窗框和背景的图案,点左键可以拖动。
之后,就是设置时间,为了定时自动更换位图。将SetTimer(hDlg,ID_TIMER,1000,NULL);写到case WM_INITDIALOG下,1000是一秒。将#define ID_TIMER  10写到shu.c文件开始处,10可以是任意数,只要不和其它的重复就行,只是有个值可以内存里占个坑。至于何时BitBlt哪张图,有人喜欢用flag或BOOL型控制,但只有FALSE和TRUE。这里用 int型,数值大,在DialogProc里定义个static int,将WM _PAINT里的东西挪到WM_TIMER里,取消WM_PAINT。这时sh.c的代码已经修改成如下:WinMai
n和WndProc没改。
*************************
//shu.c
#define ID_TIMER  10
#include <windows.h>
HWND hwnd,hdlgmdles;
HDC hdc,hmemdc;
HANDLE hInst;
HBITMAP hBitmap1,hBitmap2;
typedef BOOL (FAR WINAPI *LAYERFUNC)(HWND,COLORREF,BYTE,DWORD);
BOOL SetLayeredWindowAttributes(HWND hwnd,COLORREF crKey,BYTE bAlpha,DWORD dwFlags)
{ BOOL bReturn;
LAYERFUNC SetLayer;
HMODULE hmod = LoadLibrary("user32.dll");
SetLayer=(LAYERFUNC)GetProcAddress(hmod,"SetLayeredWindowAttributes");
bReturn = SetLayer(hwnd,crKey,bAlpha,dwFlags);
FreeLibrary(hmod);
return bReturn;
}     
LRESULT CALLBACK  WndProc(HWND hWnd,UINT message,WPARAM w
Param, LPARAM lParam);
BOOL CALLBACK DialogProc(HWND hmDlg,UINT message,WPARAM wParam,LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,int cmdShow)
{ ......省略}
LRESULT CALLBACK  WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{ ......省略}
BOOL CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
static int nTransparent =200,putbmp=0;
c语言编译器ide代码编辑switch (message)
{
case WM_INITDIALOG:
hBitmap1 =LoadBitmap(hInst,"bmp1");
hBitmap2 =LoadBitmap(hInst,"bmp2");
hmemdc = CreateCompatibleDC(hdc);
SetWindowLong(hDlg, GWL_EXSTYLE, GetWindowLong(hDlg, GWL_EXSTYLE) | 0x80000);
SetLayeredWindowAttributes(hDlg,0, nTransparent,0x00000002|0x00000001);
SetWindowPos( hDlg,HWND_TOPMOST,300,300,150,150,SWP_SHOWWINDOW );
SetTimer(hDlg,ID_TIMER,1000,NULL);
break;
case WM_COMMAND:
break;
case WM_TIMER:       
putbmp++;
hdc = GetDC(hDlg);
//这里也有人喜欢用switch;
if(putbmp==1)
{    SelectObject(hmemdc, hBitmap1);
BitBlt(hdc,0,0,200,200,hmemdc,0,0,SRCCOPY );
}
if(putbmp==2)
{    SelectObject(hmemdc, hBitmap2);
BitBlt(hdc,0,0,200,200,hmemdc,0,0,SRCCOPY );
}
if(putbmp==3){};  //备用
if(putbmp==5){};
ReleaseDC(hDlg, hdc);
if(putbmp>5)putbmp=0;
break;
case WM_CTLCOLORDLG:
return (BOOL)((HBRUSH)GetStockObject(NULL_BRUSH));
case WM_RBUTTONDOWN:
break;
case WM_LBUTTONDOWN:
SendMessage( hDlg, WM_NCLBUTTONDOWN, HTCAPTION, 0);
break;
case WM_KEYDOWN:
switch (wParam)
{
case VK_ESCAPE:
DeleteDC(hmemdc);
PostQuitMessage(0);break;
}break;
}return FALSE;
}
************************
现在,你可以发挥你的想象力,因为只是不同时间显示不同的位图了,增加的位图越多,动态效果越好,程序体积就越大。你可以自己加个图标。在《树》这个程序里,用不到StretchBlt,所以主要的位图最好和窗口一样大。
剩下的事是右键弹出菜单及菜单包括的事,还有不同位图显示时重叠部位的不同透明度问题,以后有空再说了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。