圣诞节,用C语言编写一段代码送给你的女神吧

本文只是写给初学者,其中一些代码很随意,望高手们不要见笑。
许多学习C语言的人,一段时间后,为了更进一步,开始学习C++,然而有关类的一些东西,搞的头昏脑胀。其实类就是源代码编好后封装,别人使用时找到类的接口,类再利用API接下口。说白了,类就是一个中介,不过编写MFC类的人掌握了一些微软没公开的接口。这就是微软的经商之道。
其实初学C语言时,只要掌握一些已经公开的API函数,绕过中介,也可以编出一些不错的小程序,下面就以网上流传的《送你一颗圣诞树》讲一下如何用纯C编写源代码。
第一步:编写主框架。
首先,《送你一颗圣诞树》是windows程序,其次是任务栏不显示,说明是对话框程序。下面是一段源代码,可以满足这两点;

欢迎加入学习群【892643663】,获取全套免费C/C++企业实战级课程资源(素材+源码+视频)和编译大礼包

// 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即可。

//shu.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"

欢迎加入学习群【892643663】,获取全套免费C/C++企业实战级课程资源(素材+源码+视频)和编译大礼包

以上代码写好后,下一步需要做的就是准备两张.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,xxp; 加载对话框也不必写成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;
}

之后将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的代码已经修改成如下:WinMain和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 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)
{ ......省略}
    
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;
    
    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,所以主要的位图最好和窗口一样大。
剩下的事是右键弹出菜单及菜单包括的事,还有不同位图显示时重叠部位的不同透明度问题,以后有空再说了。

欢迎加入学习群【892643663】,获取全套免费C/C++企业实战级课程资源(素材+源码+视频)和编译大礼包
C/C++编程交流学习

猜你喜欢

转载自blog.csdn.net/weixin_43659511/article/details/85237935