• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            2008-08-25 17:39

            前幾天在工作中需要寫一段代碼,獲取一些視頻文件的大小,心想:這還不簡單嗎?直接用標準C 的文件操作函數就OK了。于是寫了下面的一段代碼來實現:

            unsigned long get_file_size(const char *filename)

            {

            unsigned long size;

            FILE* fp = fopen( filename, "rb" );

            if(fp==NULL)

            {

            printf("ERROR: Open file %s failed.\n", filename);

            return 0;

            }

            fseek( fp, SEEK_SET, SEEK_END );

            size=ftell(fp);

            fclose(fp);

            return size;

            }

            沒有想到的是,在程序執行后發現有的文件能正確的獲取大小,而有的文件則不能正確的獲取到文件大小,檢查了代碼,也沒有發現有什么不對的地方。但是在這過程中發現了一個問題,就是能正確獲取大小的文件都是相對比較小的文件,而出現錯誤的都是很大的文件。于是想到會不會是因為標準C文件操作函數對超過一定大小的文件不支持所造成的呢,于是Google了一下,沒想到我的猜測是正確的,標準C的文件操作函數不支持對超過2G的文件讀取。

            問題找到了,看來只有換一種方法來實現了,因為平時很少用到標準C的一些函數,所以,又只有求助于Google了,在看了網上不少的參考文章之后,發現調用stat函數可以正確的得到超大文件的狀態信息(當然包括文件大小),于是最終實現了如下的代碼:

            unsigned long get_file_size(const char *filename)

            {

            struct stat buf;

            if(stat(filename, &buf)<0)

            {

            return 0;

            }

            return (unsigned long)buf.st_size;

            }

            從寫這么一個小小功能的函數可以看出,平時多積累一些計算機方面的知識真的是很重要的,同時對代碼的全面測試也是相當重要的,否則,看著很正確的代碼可能在某些情況下會給你帶來意想不到的麻煩。

            posted @ 2010-06-21 22:56 lhking 閱讀(2850) | 評論 (0)編輯 收藏
            ---- 所謂遍歷目錄,就是給定一個目錄,訪問其中的所有文件(包括子目錄下的文件)。迭代是比較常用的遍歷算法。本文利用C++面向對象的特性,通過一個類CBrowseDir,對目錄遍歷進行了封裝。用戶只需掌握該類四個成員函數的用法,就可以在自己的程序中,很方便地實現目錄遍歷。

            ---- 類CBrowseDir使用了迭代算法。因為算法不是本文重點,筆者不打算展開進一步討論,對其感興趣者可參考相關資料。

            一、類成員函數說明:

            ---- bool SetInitDir(const char *dir);

            ---- 功能:設置要遍歷的目錄。

            ---- 參數:dir 指向要遍歷的目錄,可以使用相對路徑,比如"d:..hawk";還可以使用網絡路徑,比如"\wfdhawk"(其中wf是主機名,d是共享目錄,hawk是目錄)。

            ---- 返回值:返回true,表示設置成功;返回false,說明目錄不可用。

            ---- bool BeginBrowse(const char *filespec);

            ---- 功能:開始遍歷目錄中由filespec指定的文件(包括隱藏文件)。

            ---- 參數:filespec 指定文件類型,可以使用通配符*和?,比如"*.exe"或"a?.*"都是合法參數。注意:filespec中不能包含路徑,象"hawk*.*"是錯誤的。

            ---- 返回值:函數返回true,表明已順利遍歷完所有文件;返回false,遍歷過程被用戶中止。

            ---- virtual bool ProcessFile(const char *filename);

            ---- 功能:虛函數。每找到一個文件,程序就會調用ProcessFile,并把文件名作為參數傳遞給函數。如果函數返回false,則強制遍歷中止,并導致類成員函數函數BeginBrowse返回false。 用戶應該覆寫此函數,以加入自己的處理代碼。

            ---- 參數:filename 指向一個文件名。注意:filename使用絕對路徑。

            ---- 返回值:返回true,繼續遍歷;否則,中止遍歷。

            ---- virtual void ProcessDir (const char *currentdir,const char *parentdir);

            ---- 功能:虛函數。在遍歷過程中,每進入一個子目錄,程序就會調用ProcessDir,并把目錄名及其上一級目錄名作為參數傳遞給函數。如果該目錄是成員函數SetInitDir指定的初始目錄,則parentdir=NULL。用戶可以覆寫此函數,以加入自己的處理代碼。比如可以在這里統計子目錄的個數。

            ---- 參數:currentdir 指向一個子目錄。
            ---- parentdir 指向currentdir的父目錄。
            ---- 注意:currentdir和parentdir均使用絕對路徑。

            二、使用:

            ---- 把類CBrowseDir的頭文件BrowseDir.h及實現文件BrowseDir.cpp加到項目(Project)中,然后派生自己的類并覆寫虛函數ProcessFile和ProcessDir。遍歷目錄時,先構造一個派生類對象,用成員函數SetInitDir指定目錄,然后調用BeginBrowse開始遍歷。

            ---- 本文提供了一個例子 example.cpp,它從CBrowseDir派生出子類CStatDir,通過統計函數ProcessFile及ProcessDir的調用次數,可以得知目錄中的文件及子目錄個數。程序都有注釋,這里就不再羅嗦了。

            三、注意事項:

            ---- 1. 類CBrowseDir會改變當前工作目錄。同一個相對路徑,使用CBrowseDir前后,可能會有不同的含義。因此用戶編程時,要小心使用相對路徑。

            ---- 2. 如果項目(Project)是一個MFC應用程序,直接加入BrowseDir.h及BrowseDir.cpp會導致編譯出錯。這是因為缺省情況下,MFC項目使用了預編譯頭(Precompiled Header),而BrowseDir.h和BrowseDir.cpp是用標準C++語句編寫的,沒用預編譯。一個解決辦法是先用類向導生成類CBrowseDir的"架子",再把相應的代碼拷貝過去。

            ---- 本文代碼均在Win95、Visual C++ 5.0環境下調試通過。

            附源代碼:

            /**************************************************
                這是CBrowseDir的類定義文件 BrowseDir.h

            /**************************************************
            #include "stdlib.h"

            class CBrowseDir
            {
            protected:
            //存放初始目錄的絕對路徑,以結尾
            char m_szInitDir[_MAX_PATH];

            public:
            //缺省構造器
            CBrowseDir();

            //設置初始目錄為dir,如果返回false,表示目錄不可用
            bool SetInitDir(const char *dir);

            //開始遍歷初始目錄及其子目錄下由filespec指定類型的文件
            //filespec可以使用通配符 * ?,不能包含路徑。
            //如果返回false,表示遍歷過程被用戶中止
            bool BeginBrowse(const char *filespec);

            protected:
            //遍歷目錄dir下由filespec指定的文件
            //對于子目錄,采用迭代的方法
            //如果返回false,表示中止遍歷文件
            bool BrowseDir(const char *dir,const char *filespec);

            //函數BrowseDir每找到一個文件,就調用ProcessFile
            //并把文件名作為參數傳遞過去
            //如果返回false,表示中止遍歷文件
            //用戶可以覆寫該函數,加入自己的處理代碼
            virtual bool ProcessFile(const char *filename);

            //函數BrowseDir每進入一個目錄,就調用ProcessDir
            //并把正在處理的目錄名及上一級目錄名作為參數傳遞過去
            //如果正在處理的是初始目錄,則parentdir=NULL
            //用戶可以覆寫該函數,加入自己的處理代碼
            //比如用戶可以在這里統計子目錄的個數
            virtual void ProcessDir(const char
            *currentdir,const char *parentdir);
            };


            /*********************************************/

            這是CBrowseDir的類實現文件 BrowseDir.cpp

            /***********************************************/
            #include "stdlib.h"
            #include "direct.h"
            #include "string.h"
            #include "io.h"

            #include "browsedir.h"

            CBrowseDir::CBrowseDir()
            {
            //用當前目錄初始化m_szInitDir
            getcwd(m_szInitDir,_MAX_PATH);

            //如果目錄的最后一個字母不是,則在最后加上一個
            int len=strlen(m_szInitDir);
            if (m_szInitDir[len-1] != \)
            strcat(m_szInitDir,"\");
            }

            bool CBrowseDir::SetInitDir(const char *dir)
            {
            //先把dir轉換為絕對路徑
            if (_fullpath(m_szInitDir,dir,_MAX_PATH) == NULL)
            return false;

            //判斷目錄是否存在
            if (_chdir(m_szInitDir) != 0)
            return false;

            //如果目錄的最后一個字母不是,則在最后加上一個
            int len=strlen(m_szInitDir);
            if (m_szInitDir[len-1] != \)
            strcat(m_szInitDir,"\");

            return true;
            }

            bool CBrowseDir::BeginBrowse(const char *filespec)
            {
            ProcessDir(m_szInitDir,NULL);
            return BrowseDir(m_szInitDir,filespec);
            }

            bool CBrowseDir::BrowseDir
            (const char *dir,const char *filespec)
            {
            _chdir(dir);

            //首先查找dir中符合要求的文件
            long hFile;
            _finddata_t fileinfo;
            if ((hFile=_findfirst(filespec,&fileinfo)) != -1)
            {
            do
            {
            //檢查是不是目錄
            //如果不是,則進行處理
            if (!(fileinfo.attrib & _A_SUBDIR))
            {
            char filename[_MAX_PATH];
            strcpy(filename,dir);
            strcat(filename,fileinfo.name);
            if (!ProcessFile(filename))
            return false;
            }
            } while (_findnext(hFile,&fileinfo) == 0);
            _findclose(hFile);
            }

            //查找dir中的子目錄
            //因為在處理dir中的文件時,派生類的ProcessFile有可能改變了
            //當前目錄,因此還要重新設置當前目錄為dir。
            //執行過_findfirst后,可能系統記錄下了相關信息,因此改變目錄
            //對_findnext沒有影響。
            _chdir(dir);
            if ((hFile=_findfirst("*.*",&fileinfo)) != -1)
            {
            do
            {
            //檢查是不是目錄
            //如果是,再檢查是不是 . 或 ..
            //如果不是,進行迭代
            if ((fileinfo.attrib & _A_SUBDIR))
            {
            if (strcmp(fileinfo.name,".") != 0 && strcmp
            (fileinfo.name,"..") != 0)
            {
            char subdir[_MAX_PATH];
            strcpy(subdir,dir);
            strcat(subdir,fileinfo.name);
            strcat(subdir,"\");
            ProcessDir(subdir,dir);
            if (!BrowseDir(subdir,filespec))
            return false;
            }
            }
               } while (_findnext(hFile,&fileinfo) == 0);
            _findclose(hFile);
            }
            return true;
            }

            bool CBrowseDir::ProcessFile(const char *filename)
            {
            return true;
            }

            void CBrowseDir::ProcessDir(const char
            *currentdir,const char *parentdir)
            {
            }


            /*************************************************
            這是例子example.cpp
              
            /*************************************************
            #include "stdio.h"

            #include "BrowseDir.h"

            //從CBrowseDir派生出的子類,用來統計目錄中的文件及子目錄個數
            class CStatDir:public CBrowseDir
            {
            protected:
            int m_nFileCount;    //保存文件個數
            int m_nSubdirCount; //保存子目錄個數

            public:
            //缺省構造器
            CStatDir()
            {
            //初始化數據成員m_nFileCount和m_nSubdirCount
            m_nFileCount=m_nSubdirCount=0;
            }

            //返回文件個數
            int GetFileCount()
            {
            return m_nFileCount;
            }

            //返回子目錄個數
            int GetSubdirCount()
            {
            //因為進入初始目錄時,也會調用函數ProcessDir,
            //所以減1后才是真正的子目錄個數。
            return m_nSubdirCount-1;
            }

            protected:
            //覆寫虛函數ProcessFile,每調用一次,文件個數加1
            virtual bool ProcessFile(const char *filename)
            {
            m_nFileCount++;
            return CBrowseDir::ProcessFile(filename);
            }

            //覆寫虛函數ProcessDir,每調用一次,子目錄個數加1
            virtual void ProcessDir
            (const char *currentdir,const char *parentdir)
            {
            m_nSubdirCount++;
            CBrowseDir::ProcessDir(currentdir,parentdir);
            }
            };

            void main()
            {
            //獲取目錄名
            char buf[256];
            printf("請輸入要統計的目錄名:");
            gets(buf);

            //構造類對象
            CStatDir statdir;

            //設置要遍歷的目錄
            if (!statdir.SetInitDir(buf))
            {
            puts("目錄不存在。");
            return;
            }

            //開始遍歷
            statdir.BeginBrowse("*.*");

            //統計結果中,子目錄個數不含 . 及 ..
            printf("文件總數: %d 子目錄總數:
            %d ",statdir.GetFileCount(),
            statdir.GetSubdirCount());
            }  
            posted @ 2010-06-21 22:55 lhking 閱讀(1316) | 評論 (2)編輯 收藏

            // dll.cpp : Defines the initialization routines for the DLL.
            // Author:秋鎮菜

            #include "stdafx.h"
            #include <afxdllx.h>

            #ifdef _DEBUG
            #define new DEBUG_NEW
            #undef THIS_FILE
            static char THIS_FILE[] = __FILE__;
            #endif


            static AFX_EXTENSION_MODULE DllDLL = { NULL, NULL };

            extern "C" int APIENTRY
            DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
            {
            // Remove this if you use lpReserved
            UNREFERENCED_PARAMETER(lpReserved);

            if (dwReason == DLL_PROCESS_ATTACH)
            {
               TRACE0("DLL.DLL Initializing!\n");
              
               // Extension DLL one-time initialization
               if (!AfxInitExtensionModule(DllDLL, hInstance))
                return 0;

               // Insert this DLL into the resource chain
               // NOTE: If this Extension DLL is being implicitly linked to by
               // an MFC Regular DLL (such as an ActiveX Control)
               // instead of an MFC application, then you will want to
               // remove this line from DllMain and put it in a separate
               // function exported from this Extension DLL. The Regular DLL
               // that uses this Extension DLL should then explicitly call that
               // function to initialize this Extension DLL. Otherwise,
               // the CDynLinkLibrary object will not be attached to the
               // Regular DLL's resource chain, and serious problems will
               // result.
               MessageBox(NULL, "對話框", NULL, MB_OK);
               new CDynLinkLibrary(DllDLL);
            }
            else if (dwReason == DLL_PROCESS_DETACH)
            {
               TRACE0("DLL.DLL Terminating!\n");
               // Terminate the library before destructors are called
               AfxTermExtensionModule(DllDLL);
            }
            return 1;   // ok
            }


            // remotethread.cpp : Defines the entry point for the console application.
            // Author:秋鎮菜

            #include "stdafx.h"
            #include "windows.h"

            int main(int argc, char* argv[])
            {
            HWND hWnd = FindWindow("notepad", NULL);
            DWORD dwId;
            GetWindowThreadProcessId(hWnd, &dwId);
            HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId);
            if (! hProcess)
               return 0;

            char sz[MAX_PATH];
            GetModuleFileName(NULL, sz, MAX_PATH);
            strcpy(strstr(sz,".exe"), ".dll");
            strcpy(sz, "c:\\windows\\dll.dll");
            void *pData = VirtualAllocEx(hProcess, 0, sizeof (sz), MEM_COMMIT, PAGE_READWRITE);
            if (! pData)
               return 0;
            if (! WriteProcessMemory(hProcess, pData, sz, sizeof (sz), 0))
               return 0;
            HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
               (LPTHREAD_START_ROUTINE)GetProcAddress(
               LoadLibrary("kernel32.dll"), "LoadLibraryA"), pData, 0, 0);
            if (hThread == NULL)
               return 0;
            printf("sssssssssssssssssssssssssssssss\r\n");
            WaitForSingleObject(hThread, INFINITE);
            DWORD dwModule;
            GetExitCodeThread(hThread, &dwModule);
            CloseHandle(hThread);
            VirtualFreeEx(hProcess, pData, sizeof (sz), MEM_RELEASE);
            printf("...............................\r\n");
            hThread = CreateRemoteThread(hProcess, NULL, 0,
               (LPTHREAD_START_ROUTINE)GetProcAddress(
               LoadLibrary("kernel32.dll"), "FreeLibrary"), &dwModule, 0, 0);
            if (hThread == NULL)
               return 0;
            WaitForSingleObject(hThread, INFINITE);
            CloseHandle(hThread);
            CloseHandle(hProcess);
            printf(sz);
            printf("\r\n");
            Sleep(2000);
            return 0;
            }

            posted @ 2010-06-21 22:53 lhking 閱讀(376) | 評論 (0)編輯 收藏

            // _remotethreaddemo.cpp : Defines the entry point for the console application.
            // Author:秋鎮菜

            #include "stdafx.h"
            #include "windows.h"


            // ========== 定義一個代碼結構,本例為一個對話框============
            struct MyData
            {
            char sz[64]; // 對話框顯示內容
            DWORD dwMessageBox; // 對話框的地址
            };

            // ========== 遠程線程的函數 ==============================
            DWORD __stdcall RMTFunc(MyData *pData)
            {
            typedef int(__stdcall*MMessageBox)(HWND,LPCTSTR,LPCTSTR,UINT);
            MMessageBox MsgBox = (MMessageBox)pData->dwMessageBox;
            MsgBox(NULL, pData->sz, NULL, MB_OK);
            return 0;
            }
            int main(int argc, char* argv[])
            {
            // ===== 獲得需要創建REMOTETHREAD的進程句柄 ===============================
            HWND hWnd = FindWindow("notepad", NULL); // 以NOTEPAD為例
            DWORD dwProcessId;
            ::GetWindowThreadProcessId(hWnd, &dwProcessId);
            HANDLE hProcess = OpenProcess(
                     PROCESS_ALL_ACCESS,
                     FALSE,
                     dwProcessId);

            // ========= 代碼結構 ================================================
            MyData data;
            ZeroMemory(&data, sizeof (MyData));
            strcat(data.sz, "對話框的內容.");
            HINSTANCE hUser = LoadLibrary("user32.dll");
            if (! hUser)
            {
               printf("Can not load library.\n");
               return 0;
            }
            data.dwMessageBox = (DWORD)GetProcAddress(hUser, "MessageBoxA");
            FreeLibrary(hUser);
            if (! data.dwMessageBox)
               return 0;

            // ======= 分配空間 ===================================================
            void *pRemoteThread
               = VirtualAllocEx(hProcess, 0,
                   1024*4, MEM_COMMIT|MEM_RESERVE,
                   PAGE_EXECUTE_READWRITE);
            if (! pRemoteThread)
               return 0;
            if (! WriteProcessMemory(hProcess, pRemoteThread, &RMTFunc, 1024*4, 0))
               return 0;

            MyData *pData
               = (MyData*)VirtualAllocEx(hProcess, 0,
                   sizeof (MyData), MEM_COMMIT,
                   PAGE_READWRITE);
            if (!pData)
               return 0;

            if (! WriteProcessMemory(hProcess, pData, &data, sizeof (MyData), 0))
               return 0;

            // =========== 創建遠程線程 ===========================================
            HANDLE hThread
               = CreateRemoteThread(hProcess, 0,
                    0, (LPTHREAD_START_ROUTINE)pRemoteThread,
                    pData, 0, 0);
            if (! hThread)
            {
               printf("遠程線程創建失敗");
               return 0;
            }
            CloseHandle(hThread);
            VirtualFreeEx(hProcess, pRemoteThread, 1024*3, MEM_RELEASE);
            VirtualFreeEx(hProcess, pData, sizeof (MyData), MEM_RELEASE);
            CloseHandle(hProcess);
            printf("Hello World!\n");
            return 0;
            }


            posted @ 2010-06-21 22:53 lhking 閱讀(467) | 評論 (0)編輯 收藏
            Windows黑客編程基礎
            文章作者:不詳  
            本文連接:http://hi.baidu.com/wwwanq/


              從理論上說,任何一門語言都可以在任何一個系統上編程,只要找到該系統提供的“接口”和對系統內部機制有深

            入的了解就可以了,至少我是這么認為的。正如c語言可以在windows下編程,也同樣可以在Linux上大放異彩一樣。

              編程是一項很繁雜的工作,除了應用編程工具之外,了解系統本身內部工作機理非常重要,這是你寫出穩定兼容的

            程序所必不可少的前提條件。你要在哪一種系統上編程就要對該系統的機制進行研究,至少你應該知道一個程序在那個

            系統上是如何運行的。

              一、了解Windows 內部機制

              Windows 是一個“基于事件的,消息驅動的”操作系統。

              在Windows下執行一個程序,只要用戶進行了影響窗口的動作(如改變窗口大小或移動、單擊鼠標等)該動作就會

            觸發一個相應的“事件”。系統每次檢測到一個事件時,就會給程序發送一個“消息”,從而使程序可以處理該事件。

            每個Windows 應用程序都是基于事件和消息的,而且包含一個主事件循環,它不停地、反復地檢測是否有用戶事件發

            生。每次檢測到一個用戶事件,程序就對該事件做出響應,處理完再等待下一個事件的發生。

              Windows 下的應用程序不斷地重復這一過程,直至用戶終止程序,用代碼來描述實際上也就是一個消息處理過程

            的while循環語句。

              下面便簡單介紹一下與 Windows 系統密切相關的幾個基本概念:

              ⒈窗口:這是我要說的第一個概念。似乎是地球人都知道的事兒了,窗口是Windows本身以及Windows 環境下的

            應用程序的基本界面單位,但是很多人都誤以為只有具有標題欄、狀態欄、最大化、最小化按鈕這樣標準的方框才叫

            窗口。其實窗口的概念很廣,例如按鈕和對話框等也是窗口哦,只不過是一種特殊的窗口罷了。

              從用戶的角度看,窗口就是顯示在屏幕上的一個矩形區域,其外觀獨立于應用程序,事實上它就是生成該窗口

            的應用程序與用戶間的直觀接口;從應用程序的角度看,窗口是受其控制的一部分矩形屏幕區。應用程序生成并控

            制與窗口有關的一切內容,包括窗口的大小、風格、位置以及窗口內顯示的內容等。用戶打開一個應用程序后,程

            序將創建一個窗口,并在那里默默地等待用戶的要求。每當用戶選擇窗口中的選項,程序即對此做出響應。

              ⒉程序:通常說的程序都是指一個能讓計算機識別的文件,接觸得最多的便是.exe型的可執行文件,這個不難

            理解。

              ⒊進程:說到進程,學過《操作系統》的人都很清楚,所謂進程就是應用程序的執行實例(或稱一個執行程序)

            需要注意的是:進程是程序動態的描述,而上面說到的程序是靜態的描述,兩者有本質的區別。舉個例子,從網上

            Down了一個瑞星殺毒軟件到C盤但沒有運行,那個.exe 可執行文件叫做程序,它是一個二進制碼的文件。一旦雙擊

            了exe文件圖標運行程序,那個“正在運行著的瑞星殺毒”便稱為進程,它在雙擊的那一刻被系統創建,當你關機

            或者在任務欄的圖標上單擊鼠標右鍵選“退出”時,進程便消亡,徹底結束了生命。進程經歷了由“創建”到

            “消亡”的生命期,而程序自始至終存在于你的硬盤上,不管你的機器是否啟動。

              ⒋線程:線程是進程中的一個執行單元,同一個進程中的各個線程對應于一組CPU指令、一組CPU寄存器以及一

            堆棧。進程本來就具有動態的含義,然而實質上是通過線程來執行體現的,從這個意義上說,Windows 中進程的動

            態性意義已經不是很明顯了,只算是給程序所占的資源劃定一個范圍而已(個人觀點,純屬個人理解,不必引起爭

            議!),真正具有動態性意義的是線程。以前在大二學習操作系統課的時候就有個同學跟筆者提起這點,筆者還跟

            他駁得面紅耳赤呢!現在想想,覺得很有道理,不得不佩服那位同學對Windows內部機制了解得如此清楚。

              之所以在此花那么多的篇幅說線程,是因為下面將要介紹到多線程編程技巧,如果不理解這點,那就很難應

            用到實踐上,希望大家明白。

              ⒌消息:我們幾乎做每一個動作都會產生一個消息,在用鼠標指點江山的今天,鼠標被移動會產生WM_MOUSEMOVE

            消息,鼠標左鍵被按下會產生WM_LBUTTONDOWN的消息,鼠標右鍵按下便產生WM_RBUTTONDOWN消息等等。所有的這些都

            可以通過GetMessage,SendMessage等函數得到,以后的操作中我們會經常接觸到這些函數。

              ⒍事件:何謂事件?從它的字面意思我們就可以明白它的含義,如在程序運行的過程中改變窗口的大小或者移動

            窗口等,都會觸發相應的“事件”。

              ⒎句柄:單單一個“柄”字便可以解釋它的意思了,我們天氣熱搖扇子的時候只要抓住扇柄便可以控制整個扇子

            的運動了,在程序中也差不多是這個意思。通常一個句柄就可以傳遞我們所要做的事情。有經驗的讀者肯定清楚,編

            寫程序總是要和各種句柄打交道的,句柄是系統用來標識不同對象類型的工具,如窗口、菜單等,這些東西在系統中

            被視為不同類型的對象,用不同的句柄將他們區分開來。

              看看C++ 教材中是如何給句柄下定義的:“在Win32里,句柄是指向一個無值型對象(void *)的指針,是一個4

            字節長的數據”。雖然我對它的本質是什么還是很迷惑,但我知道句柄并不是一個真正意義上的指針。從結構上看,

            句柄的確是一個指針,盡管它沒有指向用于存儲某個對象的內存位置(很多書都這么說,這正是我的迷惑所在),而

            實際上句柄指向的是一個包含了對該對象進行的引用的位置。在編程時,只要抓住了對象的句柄就可以對該對象進行

            操作了(我在《一個簡單木馬程序的編寫與偽裝策略》中說到的對QQ密碼的截獲就是要找到QQ登陸窗口的句柄后才開

            始截密行動的)。下面再舉個例子來說明句柄的運用:編一個程序,使QQ登陸窗口的號碼框和密碼框均變黑,相關代

            碼及解釋:

            void __fastcall Tform1::formCreate(TObject *Sender)
            {
            HWND hCurWindow,HC,HE;//定義三個窗口句柄變量,hCurWindow用于存放QQ用戶登陸窗口的句柄,HC、HE分別存放
            //號碼框和密碼框的句柄。
            if((hCurWindow= FindWindow(NULL,"QQ用戶登錄"))!=0||(hCurWindow=FindWindow(NULL,"OICQ用戶登錄"))!=0)
            {//很明顯,調用FindWindow()函數去獲得QQ登陸窗口的句柄
            String str;
            str.sprintf("0x%x",hCurWindow);
            }
            TCHAR wClassName[255];//類名變量
            HC=GetWindow(hCurWindow, GW_CHILD);//得到號碼框的句柄
            HE=GetWindow(HC, GW_HWNDNEXT);//接著得到密碼框的句柄
            GetClassName(HE, wClassName, sizeof(wClassName));//得到類名
            GetClassName(HC, wClassName, sizeof(wClassName));//得到類名
            EnableWindow(HE,false);//使窗口失效
            EnableWindow(HC,false);//使窗口失效
            }
              以上代碼在C++ Builder下編譯通過,只要運行次程序,QQ登陸窗口的號碼框和密碼框馬上變黑色,無非是

            EnableWindow()函數所起的作用。

              你還可以添加一個Timer控件,將上面的代碼copy到void __fastcall Tform1::Timer1Timer

            (TObject *Sender)函數中,并在后邊加上這一句代碼:

            SendMessage(hCurWindow,WM_CLOSE,0,0); 使QQ一啟動就關閉,讓別人永遠也用不了QQ,挺有趣兒的哦:).

              ⒏API與SDK:API是英文 Application Programming Interface 的簡稱,意為“應用程序接口”,泛指系

            統為應用程序提供的一系列接口函數。其實質是程序內的一套函數調用,在編程的時候可以直接調用,而不必

            知道其內部實現的過程,只知道它的原型和返回值就可以了,此外,手頭經常放著一本“Windows API大全”之

            類的書也是必不可少的,不然你根本不知道哪些API是干什么用的,瞎編也編不出什么東西來。在后面我們會介

            紹調用API編程的例子,調用API編程工作雖然煩瑣,但由于API函數都被封裝在dll庫里,程序只有在運行的時

            候才調用的,因此程序的體積小而且運行效率高。

              SDK是英文 Software Development Kit 的縮寫,指“軟件開發工具包”,在防火墻的設計中就經常涉及到

            SDK。

              有關基本的概念就談這些,那些C/C++的基本語法、什么是面向對象等知識請大家查閱相關的書,此類書

            籍各大書店已汗牛充棟,不再多敘。下面直接談談語種和編程工具的選擇問題,這也是初學者們最迷惑的問題。

              二、編程語言以及工具的選擇:

              從上面的介紹我們對Windows 有了進一步的了解,現在就該開始行動了,選擇要學的語言和工具是第一步

            ,而且是非常重要的一步工作,筆者建議一切以簡單、易接受為原則,不然你會自信心大減的,何必偏要跟

            自己過不去自討苦吃呢?

              在開始的時候很多人都感到迷惑,目前的編程語言那么多,有c、c++、c#、java、匯編、html等等,究

            竟學哪些好呢?最開始我該學什么呢?甚至有人將vc、c++ builder也列為兩種不同的語言!這些都是對編

            程語言缺乏了解造成的。筆者開始的時候也犯過同樣的錯誤,曾經給自己寫過一份計劃書:先學c語言,接

            著學c++、c#、java、匯編、vb、vc、c++ builder……,哪一種語言用多少時間去專攻等等,現在回想起

            來覺得多么的可笑!只要學得精,一門就夠了。從實用的角度來講,C++ 是最好的選擇(個人意見,其實

            每一種語言都很好),而VC和C++ Builder是其相應開發工具的兩大主流,筆者極力推薦初學者使用C++ Builder,

            因為很容易上手,如果一下子就用VC的話,也許會打擊你的自信心:)。

              三、談談促進編程能力提高的兩個途徑

              如果你是一個黑客技術的狂熱者的話,到雅虎去搜索黑客教程的時候就會發現,很多的中文教程在談到如何

            進行黑客編程時,十有八九都會介紹以下兩大最佳途徑:一、讀程序;二、寫程序,并且都提出了教程作者的看法

            ,下面我想談談這方面的個人觀點。

              ⒈讀程序:我將讀程序放在前面是有原因的。在你沒有閱讀過一份完整的源代碼之前,你別指望能寫出有多好

            的程序來!這是對每一位初學者的忠告也是警告,而且必須具備一定的語言基礎知識,這里的基礎知識主要是指語

            法知識,最起碼要能讀懂別人的程序的每一行意思。有沒有程序的設計思想,在這個時期并不重要,只要具備一定

            的語法基礎就可以了,思想可以通過閱讀完別人的源程序后分析得來。

              記得在大一學習C語言的時候,我們都很重視語法的學習,整天都看教材、做練習,而且趕在老師的講課前預習,

            課后又復習鞏固,將一些語法點記得滾瓜爛熟,可后來一到做課程設計的時候,坐在電腦面前簡直是老鼠拖雞蛋—

            無從下手了,而且不斷的問自己:“我平時的努力哪去了?語法都會了呀,怎么還是做不出程序來?”相信很多人

            都像筆者以前那樣,錯誤地以為學會了語法就等于掌握了編程。

            編程的能力包括經驗、技巧、耐心等幾個因素,而并非想象中的那樣簡單,更不要以為編程就是簡簡單單的寫程序!

              其實學一門語言并不需要刻意去記那些條條框框的語法,在看代碼的時候,遇到了不明白的地方再去查相關的

            資料,一點一點補充基礎知識再配合源程序的思路,這時的理解才是最深刻的,我可以肯定地說,這個時候對語法

            的接受程度絕對比你剛開始時的死記要強!

              讀程序也不能單純地讀,要真正做到“俯而讀,昂而思”。好的代碼是百讀不厭的,比如Shotgun的那道構造

            洪水Ping攻擊的代碼,我至少讀了20遍。筆者喜歡將從網上搜集來的代碼打印到紙上(盡管學校的打印費貴得要命

            ,打一份代碼就得花去十幾塊甚至幾十塊大洋~~~),然后邊看邊做好眉批,遇到一個新函數記下它的功能,一些

            忘記了的知識在旁邊標出來,還可以寫上對程序的看法等等。特別是遇到了一些新的API函數,最好標出來,對你

            以后編程的時候也許會用得著,最后別忘了分析一下程序的思路,這樣對你以后編寫類似的程序很有幫助的。

              ⒉寫程序:問題可談到點子上了,學那么多語言,讀那么多程序最終還不是為了寫程序,做出適合需要的軟件

            來?“君子性非異也,善加于物也”,筆者認為一切從借鑒開始,先是修改別人的程序,等到有了一定的程度再寫

            出屬于自己的程序。

              剛開始寫程序,不要奢望一下子寫出很出色的程序來,“萬丈高樓平底起”,編程貴在動手,只要你動手去寫

            了,就算只有一句“printf(“Hello!”);”也是一次進步!此外,還要依照自身的能力循序漸進地寫,開始的時候

            寫一點功能簡單的、篇幅短小的代碼,力求簡潔、完整,“麻雀雖小,但五臟俱全”,然后在此基礎上進行擴充,

            一點一點添加功能,下面筆者摘錄一位國內一流編程高手、“豪杰超級解霸”的作者梁肇新的編程心得,請大家看

            看一個成功的程序員是如何寫程序的,希望對廣大菜鳥有所啟發:

              寫程序的方法:在Win98的環境中,先寫主干,用最少的代碼實現最基本的功能。然后一點點添加功能,每加

            一點都要調試。盡量少用動態分配、全局變量。充分利用操作系統直接提供的API。在Win98下調試通過之后,再

            在Win95下調試通過,然后是Win97,WindowsME,WinNT4.0。這樣才能寫出穩定、快速的程序。

              給程序員的建議:1、不要急于求成,這樣往往欲速不達。2、不要什么東西都想學,什么都沒掌握。3、每天

            都要自我總結,分析自己的錯誤率和廢碼率,不斷加強自我管理。4、代碼格式很重要。代碼要規范、嚴謹,效率要高。

            5、不要盲從簡單的開發工具(這點筆者不是很同意,最起碼要有一定的功底的人才敢這么說)。6、有了成果要公開,

            不要舍不得,不然很快會過時的(以上兩段摘自《程序員》增值合訂本2001.上冊P18,請讀者前往參考)。

            參考書籍:
            《Windows C 程序設計》,清華大學出版社
            《超級解霸梁肇新》,《程序員》合訂本

            黑客編程的幾個基本技巧
              以下將要談到的幾個基本技巧很重要,雖然對于編程高手來說這是在玩小孩子把戲,但對于一位初學者,掌握

            以下幾個技巧將為你的編程掃清道路,而且很容易編寫出有趣的程序,培養你對編程的興趣。

              技巧⒈學會修改注冊表。

              相信大家都知道當瀏覽了一些網頁惡意代碼,IE標題、默認主頁等被改得面目全非,這就是通過改動注冊表來

            更改系統設置的例子。Windows中的注冊表是個好東東,它是windows系統的靈魂,是許多軟件記錄數據的地方(當

            然也包括windows本身)。windows通過它記錄大量的數據,然后在下一次啟動時再讀取相應的數據來設置系統。通

            過控制注冊表就可以控制整個系統,所以很多的黑客程序都在注冊表上動手腳(尤其是木馬程序和作惡劇程序),

            學會修改注冊表可以實現一些有趣而強大的功能。我們完全可以通過編程來操作注冊表,達到與手動更改注冊表

            編輯器產生一樣的效果。“超級兔子”中的大部分功能就是通過修改注冊表來完成的。操作注冊表有專門的API函

            數,大家可以參考有關資料,下面筆者以C++ Builder為例說明如何在程序中操作注冊表:

              程序二:編程修改IE標題內容

              新建一個工程,在Unit1.h文件中包含Registry單元:

            #include

            然后就可以在.cpp文件操作注冊表了,接著來!在窗體的OnCreate()里加入以下代碼(你可以在try{}里面加入

            任何操作注冊表的代碼):

            TRegistry* Registry;
            Registry = new TRegistry();創建一個TRegistry類型的對象Registry,用于修改注冊表。
            try{
            Registry->RootKey = HKEY_CURRENT_USER;//設置主鍵,這是必不可少的,設置好主鍵后,就可以操作這個主
            //鍵下所有的鍵值了。
            if( Registry->OpenKey("Software\Microsoft\Internet Explorer\Main",FALSE))//調用OpenKey()

            //打開.
            //括號里所指的鍵
            {
            Registry->WriteString("Window Title",”臺灣是中國的一部分,世界上只有一個中國!”);
            //調用WriteString()往注冊表里寫入IE標題
            Registry->CloseKey();//關閉該鍵
            }
            else
            {//如果打開失敗的話
            Registry->CreateKey("Software\Microsoft\Internet Explorer\Main");//就調用CreateKey()
            //新建上述鍵
            Registry->WriteString("Window Title","臺灣是中國的一部分,世界上只有一個中國!");//再寫入//IE標
            //題內容
            Registry->CloseKey();//最后關閉該鍵,這個也不能忽視,它跟上面的OpenKey成對使用的}//End of try
            __finally
            {//要是出錯,跳到這里處理
            Registry->CloseKey();//關閉所要打開的鍵
            delete Registry;//銷毀Registry對象,釋放資源。
            }

              編譯運行上面的代碼就可以將IE的標題改為“臺灣是中國的一部分,世界上只有一個中國!”了。筆者寫了個

            小程序,可以測出當前的IE標題和默認主頁是什么,并可隨意修改他們,還可以禁止別人修改你的默認主頁和注冊

            表編輯器.。

              技巧⒉調用API編程

              其實這是最簡單的,API是系統在DLL里為我們提供的程序接口,可以直接調用的。只要我們有一本《Windows

            API大全》之類的書就足夠了,下面舉個簡單的例子:

              程序三:調用API函數隱藏Windows的任務欄:

            HWND WndHandle;//定義句柄類型變量

            WndHandle=FindWindow("Shell_TrayWnd",NULL);//調用API函數FindWindow()獲得任務欄的句柄

            ShowWindow(WndHandle,SW_HIDE);//再調用API函數ShowWindow()隱藏任務欄

              大家看到,在上面調用API函數FindWindow()和ShowWindow()的過程中,只要我們知道函數的

            名字和括號里的參數是什么就行了,至于實現的過程不必理會,也輪不到我們這些菜鳥去理會:)學會

            調用API,你可以寫出功能強大的程序來,這一技巧對于初學者來說是必須掌握的(代碼請參考黑防光

            盤)。

              技巧⒊多線程編程技術

              通過上一篇的介紹 ,大家都很清楚線程的概念了,它是進程內部的一個執行單元(如一個函數等),上期說

            了那么多理論,現在該派上用場了。編寫多線程應用程序是指使程序在運行時創建多個線程并發地運行于同一個

            進程中。今年6月份橫空出世的“中國黑客”病毒不是采用了全球獨創的“三線程技術”嗎?雖然筆者沒機會分析

            它的樣本代碼,但此種病毒的工作效率如此之高是與它的多線程技術分不開的。
              
            使用多線程技術編程有如下優點:

              ①提高CPU的利用率。由于多線程并發運行,可以使用戶在做一件事情的時候還可以做另外一件事。特別是在

            多個CPU的情況下,更可以充分地利用硬件資源的優勢:將一個大任務分成幾個小任務,由不同的CPU來合作完成。

              ②采用多線程技術,可以設置每個線程的優先級,調整工作的進度。

            清楚了使用多線程技術的優勢之后,下面便來談談如何在C++ Builder環境下開發多線程的應用程序,在C++

            Builder 環境中,通過 TThread 類就可以很方便地編寫多線程應用程序(但不能直接使用,因此要派生新類),

            具體流程如下:

              從TThread 類派生出一個新的線程類->創建線程對象->設置線程對象的屬性項->掛起或喚醒線程(根據具體

            情況操作)->結束線程。

              要說明一點的是:在應用程序中要合理地設置線程的優先級。不要因為某些線程的優先級很高而使其他一些

            線程因為等不到CPU的處理時間而被“餓死”,也不要因為線程的級別都差不多而導致的頻繁切換花費大量的CPU時

            間。(本段引自《C++ Builder 5 編程實例與技巧》P284)。

              技巧⒋讓程序實現后臺監控
              這是一個很基本的技巧。如果你是一個木馬程序的愛好者,當你閱讀眾多的木馬源程序的時候,就會發現100%

            的木馬程序都很注意自身的后臺監控本領,也就是隱身技術,面對不同的系統要施展不同的對策才能實現。很多殺

            毒程序就采用了這種后臺監控技術,使程序隨著系統的啟動而運行,然后在后臺悄悄地監視系統的一舉一動,一發

            現有不對路的程序就把它“揪”出來示眾。實現程序的后臺監控技術有如下幾個關鍵:

              ①正常運行時,不顯示程序的窗體;

              ②系統每次啟動都自動運行程序一次;

              ③程序圖標不顯示在任務欄上;

              ④不顯示在按Ctrl+Alt+Del 調出的任務列表中;

              ⑤通過熱鍵可以調出隱藏的窗體

              實現方法:對于①,要不顯示窗體,我們可以編輯WinMain函數,設置ShowMainform值為False就可以隱藏程序

            的窗體了。參考代碼:Application->ShowMainform = false ;對于②,可以利用技巧1所介紹的方法修改注冊表,

            鍵值如下:HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionRun ,使用的是WriteString()

            方法。這是冰河等多種舊木馬慣用的啟動手段之一(當然還有文件關聯、注入dll等方法);對于③,要使程序圖

            標不顯示在任務欄上,也很簡單,調用API函數SetWindowLong 可以讓程序運行后不出現在任務欄里,不過要放在

            窗體的OnCreate()里面。代碼如下:

            SetWindowLong(Application->Handle,GWL_EXstyle,WS_EX_TOOLWINDOW);

            對于④,調用RegisterServiceProcess API 函數將程序注冊成為一個服務模式程序,讓它運行在較高的優先級下,

            就不會出現在程序列表中(對Win9X有效,WinNT/2000/XP下無效)。具體的代碼請參考筆者的《一個簡單木馬程序

            的編寫與偽裝策略》一文,不在此重敘對于⑤,要先定義捕獲Windows消息WM_HOTKEY的鉤子函數,然后向Windows

            加入一個全局原子,并保留其句柄,最后向Windows登記熱鍵,這個可以調用API函數RegisterHotKey來實現。

              技巧⒌使用定時觸發器

              在C++ Builder 環境下,定時觸發器即Timer控件,有時候我們希望程序隔一段時間重復執行相同的動作,比

            如對QQ密碼截獲的時候,就要隔一段間隔尋找一次QQ登錄窗口。在C++ Builder中,只要將執行這些動作的代碼放

            到一個Timer中去就OK了。

              聽說“中國黑客”病毒運行幾分鐘后就自動創建一個新的線程,用于尋找OICQ的“發送消息”窗口,在10分

            鐘內一直在找,一旦找到就將“去*****功”等帶有政治色彩的言論發送給受害者QQ上的好友,10分鐘后自動

            結束該線程。我想在查找“發送消息”窗口的10分鐘內就運用了定時器,該病毒是用匯編開發的。可是在C++

            Builder中是如何運用的呢?其實控件的出現使得編程變得很簡單,添加一個Timer控件,設置幾下控件的屬性,

            雙擊Timer控件,將代碼放到里面去就行了。程序執行的時候,相隔指定的時間就重復執行里面的代碼了。實際

            上筆者在上一期的“程序一”中尋找QQ登錄窗口時,就運用了定時器.

              有關編程技巧的介紹到此為止,請讀者參考另類書籍,掌握更多的黑客編程技巧,編寫出受歡迎的黑客程序

            來。

              五、Socket 編程與網絡通信基礎

            由于本文的主題是“黑客編程基礎”,而黑客是互連網上“來無影,去無蹤”的黑衣人,如冰河、網絡神偷等

            黑客程序都是基于互連網的,談黑客編程離開網絡編程就會大失其味。所以,下面接著談談網絡編程,大凡基

            于網絡應用的程序都離不開Socket。

              Socket 為套接字之意,是作為計算機與計算機之間通信的接口。有關Socket的概念在第6期《黑客防線》

            的《Socket 編程的基礎和基本過程》一文中有詳細的描述,請大家參考,不在此多敘。需要指出的是:Winsock

            是訪問眾多的基層網絡協議的一種接口,在每個Win32平臺上,它都以不同的形式存在著,Winsock 是網絡編程的

            接口,不是協議,這是容易弄錯的地方。

              現在來談談Winsock 編程的過程,大凡在Win32平臺上的Winsock編程都要經過下列的基本步驟:定義變量->

            獲得Winsock版本->加載Winsock庫->初始化->創建套接字->設置套接字選項->關閉套接字->卸載Winsock庫,

            釋放所有資源。

              下面以一道極其簡單的程序來說明如何進行Winsock編程。程序四:編一個程序來獲取本地機器的IP地址。

              使用Winsock提供的API函數是最基本的網絡技術,為了給初學者看個清楚,筆者打算在Visual C++ 和

            C++ Builder下各寫一個,便于大家區分這兩種不同的編程工具的特性(對于本程序來說,他們都差不多,而

            對于某些通信程序,他們實現起來就相差很遠了,但本質是差不多的)。先來看Visual C++ 下的源程序,實

            現步驟:打開Visual C++ ,從“File”菜單中的“New”新建一個工程,選中“Win 32 Console Application”

            ,意思是說生成的是Win32的控制臺程序。另外,初學者要注意一點:只要程序中用到了 Winsock API 函數,

            都要在工程設置的Link 中增加 Ws2_32.lib 文件,不然程序將不能通過編譯,方法是:點擊“Project”菜單,

            選擇“Settings... ALT+F7” ,在彈出的“Project Settings”對話框右側選“Link”標簽,再在“Project

            Options”下方的編輯框中增加Ws2_32.lib文件,點“OK”就可以了。

              加載好文件之后,就可以在CheckIP.cpp文件里加入以下代碼了:
            //-------Begin from ------------
            //包含需要使用的頭文件
            #include "stdafx.h"
            #include "windows.h"
            #include
            #include "stdio.h"
            #include "stdlib.h"
            #include "string.h"
            void CheckIP(void) //定義CheckIP()函數,用于獲取本機IP地址
            {
            WORD wVersionRequested;// WORD類型變量,用于存放Winsock版本的正確值SADATA wsaData;
            char name[255];//定義用于存放獲得的主機名的變量
            CString ip;//定義IP地址變量
            PHOSTENT hostinfo;
            wVersionRequested = MAKEWORD( 2, 0 );
            //調用MAKEWORD()獲得Winsock版本的正確值,用于下面的加載Winsock庫
            if ( WSAStartup( wVersionRequested, &wsaData ) == 0 ) {
            //現在是加載Winsock庫,如果WSAStartup()函數返回值為0,說明加載成功,程序可以繼續
            //往下執行
            if( gethostname ( name, sizeof(name)) == 0) {
            //如果成功地將本地主機名存放入由name參數指定的緩沖區中
            if((hostinfo = gethostbyname(name)) != NULL) {
            //這是獲取主機名,如果獲得主機名成功的話,將返回一個指針,指向hostinfo,hostinfo
            //為PHOSTENT型的變量,下面即將用到這個結構體
            LPCSTR ip = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list);
            //調用inet_ntoa()函數,將hostinfo結構變量中的h_addr_list轉化為標準的點分表示的IP
            //地址(如192.168.0.1)
            printf("%sn",ip);//輸出IP地址 } }
            WSACleanup( );//卸載Winsock庫,并釋放所有資源 } }
            int main(int argc, char* argv[])//主函數,程序的入口
            {
            CheckIP();//調用CheckIP()函數獲得、輸出IP地址
            return 0;//由于main()定義為int型,所以應帶回一個int型的數值
            }


              下面接著來看看在C++ Builder 下如何實現,其實兩者的思想是一樣的,只是在C++ Builder下實現

            的界面友好點而已,實現方法:打開C++ Builder 5,默認情況下已經新建一個工程,保存這個工程文件

            就可以了,構造如下面圖4所示的界面,在相應之處添入下面的代碼即可。

            程序代碼:
            //包含頭文件
            #include
            #include
            #pragma hdrstop
            #include "Unit1.h"
            #pragma package(smart_init)
            #pragma resource "*.dfm"
            Tform1 *form1;
            __fastcall Tform1::Tform1(TComponent* Owner)
            : Tform(Owner)
            { }
            void Tform1::GetHostIpAddress()
            {// GetHostIpAddress()獲得本機IP地址
            struct hostent *thisHost;
            struct in_addr in;
            char MyName[80];
            char *ptr;
            WORD wVersionRequested;
            WSADATA wsaData;
            int err;
            wVersionRequested = MAKEWORD( 2, 0 );
            err = WSAStartup( wVersionRequested, &wsaData );
            if( err != 0 )
            return;
            if(LOBYTE( wsaData.wVersion ) != 2 ||
            HIBYTE( wsaData.wVersion ) != 0 )
            { WSACleanup( );
            return; }
            if(gethostname(MyName,80)==SOCKET_ERROR)
            return;
            if(!(thisHost=gethostbyname(MyName)))
            return;
            memset((void *)&in,sizeof(in),0);
            in.s_addr=*((unsigned long *)thisHost->h_addr_list[0]);
            if(!(ptr=inet_ntoa(in)))
            return;
            WSACleanup( );
            Edit1->Text=AnsiString(ptr);}
            void __fastcall Tform1::formCreate(TObject *Sender)
            { GetHostIpAddress();}
            void __fastcall Tform1::Button1Click(TObject *Sender)
            {Close();//添加一個“確定”按鈕,點擊即關閉程序。}
              程序在 C++ Builder 5 下編譯通過,通過比較你會發現他們是大同小異的,對于同一程序,

            兩者工具各有秋千,至于選擇哪種由你決定,最好是兩者相得益彰。


              “臨淵羨魚,不如退而結網”,雖說“通往電腦的路不止一條”,然而對于編程,道路卻

            只有一條,就是:動手去做,親身實踐。
            posted @ 2010-06-21 22:50 lhking 閱讀(285) | 評論 (0)編輯 收藏
            C/C++頭文件一覽
            信息來源:http://hi.baidu.com/wwwanq/
            原始連接:http://www.vbnew.net/article/html/vbnew1154334399.php?hits=356

            C/C++頭文件一覽
            C、傳統 C++

            #include <assert.h>    //設定插入點
            #include <ctype.h>     //字符處理
            #include <errno.h>     //定義錯誤碼
            #include <float.h>     //浮點數處理
            #include <fstream.h>    //文件輸入/輸出
            #include <iomanip.h>    //參數化輸入/輸出
            #include <iostream.h>   //數據流輸入/輸出
            #include <limits.h>    //定義各種數據類型最值常量
            #include <locale.h>    //定義本地化函數
            #include <math.h>     //定義數學函數
            #include <stdio.h>     //定義輸入/輸出函數
            #include <stdlib.h>    //定義雜項函數及內存分配函數
            #include <string.h>    //字符串處理
            #include <strstrea.h>   //基于數組的輸入/輸出
            #include <time.h>     //定義關于時間的函數
            #include <wchar.h>     //寬字符處理及輸入/輸出
            #include <wctype.h>    //寬字符分類

            //////////////////////////////////////////////////////////////////////////

            標準 C++ (同上的不再注釋)

            #include <algorithm>    //STL 通用算法
            #include <bitset>     //STL 位集容器
            #include <cctype>
            #include <cerrno>
            #include <clocale>
            #include <cmath>
            #include <complex>     //復數類
            #include <cstdio>
            #include <cstdlib>
            #include <cstring>
            #include <ctime>
            #include <deque>      //STL 雙端隊列容器
            #include <exception>    //異常處理類
            #include <fstream>
            #include <functional>   //STL 定義運算函數(代替運算符)
            #include <limits>
            #include <list>      //STL 線性列表容器
            #include <map>       //STL 映射容器
            #include <iomanip>
            #include <ios>       //基本輸入/輸出支持
            #include <iosfwd>     //輸入/輸出系統使用的前置聲明
            #include <iostream>
            #include <istream>     //基本輸入流
            #include <ostream>     //基本輸出流
            #include <queue>      //STL 隊列容器
            #include <set>       //STL 集合容器
            #include <sstream>     //基于字符串的流
            #include <stack>      //STL 堆棧容器    
            #include <stdexcept>    //標準異常類
            #include <streambuf>    //底層輸入/輸出支持
            #include <string>     //字符串類
            #include <utility>     //STL 通用模板類
            #include <vector>     //STL 動態數組容器
            #include <cwchar>
            #include <cwctype>

            using namespace std;

            //////////////////////////////////////////////////////////////////////////

            C99 增加

            #include <complex.h>   //復數處理
            #include <fenv.h>    //浮點環境
            #include <inttypes.h>  //整數格式轉換
            #include <stdbool.h>   //布爾環境
            #include <stdint.h>   //整型環境
            #include <tgmath.h>   //通用類型數學宏
            posted @ 2010-06-21 22:48 lhking 閱讀(300) | 評論 (0)編輯 收藏
            限制程序只打開一個實例
            信息來源:http://hi.baidu.com/wwwanq/
            文章作者:fictiony

            當我們在做一些管理平臺類的程序(比如Windows的任務管理器)時,往往需要限制程序只能打開一個實例。解決這個問題的大致思路很簡單,無非是在程序打開的時候判斷一下是否有與自己相同的進程開著,如果有,則關閉自身,否則正常運行。
              但是,問題就出在如何判別是否有一個與自己相同的進程開著上面。我在網上搜索了一下相關的文章,發現對于這個問題的解決不外乎以下幾種方式:
              1、在進程初始化時使用::CreateMutex創建一個互斥對象,通過檢測互斥對象是否已存在來確定該程序是否已經運行。
              該方式的確可以很容易的實現判別程序實例是否已存在,只需要在InitInstance方法開頭添加以下語句:
            m_hUnique = ::CreateMutex(NULL, FALSE, UNIQUE_ID);
            if (GetLastError() == ERROR_ALREADY_EXISTS) return FALSE;

              UNIQUE_ID為具有唯一性的字符串,一般可以用VC++為主程序頭文件自動生成的包含標識宏(就是.h文件頂上的那一長串宏定義),當然,也可以用工具自己手動生成,隨君所好了^^。要注意的是別忘了在ExitInstance方法中用 CloseHandle(m_hUnique) 將該互斥對象關閉。但這種方式存在一個很大的問題,就是很難獲取已打開程序實例的主窗口句柄。而我們絕大多數時候,都需要將那個程序實例的主窗口激活。為了獲取主窗口句柄,就需要再用到后面提到的其他方法。
              2、遍歷所有已經打開的進程主窗口,比較窗口標題,如果找到滿足條件的標題,則表示程序已經運行,并激活該窗口。
              這種方式雖然可以找到程序的主窗口,但問題明顯:A.如果窗口標題經常變化怎么辦(比如標題中會帶有打開文檔的文件名)?B.如果其他程序的主窗口標題恰好與該程序的相同怎么辦?
              第一個問題可以通過寫注冊表或者寫INI文件的方式來解決。即當主窗口標題改變時,將新標題寫入注冊表或者INI文件。不過這種解決方式也忒麻煩了吧-_-||   第二個問題就麻煩了,至少我還沒有找到好的解決方案。如果非要說一個,那我提議你“想盡辦法”“不擇手段”的將窗口標題設的和別的程序絕對不同。不過估計搞定了這步,你半條命也快沒了。
              3、用::SetProp給主窗口添加一個具有唯一性的屬性值,以便在進程初始化的時候可以通過遍歷所有窗口的該屬性來判斷。
              添加屬性值的代碼一般可以放在InitInstance方法的最后,如下:
            ::SetProp(m_pMainWnd->m_hWnd, "UNIQUE_ID", (HANDLE)UNIQUE_ID);

              UNIQUE_ID是一個具有唯一性的整數值(為什么不能用字符串?因為字符串的比較需要將字符串讀取出來,而這兒只能記錄字符串地址,在別的程序里這個地址無意義,所以無法讀出這個字符串)。這種方式僅有的問題就出在如何確定該整數值是具有唯一性的。我們后面提出的解決方法,就是在這種方法的基礎上發展出來的。
              在總結了上述幾種方式的利弊之后,我發現,只需要為程序建立一個具有唯一性的整數值,一方面可以通過這個值是否存在來判斷程序是否已經運行(::CreateMutex其實也是類似的概念),另一方面可以通過將這個值賦給主窗口,以便能夠找到已打開的程序實例的主窗口句柄。于是,ATOM量便派上用場了(ATOM變量類型等同于WORD,因而是一個整數值)。
              ATOM量本質上就是散列表的鍵標識符,其對應鍵值為一個字符串。每個程序都有自己的ATOM量表,同時Windows也有一個全局的ATOM表。我們要用的方法就是,為程序創建一個全局的ATOM量,通過這個量是否存在來判斷程序是否已經運行,并通過將這個量作為屬性值添加到主窗口來標識這個主窗口。具體過程如下:
              1、給主程序App類添加一個ATOM類型的成員變量:m_aAppId,作為程序ID。
              2、在InitInstance方法開頭添加以下代碼(UNIQUE_ID是具有唯一性的字符串宏):
            m_aAppId = ::GlobalFindAtom(UNIQUE_ID); //查找程序ID是否存在
            if (m_aAppId) //程序ID存在,激活已打開的程序實例的主窗口
            {
            HWND hWnd = ::GetWindow(::GetForegroundWindow(), GW_HWNDFIRST);
            for (; hWnd; hWnd = ::GetWindow(hWnd, GW_HWNDNEXT))
            {
            if ((ATOM)::GetProp(hWnd, "APP_ID") == m_aAppId)
            {
            if (::IsIconic(hWnd)) ::ShowWindow(hWnd, SW_RESTORE); //還原最小化的窗口
            ::SetForegroundWindow(hWnd); //激活窗口
            m_aAppId = 0; //賦值0是為了防止ExitInstance中將找到的ATOM量刪除
            break;
            }
            }
            return FALSE;
            }
            else //程序ID不存在,創建程序ID
            {
            m_aAppId = ::GlobalAddAtom(APP_ID);
            }

              3、在InitInstance方法最后為主窗口添加標識屬性:
            ::SetProp(m_pMainWnd->m_hWnd, "APP_ID", (HANDLE)m_aAppId);

              4、在ExitInstance方法中添加下面代碼以刪除程序ID:
            if (m_aAppId) ::GlobalDeleteAtom(m_aAppId);

            心得:該方法所用到的ATOM量是一個應用廣泛的技術,如::CreateMutex、::SetProp等API函數都間接用到了ATOM量。利用它,我們可以做很多需要用到唯一性驗證的事情。

            posted @ 2010-06-21 22:47 lhking 閱讀(613) | 評論 (0)編輯 收藏
            MFC消息映射機制的剖析

            一,消息映射機制

            1,消息響應函數:(例:在CDrawView類響應鼠標左鍵按下消息)
            1)在頭文件(DrawView.h)中聲明消息響應函數原型。
            //{{AFX_MSG(CDrawView)   //注釋宏
            afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
            //}}AFX_MSG   //注釋宏
            說明:
            在注釋宏之間的聲明在VC中灰色顯示。afx_msg宏表示聲明的是一個消息響應函數。
            2)在源文件(DrawView.cpp)中進行消息映射。
            BEGIN_MESSAGE_MAP(CDrawView, CView)
            //{{AFX_MSG_MAP(CDrawView)
            ON_WM_LBUTTONDOWN()
            //}}AFX_MSG_MAP
            // Standard printing commands
            ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
            ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
            ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
            END_MESSAGE_MAP()
            說明:
            在宏BEGIN_MESSAGE_MAP()與END_MESSAGE_MAP()之間進行消息映射。
            宏ON_WM_LBUTTONDOWN()把消息WM_LBUTTONDOWN與它的響應函數OnLButtonDown()相關聯。這樣一旦有消息的產生,就會自動調用相關聯的消息響應函數去處理。
            宏ON_WM_LBUTTONDOWN()定義如下:
            #define ON_WM_LBUTTONDOWN() \
            { WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \
               (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown },
            3)源文件中進行消息響應函數處理。(DrawView.cpp中自動生成OnLButtonDown函數輪廓,如下)
            void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
            {
            // TODO: Add your message handler code here and/or call default
            CView::OnLButtonDown(nFlags, point);
            }
            說明:
            可見當增加一個消息響應處理,在以上三處進行了修改。可在消息響應函數里添加消息處理代碼完成對消息的響應、處理。

            2,消息響應的方式:
            1)在基類中針對每種消息做一個虛函數,當子類對消息響應時候,只要在子類中重寫這個虛函數即可。缺點:MFC類派生層次很多,如果在基類對每個消息進行虛函數處理,那么從基類派生的每個子類都將背負一個龐大的虛表,這樣浪費內存,故MFC沒有采取這中方式而采取消息映射方式。
            2)消息映射方式:MFC在后臺維護了一個句柄和C++對象指針對照表,當收到一個消息后,通過消息結構里資源句柄(查對照表)就可找到與它對應的一個C++對象指針,然后把這個指針傳給基類,基類利用這個指針調用WindowProc()函數對消息進行處理,WindowProc()函數中調用OnWndMsg()函數,真正的消息路由及處理是由OnWndMsg()函數完成的。由于WindowProc()和OnWndMsg()都是虛函數,而且是用派生類對象指針調用的,由多態性知最總終調用子類的。在OnWndMsg()函數處理的時候,根據消息種類去查找消息映射,判斷所發的消息有沒有響應函數,具體方式是到相關的頭文件和源文件中尋找消息響應函數聲明(從注釋宏//{{AFX_MSG(CDrawView)...//}}AFX_MSG之間尋找),消息映射(從宏BEGIN_MESSAGE_MAP(...)....END_MESSAGE_MAP()之間尋找),最終找到對應的消息處理函數。當然,如果子類中沒有對消息進行處理,則消息交由基類處理。
            說明:
            virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
            virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);

             

            二,有關繪圖

            1,使用SDK獲取DC句柄:
            HDC hdc;
            hdc=::GetDc(m_hWnd);//獲取DC句柄
            MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);
            LineTo(hdc,point.x,point.y);
            ::ReleaseDC(m_hWnd,hdc);//釋放DC

            2,利用CDC類指針和CWin類成員函數獲取DC。
            CDC *pDC=GetDC();
            pDC->MoveTo(m_ptOrigin);
            pDC->LineTo(point);
            ReleaseDC(pDC);

            3,利用CClientDC對象。(CClientDC類從CDC類派生來的)
            CClientDC dc(this);
            dc.MoveTo(m_ptOrigin);
            dc.LineTo(point);
            說明:
            The CClientDC class is derived from CDC and takes care of calling the Windows functions GetDC at construction time and ReleaseDC at destruction time. This means that the device context associated with a CClientDC object is the client area of a window.

            4,利用CWindowDC對象。(CWindowDC類從CDC類派生來的)
            CWindowDC dc(this);//
            dc.MoveTo(m_ptOrigin);
            dc.LineTo(point);
            說明:
            The CWindowDC class is derived from CDC. It calls the Windows functionsGetWindowDC at construction time andReleaseDC at destruction time. This means that a CWindowDC object accesses the entire screen area of a CWnd (both client and nonclient areas).

            5,GetParent()得到父窗口指針;GetDesktopWindow()得到屏幕窗口指針。

            6,利用畫筆改變線條顏色和類型:
            CPen pen(PS_DOT,1,RGB(0,255,0));//構造畫筆對象
            CClientDC dc(this);CPen *pOldPen=dc.SelectObject(&pen);//將畫筆選入DC
            dc.MoveTo(m_ptOrigin);
            dc.LineTo(point);
            dc.SelectObject(pOldPen);//恢復先前的畫筆

            7,使用畫刷(通常利用畫刷去填充矩形區域):
            使用單色畫刷
            CBrush brush(RGB(255,0,0));//構造畫刷對象
            CClientDC dc(this);
            dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的畫刷去填充矩形區域

            使用位圖畫刷
            CBitmap bitmap;//構造位圖對象(使用前需要初試化)
            bitmap.LoadBitmap(IDB_BITMAP1);//初試化位圖對象
            CBrush brush(&bitmap);//構造位圖畫刷
            CClientDC dc(this);
            dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的位圖畫刷去填充矩形區域

            使用透明畫刷
            CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//獲取透明畫刷對象指針
            CClientDC dc(this);
            CBrush *pOldBrush=dc.SelectObject(pBrush);//將透明畫刷選入DC
            dc.Rectangle(CRect(m_ptOrigin,point));
            dc.SelectObject(pOldBrush);//釋放透明畫刷
            說明:
            The GetStockObject function retrieves a handle to one of the predefined stock pens, brushes, fonts, or palettes.
            HGDIOBJ GetStockObject(
            int fnObject   // type of stock object
            );

            Returns a pointer to a CBrush object when given a handle to a Windows HBRUSH object.
            static CBrush* PASCAL FromHandle( HBRUSH hBrush );//FromHandle是一個靜態方法,故可用CBrush::FromHandle()形式調用。
            注意點:
            1)靜態方法不屬于某一個具體對象,而屬于類本身,在類加載的時候就已經為類靜態方法分配了代碼去,故可用CBrush::FromHandle()形式調用。
            2)靜態方法中,不能引用非靜態的數據成員和方法。
            3)靜態數據成員需要在類外單獨做初始化,形式如: 變量類型 類名::變量名=初始值;

            8,CDC::SetROP2方法:
            int SetROP2( int nDrawMode );
            Sets the current drawing mode.  

            posted @ 2010-06-21 22:43 lhking 閱讀(273) | 評論 (0)編輯 收藏

            1,尋找WinMain人口:
            在安裝目錄下找到MFC文件夾下的SRC文件夾,SRC下是MFC源代碼。
            路徑:MFC|SRC|APPMODUL.CPP:
            _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
            LPTSTR lpCmdLine, int nCmdShow)
            {
            // call shared/exported WinMain
            return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
            }
            注意:(#define _tWinMain   WinMain)

            2,對于全局對象或全局變量來說,在程序運行即WINMAIN函數加載的時候,已經為全局對象或全局變量分配了內存和賦初值。
            所以:CTEApp theApp;->CTEApp ::CTEApp(){}->_tWinMain(){}
            說明:每一個MFC程序,有且只有一個從WinApp類派生的類(應用程序類),也只有一個從應用程序類所事例化的對象,表示應用程序本身。在WIN32程序當中,表示應用程序是通過WINMAIN入口函數來表示的(通過一個應用程序的一個事例號這一個標識來表示的)。在基于MFC應用程序中,是通過產生一個應用程序對象,用它來唯一的表示了應用程序。

            3,通過構造應用程序對象過程中調用基類CWinApp的構造函數,在CWinApp的構造函數中對程序包括運行時一些初始化工作完成了。
            CWinApp構造函數:MFC|SRC|APPCORE.CPP
            CWinApp::CWinApp(LPCTSTR lpszAppName){...}//帶參數,而CTEApp構造函數沒有顯式向父類傳參,難道CWinApp()有默認參數?見下:
            (在CWinApp類定義中, CWinApp(LPCTSTR lpszAppName = NULL); )
            注意:CWinApp()函數中:
            pThreadState->m_pCurrentWinThread = this;
            pModuleState->m_pCurrentWinApp = this
            (this指向的是派生類CTEApp對象,即theApp)
            調試:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}

            4,_tWinMain函數中通過調用AfxWinMain()函數來完成它要完成的功能。(Afx*前綴代表這是應用程序框架函數,是一些全局函數,應用程序框架是一套輔助生成應用程序的框架模型,把一些類做一些有機的集成,我們可根據這些類函數來設計自己的應用程序)。
            AfxWinMain()函數路徑:MFC|SRC|WINMAIN.CPP:
            在AfxWinMain()函數中:
            CWinApp* pApp = AfxGetApp();
            說明:pApp存儲的是指向WinApp派生類對象(theApp)的指針。
            //_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
            // { return afxCurrentWinApp; }

            調用pThread->InitInstance()
            說明:pThread也指向theApp,由于基類中virtual BOOL InitApplication()定義為虛函數,所以調用pThread->InitInstance()時候,調用的是派生類CTEApp的InitInstance()函數。

            nReturnCode = pThread->Run();
            說明:pThread->Run()完成了消息循環。

            5,注冊窗口類:AfxEndDeferRegisterClass();
            AfxEndDeferRegisterClass()函數所在文件:MFC|SRC|APPCORE.CPP
            BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...}
            說明:設計窗口類:在MFC中事先設計好了幾種缺省的窗口類,根據不同的應用程序的選擇,調用AfxEndDeferRegisterClass()函數注冊所選擇的窗口類。
            調試:CWinApp::CWinApp();->CTEApp theApp;(->CTEApp ::CTEApp())->CWinApp::CWinApp()->CTEApp ::CTEApp()->_tWinMain(){}//進入程序
            ->AfxWinMain();->pApp->InitApplication();->pThread->InitInstance()//父類InitInstance虛函數;->CTEApp::InitInstance()//子類實現函數;->AfxEndDeferRegisterClass(LONG fToRegister)//注冊所選擇的窗口類(出于文檔管理,注冊提前,正常的應在PreCreateWindow中進行注冊)//之后進入創建窗口階段(以下再不做調試)

            6,PreCreateWindow()://主要是注冊窗口類
            BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
            {
            if( !CFrameWnd::PreCreateWindow(cs) )
               return FALSE;
            return TRUE;
            }
            說明:
            CFrameWnd::PreCreateWindow()函數所在文件:MFC|SRC|WINFRM.CPP
            BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
            {
            if (cs.lpszClass == NULL)
            {
               VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
                //判斷AFX_WNDFRAMEORVIEW_REG型號窗口類是否注冊,如果沒有注冊則注冊
               cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background
                //把注冊后的窗口類名賦給cs.lpszClass
            }

            if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4)
               cs.style |= FWS_PREFIXTITLE;

            if (afxData.bWin4)
               cs.dwExStyle |= WS_EX_CLIENTEDGE;

            return TRUE;
            }

            其中:
            virtual BOOL PreCreateWindow(CREATESTRUCT& cs);//PreCreateWindow()是個虛函數,如果子類有則調用子類的。
            #define VERIFY(f)          ASSERT(f)
            #define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
            define AFX_WNDFRAMEORVIEW_REG          0x00008
            const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//WINCORE.CPP文件中,定義為全局數組。
            //#define AFX_WNDFRAMEORVIEW AFX_WNDCLASS("FrameOrView")

            7,創建窗口:
            Create()函數路徑:MFC|SRC|WINFRM.CPP:
            CFrameWnd::Create(...){
            ...
            CreateEx(...);//從父類繼承來的,調用CWnd::CreateEx().
            ...
            }

            CWnd::CreateEx()函數路徑:MFC|SRC|WINCORE.CPP
            BOOL CWnd::CreateEx(...){
            ...
            if (!PreCreateWindow(cs))//虛函數,如果子類有調用子類的。
            {
               PostNcDestroy();
               return FALSE;
            }
            ...
            HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
               cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
               cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);

            ...
            }
            說明:CreateWindowEx()函數與CREATESTRUCT結構體參數的對應關系,使我們在創建窗口之前通過可PreCreateWindow(cs)修改cs結構體成員來修改所要的窗口外觀。PreCreateWindow(cs))//是虛函數,如果子類有調用子類的。
            HWND CreateWindowEx(
            DWORD dwExStyle,     
            LPCTSTR lpClassName,
            LPCTSTR lpWindowName,
            DWORD dwStyle,       
            int x,               
            int y,               
            int nWidth,          
            int nHeight,         
            HWND hWndParent,     
            HMENU hMenu,         
            HINSTANCE hInstance,
            LPVOID lpParam       
            );
            typedef struct tagCREATESTRUCT { // cs
                LPVOID    lpCreateParams;
                HINSTANCE hInstance;
                HMENU     hMenu;
                HWND      hwndParent;
                int       cy;
                int       cx;
                int       y;
                int       x;
                LONG      style;
                LPCTSTR   lpszName;
                LPCTSTR   lpszClass;
                DWORD     dwExStyle;
            } CREATESTRUCT;

            8,顯示和更新窗口:
            CTEApp類,TEApp.cpp中
            m_pMainWnd->ShowWindow(SW_SHOW);//顯示窗口,m_pMainWnd指向框架窗口
            m_pMainWnd->UpdateWindow();//更新窗口
            說明:
            class CTEApp : public CWinApp{...}
            class CWinApp : public CWinThread{...}
            class CWinThread : public CCmdTarget
            {
            ...
            public:
            CWnd* m_pMainWnd;
            ...
            ...
            }

            9,消息循環:
            int AFXAPI AfxWinMain()
            { ...
            // Perform specific initializations
            if (!pThread->InitInstance()){...}
            //完成窗口初始化工作,完成窗口的注冊,完成窗口的創建,顯示和更新。
            nReturnCode = pThread->Run();
            //繼承基類Run()方法,調用CWinThread::Run()來完成消息循環
            ...
            }
            ////////////////////////////////////////////////////////////////
            CWinThread::Run()方法路徑:MFC|SRC|THRDCORE.CPP
            int CWinThread::Run()
            { ...
               // phase2: pump messages while available
               do//消息循環
               {
                // pump message, but quit on WM_QUIT
                if (!PumpMessage())//取消息并處理
                 return ExitInstance();
                ...
               } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
            ...
            }
            說明:
            BOOL PeekMessage(,,,,)函數說明
            The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure.
            If a message is available, the return value is nonzero.
            If no messages are available, the return value is zero.

            /////////////////////////////////////////////////////////////
            BOOL CWinThread::PumpMessage()
            {
            ...
            if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))//取消息
            {...}
            ...
            // process this message
            if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
            {
               ::TranslateMessage(&m_msgCur);//進行消息(如鍵盤消息)轉換
               ::DispatchMessage(&m_msgCur);//分派消息到窗口的回調函數處理(實際上分派的消息經過消息映射,交由消息響應函數進行處理。)
            }
            return TRUE;
            }

            9,文檔與視結構:
            可以認為View類窗口是CMainFram類窗口的子窗口。
            DOCument類是文檔類。
            DOC-VIEW結構將數據本身與它的顯示分離開。
            文檔類:數據的存儲,加載
            視類:數據的顯示,修改

            10,文檔類,視類,框架類的有機結合:
            在CTEApp類CTEApp::InitInstance()函數中通過文檔模板將文檔類,視類,框架類的有機組織一起。
            ...
            CSingleDocTemplate* pDocTemplate;
            pDocTemplate = new CSingleDocTemplate(
            IDR_MAINFRAME,
            RUNTIME_CLASS(CTEDoc),
            RUNTIME_CLASS(CMainFrame),       // main SDI frame window
            RUNTIME_CLASS(CTEView));
            AddDocTemplate(pDocTemplate);//增加到模板
            ...


            posted @ 2010-06-21 22:43 lhking 閱讀(448) | 評論 (0)編輯 收藏
            Windows程序內部運行機制

            1,windows程序設計是種事件驅動方式的程序設計,主要基于消息的。當用戶需要完成某種功能時,需要調用OS某種支持,然后OS將用戶的需要包裝成消息,并投入到消息隊列中,最后應用程序從消息隊列中取走消息并進行響應。
            2,消息結構:
            typedef struct tagMSG {     // msg
                HWND   hwnd;     //接收消息的窗口句柄。和哪個窗口相關聯。
                UINT   message; //消息標識。消息本身是什么。
                WPARAM wParam;   //消息的附加信息。具體取決于消息本身。  
                LPARAM lParam;
                DWORD time;     //消息投遞時間。
                POINT pt;       //消息投遞時,光標在屏幕上的位置。
            } MSG;

            3,消息隊列:
            每個應用程序OS都為它建立一個消息隊列,消息隊列是個先進先出的緩沖區,其中每個元素都是一個消息,OS將生成的每個消息按先后順序放進消息隊列中,應用程序總是取走當前消息隊列中的第一條消息,應用程序取走消息后便知道用戶的操作和程序的狀態,然后對其處理即消息響應,消息響應通過編碼實現。

            4,使用VC編程除了良好的C基礎外還需要掌握兩方面:
            一,消息本身。不同消息所代表的用戶操作和應用程序的狀態。
            二,對于某個特定的消息來說,要讓OS執行某個特定的功能去響應消息。

            5,Window程序入口:
            int WINAPI WinMain(
            HINSTANCE hInstance, // 當前事例句柄。
            HINSTANCE hPrevInstance, // 先前事例句柄。
            LPSTR lpCmdLine,      // 命令行指針
            int nCmdShow          // (窗口)顯示的狀態
            );
            說明:WinMain函數是Windows程序入口點函數,由OS調用,當OS啟動應用程序的時候,winmain函數的參數由OS傳遞的。

            6,創建一個完整的窗口需要經過下面四個操作步驟:
            一,設計一個窗口類;如:WNDCLASS wndcls;
            二,注冊窗口類;    如:RegisterClass(&wndcls);
            三,創建窗口;      如:CreateWindow(),CreateWindowEX();
            四,顯示及更新窗口。如:ShowWindow(),UpdateWindow();

            說明:創建窗口的時候一定要基于已經注冊的窗口類.

            7,Windows提供的窗口類:
            typedef struct _WNDCLASS {
                UINT    style;        //窗口的類型
                WNDPROC lpfnWndProc; //窗口過程函數指針(回調函數)
                int     cbClsExtra; //窗口類附加字節,為該類窗口所共享。通常0。
                int     cbWndExtra; //窗口附加字節。通常設為0。
                HANDLE hInstance; //當前應用程序事例句柄。
                HICON   hIcon;      //圖標句柄 LoadIcon();
                HCURSOR hCursor;    //光標句柄 LoadCursor();
                HBRUSH hbrBackground; //畫刷句柄 (HBRUSH)GetStockObject();
                LPCTSTR lpszMenuName; //菜單名字
                LPCTSTR lpszClassName; //類的名字
            } WNDCLASS;

            8,窗口類注冊:
            ATOM RegisterClass(
            CONST WNDCLASS *lpWndClass   // address of structure with class
                                           // data
            );

            9,創建窗口:
            HWND CreateWindow(
            LPCTSTR lpClassName, // pointer to registered class name
            LPCTSTR lpWindowName, // pointer to window name
            DWORD dwStyle,        // window style
            int x,                // horizontal position of window
            int y,                // vertical position of window
            int nWidth,           // window width
            int nHeight,          // window height
            HWND hWndParent,      // handle to parent or owner window
            HMENU hMenu,          // handle to menu or child-window identifier
            HANDLE hInstance,     // handle to application instance
            LPVOID lpParam        // pointer to window-creation data
            );

            10,顯示和更新窗口窗口:
            BOOL ShowWindow(
            HWND hWnd,     // handle to window
            int nCmdShow   // show state of window
            );
            BOOL UpdateWindow(
            HWND hWnd   // handle of window
            );

            11,消息循環:
            MSG msg;
            while(GetMessage(&msg,...))    //從消息隊列中取出一條消息
            {
            TranslateMessage(&msg); //進行消息(如鍵盤消息)轉換
            DispatchMessage(&msg); //分派消息到窗口的回調函數處理,(OS調用窗口回調函數進行處理)。
            }

            其中:
            //**The GetMessage function retrieves a message from the calling thread's message queue and places it in the specified structure.
            //**If the function retrieves a message other than WM_QUIT, the return value is nonzero.If the function retrieves the WM_QUIT message, the return value is zero. If there is an error, the return value is -1.

            BOOL GetMessage(
            LPMSG lpMsg,         // address of structure with message
            HWND hWnd,           // handle of window
            UINT wMsgFilterMin, // first message
            UINT wMsgFilterMax   // last message
            );


            //The TranslateMessage function translates virtual-key messages into character messages. The character messages are posted to the calling thread's message queue, to be read the next time the thread calls the GetMessage or PeekMessage function.
            BOOL TranslateMessage(
            CONST MSG *lpMsg   // address of structure with message
            );

            //The DispatchMessage function dispatches a message to a window procedure.
            LONG DispatchMessage(
            CONST MSG *lpmsg   // pointer to structure with message
            );


            12,窗口過程函數(回調函數)原型:
            The WindowProc function is an application-defined function that processes messages sent to a window. The WNDPROC type defines a pointer to this callback function. WindowProc is a placeholder(占位符) for the application-defined function name.

            LRESULT CALLBACK WindowProc( //這里WindowProc是個代號名字。
            HWND hwnd,      // handle to window
            UINT uMsg,      // message identifier
            WPARAM wParam, // first message parameter
            LPARAM lParam   // second message parameter
            );

            說明:兩種函數調用約定(__stdcall 和 __cdecl):
            #define CALLBACK    __stdcall
            //__stdcall 標準調用預定,是PASCAL 調用約定,象DELPHI使用的就是標準調用約定
            #define WINAPIV     __cdecl
            // __cdecl 是C 語言形式的調用約定。


            主要區別:函數參數傳遞順序 和 對堆棧的清除上。
            問題:除了那些可變參數的函數調用外,其余的一般都是__stdcall約定。但 C/C++編譯默然的是__cdecl約定。所以如果在VC等環境中調用__stdcall約定的函數,必須要在函數聲明的時加上 __stdcall 修飾符,以便對這個函數的調用是使用__stdcall約定(如使用DELPHI編寫的DLL時候)。
            (VC中可通過這途徑修改:project|settings..|c/c++|...)


            在窗口過程函數中通過一組switch語句來對消息進行處理:
            如:
            LRESULT CALLBACK WindowProc(
            HWND hwnd,
            UINT uMsg,
            WPARAM wParam,
            LPARAM lParam  
            )
            {
                switch(uMsg)
                {
            case WM_PAINT:
               ...
               break;
            case ...
               break;
            case WM_CLOSE:
               //DestroyWindow(hwnd);
                //銷毀窗口,并發送WM_DESTROY消息。
               break;
            case WM_DESTROY:
               //PostQuitMessage(0);
               //發送WM_QUIT消息到消息隊列中,請求終止。
                     //GetMessage()取到WM_QUIT消息后,返回0,退出消息循                //   環,從而終止應用程序。
               break;
            default:
               return DefWindowProc(hwnd,uMsg,wParam,lParam);
            //用缺省的窗口過程處理我們不感興趣的消息(其它消息)。
            //這是必須的。
                }//switch
            return 0;
            }//WindowProc

            13,DestroyWindow()函數和PostQuitMessage()函數原型:
            //**The DestroyWindow function destroys the specified window. The function sends WM_DESTROY and WM_NCDESTROY messages。

            BOOL DestroyWindow(
            HWND hWnd   // handle to window to destroy
            );

            //**The PostQuitMessage function indicates to the system that a thread has made a request to terminate (quit). It is typically used in response to a WM_DESTROY message.
            //**The PostQuitMessage function posts a WM_QUIT message to the thread's message queue and returns immediately; the function simply indicates(預示,通知) to the system that the thread is requesting to quit at some time in the future.

            When the thread retrieves the WM_QUIT message from its message queue, it should exit its message loop and return control to the system.

            VOID PostQuitMessage(
            int nExitCode   // exit code
            );

            14,關于DC句柄獲取:
            a)使用BeginPaint(),EndPaint()對。注意只能在響應WM_PAINT消息時使用。
            b)使用GetDc(),ReleaseDC()對。注意他們不能在響應WM_PAINT中使用。

            posted @ 2010-06-21 22:41 lhking 閱讀(128) | 評論 (0)編輯 收藏
            僅列出標題
            共3頁: 1 2 3 

            導航

            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            統計

            常用鏈接

            留言簿

            隨筆檔案

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            aaa级精品久久久国产片| 久久久久久亚洲精品影院| 99精品久久精品一区二区| 亚洲伊人久久成综合人影院 | 久久亚洲AV成人出白浆无码国产 | 看全色黄大色大片免费久久久| 欧美国产精品久久高清| 亚洲精品高清国产一线久久| 少妇久久久久久久久久| 国产精品无码久久四虎| 亚洲国产美女精品久久久久∴| 国产精品对白刺激久久久| 久久久久亚洲精品天堂久久久久久| 久久国产AVJUST麻豆| 久久久久久综合一区中文字幕| 亚洲精品无码久久久| 2020久久精品国产免费| 国产精品99久久久精品无码| 国内精品久久久久久野外| 免费无码国产欧美久久18| 精品国产青草久久久久福利| 久久久久99精品成人片试看| 久久夜色精品国产| 久久久久中文字幕| 国产午夜福利精品久久2021| yy6080久久| 99久久这里只精品国产免费| 精品人妻伦一二三区久久| 国产精品一区二区久久| 97久久国产综合精品女不卡| 日本久久中文字幕| 久久国产一片免费观看| 精品国产乱码久久久久久浪潮| 青青青国产精品国产精品久久久久| 无码日韩人妻精品久久蜜桃| 伊人久久精品无码二区麻豆| 久久无码AV一区二区三区| 国内精品伊人久久久影院| 伊人 久久 精品| 婷婷五月深深久久精品| 久久99精品国产自在现线小黄鸭 |