1. 程式人生 > >聖誕節,用C語言編寫一段程式碼送給你的女神吧

聖誕節,用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++程式設計交流學習