Windows SDK编程——启动窗口

来源:互联网 发布:超星尔雅网络通识课 编辑:程序博客网 时间:2024/05/01 23:10

原文来自:http://hi.baidu.com/combojiang/item/a972f9149e7ceffcdceeca0d

windows sdk编程系列文章 ---- 启动画面

上一章我们学习了位图的使用.在这一章我们要用 上帝赋予我们的创造力来融会贯通上一章我们学到的知识.那就是研究如何用位图来创建启动画面. 
理论:     首先,我们先要搞清楚什么是启动画面.举个简单的例子:我们启动某些作 的专业一点的程序时(比如Netscape,Adobe Acrobat等)会先跳出一个启动画面.上面通常有一 些版权信息,版本号等.与此同时,程序后台正做着一些程序的加载或初始化工作.这个启动画面有别于 一般的窗口.它没有标题栏,没有系统菜单,也没有边框.只有一张位图在屏幕上显示一会儿,然后消失 .在这一章我们来试试自己做一个. 
     第一步你可能会想到把要显示的位图包含到资源文件中去.但是这样做 有一个缺点.你的程序只在启动的时候显示这张位图,可是它却至始至终存在于你的内存中,直到你 把程序关掉.这不能不说是对内存的极大浪费.好办法是:创建一个"资源"DLL(动态连接库)来包含位图 和它独特的显示代码.这样,你就可以在想显示启动画面的时候加载他,用完了就卸载它.所以,我们的 程序需要2个模块:主程序和启动画面DLL.我们要把位图放到这个DLL的资源中去. 
     基本步骤如下:
  1. 把位图作为一个位图资源放到DLL中去.
  2. 主程序调用 LoadLibrary 把 dll 加载到内存中去.
  3. DLL 被调用后,它会建立一个定时器用于管理启动画面显示的时间.然后,注册并创建一个没有 标题和边框的窗口.同时在窗口的客户区显示位图.
  4. 等启动画面的显示时间长度到达你预先的设定值,启动画面消失,控制权回到主程序手中.
  5. 主程序调用 FreeLibrary 从内存中卸载 DLL .然后,做它该做的事去.
     下面我们来研究细节部分加载/卸载 DLL你可以用 LoadLibrary 灵活的加载一个 DLL ,它的格式如下:
HMODULE LoadLibrary( LPCTSTR lpFileName );
它只有一个参数: 你想要加载的 DLL 的名称所在的地址.调用成功返回指向该DLL模块的句柄,反之返回NULL. 要卸载 DLL, 则调用 FreeLibrary:
BOOL FreeLibrary( HMODULE hModule );
它也只有一个参数: 你想要卸载的 DLL 模块的句柄(通常就是上面那个函数返回的啦).怎样使用定时器首先, 你要用 SetTimer 创建一个定时器:
UINT_PTR SetTimer(      
    HWND hWnd,      UINT_PTR nIDEvent,      UINT uElapse,      TIMERPROC lpTimerFunc );

hWnd接受这个定时器消息的窗口的句柄.如果,你的定时器不需要窗口接受它的消息,你也可以 用NULL作为参数
nIDEvent 定时器的 ID 值. 由你自己定义. 
uElapse 定时器定的时间.以ms(千分之一秒)为单位. 
lpTimerFunc 处理该定时器消息的函数所在的地址.如果你用NULL作为该参数,那么定时器的消息会被送给 hWnd 参数所指定的窗口.

SetTimer 如果成功则返回定时器的 ID 否则返回 NULL. 所以最好不要把定时器的ID设为0

你可以用2种方法创建定时器:
  • 如果你有一个窗口并且定时器把消息传给这个窗口.那么你需要把所有的4个参数都传送给 Settimer 函数 (lpTimerFunc参数必须为NULL).
  • 如果你没有窗口或者你不想让窗口处理定时器的消息,那么你必须在窗口句柄中传送一个NULL.同时你要指定 用于处理定时器消息的函数的地址.
在这个例子中我们要使用第一种方法. 
当你设定的时间到了, 与定时器相连的窗口会收到 WM_TIMER 消息.例如,你指定 uElapse 的值为 1000, 你的窗口每 过一秒都会收到 WM_TIMER 消息.等到你再也不需要这个定时器了,就用 KillTimer 来去除定时器.
BOOL KillTimer( HWND hWnd,      UINT_PTR uIDEvent );
例子: 见光盘FirstWindow24
;----------------------------------------------------------------------- 
;                         主程序 
;----------------------------------------------------------------------- 
#include "windows.h"
#include "tchar.h"
TCHAR ClassName[] = _T("SplashDemoClass");
TCHAR Libname[] = _T("Splash.dll");
TCHAR AppName[] = _T("Splash Screen Example");

LRESULT CALLBACK WindowProc(          HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
)
{
    switch(uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(NULL);
        break;
    
    default:
        return DefWindowProc(hwnd,uMsg,wParam,lParam);
    }

    return 0;
}
int WINAPI WinMain(          HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
)
{
    WNDCLASSEX wc;
    MSG msg;
    HWND hWnd;
    HMODULE hMod = LoadLibrary(Libname);
    if(hMod)
      FreeLibrary(hMod);

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = NULL;
    wc.cbWndExtra = NULL;
    wc.hInstance = hInstance;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = ClassName;
    wc.lpszMenuName = NULL;
    wc.hIcon = wc.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);
    RegisterClassEx(&wc);
    hWnd = CreateWindowEx(NULL,ClassName,AppName,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
        CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
    
    ShowWindow(hWnd,SW_SHOWNORMAL);
    while(GetMessage(&msg,NULL,0,0))
    {
         TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
    
}
;-------------------------------------------------------------------- 
;                         位图 DLL 
;-------------------------------------------------------------------- 
#include "windows.h"
#include "tchar.h"

TCHAR BitmapName[] = _T("MySplashBMP");
TCHAR ClassName[] = _T("SplashWndClass");
HBITMAP hBitmap;
DWORD TimerID;
HINSTANCE g_hInstance;

LRESULT CALLBACK WindowProc(          HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
)
{
    PAINTSTRUCT ps;
    HDC hdc;
    HDC hMemoryDC;
    HBITMAP hOldBmp;
    BITMAP bitmap;
    DWORD DlgHeight;
    DWORD DlgWidth;
    RECT DlgRect;
    RECT DesktopRect;

    switch(uMsg)
    {
    case WM_DESTROY:
        if(hBitmap)
            DeleteObject(hBitmap);
        PostQuitMessage(NULL);
        break;
    case WM_CREATE:
        GetWindowRect(hwnd,&DlgRect);
        GetWindowRect(GetDesktopWindow(),&DesktopRect);
        DlgHeight = DlgRect.bottom - DlgRect.top;
        DlgWidth = DlgRect.right - DlgRect.left;
        MoveWindow(hwnd,(DesktopRect.right - DlgWidth)/2,(DesktopRect.bottom - DlgHeight)/2,DlgWidth,DlgHeight,FALSE);
        hBitmap = LoadBitmap(g_hInstance,BitmapName);
        TimerID = SetTimer(hwnd,1,2000,NULL);
        break;
    case WM_LBUTTONDOWN:
        DestroyWindow(hwnd);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd,&ps);
        hMemoryDC = CreateCompatibleDC(hdc);
        hOldBmp = (HBITMAP)SelectObject(hMemoryDC,hBitmap);
        GetObject(hBitmap,sizeof(BITMAP),&bitmap);
        StretchBlt(hdc,0,0,250,250,hMemoryDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY);
        SelectObject(hMemoryDC,hOldBmp);
        DeleteDC(hMemoryDC);
        EndPaint(hwnd,&ps);
        break;
    case WM_TIMER:
        SendMessage(hwnd,WM_LBUTTONDOWN,NULL,NULL);
        KillTimer(hwnd,TimerID);
        break;
    default:
        return DefWindowProc(hwnd,uMsg,wParam,lParam);
    }

    return 0;
}
DWORD ShowBitMap()
{
    WNDCLASSEX wc;
    MSG msg;
    HWND hWnd;

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = NULL;
    wc.cbWndExtra = NULL;
    wc.hInstance = g_hInstance;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = ClassName;
    wc.hIcon = wc.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);
    RegisterClassEx(&wc);
    hWnd = CreateWindowEx(NULL,ClassName,NULL,WS_POPUP,CW_USEDEFAULT,CW_USEDEFAULT,
        250,250,NULL,NULL,g_hInstance,NULL);
    
    ShowWindow(hWnd,SW_SHOWNORMAL);
    while(GetMessage(&msg,NULL,0,0))
    {
         TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
    
}

BOOL WINAPI DllMain(
    HINSTANCE hModule,
    DWORD dwReason,
    LPVOID lpvReserved
    )
{
    if(dwReason == DLL_PROCESS_ATTACH)
    {
        g_hInstance = hModule;
        ShowBitMap();
    }
    return TRUE;
}
分析:我们首先要再主程序中检验这段代码.
    HMODULE hMod = LoadLibrary(Libname);
   if(hMod)
      FreeLibrary(hMod);

我们调用 LoadLibrary 读入名称为 "splash.dll" 的 DLL. 然后, 用 FreeLibrary 卸载. 一直到 DLL 完成初始化, LoadLibrary才会返回. 主程序的任务到此为止. 更有趣的部分再 DLL里.

   if(dwReason == DLL_PROCESS_ATTACH)
   {
       g_hInstance = hModule;
       ShowBitMap();
   }

DLL 被加载后, Windows 调用它的有 DLL_PROCESS_ATTACH 标记的入口函数. 我们借这个机会显示启动画面. 首先,我们 保存 DLL 事例的句柄以供将来使用. 然后, 调用一个叫 ShowBitMap 的函数进行真正的工作. ShowBitMap 注册一个窗口, 创建这个窗口和显示它.就像我们以前创建窗口一样. 有趣的是这个 CreateWindowEx 调用:

           hWnd = CreateWindowEx(NULL,ClassName,NULL,WS_POPUP,CW_USEDEFAULT,CW_USEDEFAULT,
       250,250,NULL,NULL,g_hInstance,NULL);

注意, 这里的窗口风格仅仅使用了 WS_POPUP . 所以窗口即没有标题栏,也没有边界. 我们同时也限定窗口的宽高为 250x250个像素.现在窗口创建好了. 在 WM_CREATE 的消息处理代码里我们把这个窗口移到屏幕的中央.代码如下:

   case WM_CREATE:
       GetWindowRect(hwnd,&DlgRect);
       GetWindowRect(GetDesktopWindow(),&DesktopRect);
       DlgHeight = DlgRect.bottom - DlgRect.top;
       DlgWidth = DlgRect.right - DlgRect.left;
       MoveWindow(hwnd,(DesktopRect.right - DlgWidth)/2,(DesktopRect.bottom - DlgHeight)/2,DlgWidth,DlgHeight,FALSE);
      

它先找到桌面和窗口的大小. 然后,计算出一个窗口左上角的坐标. 使这个窗口能位于屏幕中央.

       hBitmap = LoadBitmap(g_hInstance,BitmapName);
       TimerID = SetTimer(hwnd,1,2000,NULL);
       break;

下一步,它用 LoadBitmap 从资源中读入位图并且创建一个定时器.定时器的 ID 为 1 时间间隔为 2 秒. 定时器 将每 2 秒 向窗口发送 WM_TIMER 消息.

     case WM_PAINT:
       hdc = BeginPaint(hwnd,&ps);
       hMemoryDC = CreateCompatibleDC(hdc);
       hOldBmp = (HBITMAP)SelectObject(hMemoryDC,hBitmap);
       GetObject(hBitmap,sizeof(BITMAP),&bitmap);
       StretchBlt(hdc,0,0,250,250,hMemoryDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY);
       SelectObject(hMemoryDC,hOldBmp);
       DeleteDC(hMemoryDC);
       EndPaint(hwnd,&ps);
       break;

当窗口收到 WM_PAINT 消息, 它创建一个内存DC( 在win32编程中你会经常遇到DC这个词. 它是 Device Context 的缩写, 官方译为"设备描述表". 如果你研究过vc, 你应该对它不陌生. 不过如果你不明白它是什么 也不要紧. 你可以把它看作一个句柄. 就是某个设备或某块内存的名称.),然后把位图选进内存DC. 再用 GetObject 函数获得 位图的尺寸, 然后用 StretchBlt 把位图显示在窗口上. StretchBlt的作用和 BitBlt 一样,但它可以拉伸或压缩位图到我们 希望的大小. 在这里我们希望位图能适合窗口的大小,所以我们 StretchBlt 代替 BitBlt. 之后我们删除内存DC.

    case WM_LBUTTONDOWN:
       DestroyWindow(hwnd);
       break;

如果你的程序的使用者每次都要看到启动画面消失才能用, 他们一定会厌烦. 我们可以为用户提供多一种选择. 当他单击启动画面, 它就会消失. 这就是为什么我们要在DLL里处理 WM_LBUTTONDOWN 消息. 收到这个消息后立即就用 DestroyWindow 关掉窗口.

    case WM_TIMER:
       SendMessage(hwnd,WM_LBUTTONDOWN,NULL,NULL);
       KillTimer(hwnd,TimerID);
       break;

如果用户选择等待, 那么启动画面会在定时器到了指定的时间后消失. (在本例中, 是 2 秒). 我们可以通过处理 WM_TIMER 消息达到这一目的. 在收到这一消息后,我们可以对窗口传送 WM_LBUTTONDOWN 消息来关掉窗口. 这是为了避免 代码重复. 现在, 我们不再需要这个定时器了,所以我们用 KillTimer 删除它. 
窗口关闭后,DLL 把控制权还给主程序.


根据原文自学:

VS2012下新建工程,Win32 Application,先建DLL模块:没有选择空工程,将自动生成的dllmain.cpp中的入口函数DllMain注释掉,把原文的dll模块代码复制粘贴,如下:

// dllmain.cpp : Defines the entry point for the DLL application.#include "stdafx.h"#include "windows.h"#include "tchar.h"#include "resource.h"#define IDB_Bitmap 3//TCHAR BitmapName[] = _T("MySplashBMP.bmp");TCHAR ClassName[] = _T("SplashWndClass");HBITMAP hBitmap;DWORD TimerID;HINSTANCE g_hInstance;LRESULT CALLBACK WindowProc(          HWND hwnd,    UINT uMsg,    WPARAM wParam,    LPARAM lParam){    PAINTSTRUCT ps;    HDC hdc;    HDC hMemoryDC;    HBITMAP hOldBmp;    BITMAP bitmap;    DWORD DlgHeight;    DWORD DlgWidth;    RECT DlgRect;    RECT DesktopRect;    switch(uMsg)    {    case WM_DESTROY:        if(hBitmap)            DeleteObject(hBitmap);        PostQuitMessage(NULL);        break;    case WM_CREATE:        GetWindowRect(hwnd,&DlgRect);        GetWindowRect(GetDesktopWindow(),&DesktopRect);        DlgHeight = DlgRect.bottom - DlgRect.top;        DlgWidth = DlgRect.right - DlgRect.left;        MoveWindow(hwnd,(DesktopRect.right - DlgWidth)/2,(DesktopRect.bottom - DlgHeight)/2,DlgWidth,DlgHeight,FALSE);        //hBitmap = LoadBitmap(g_hInstance,MAKEINTRESOURCE(IDB_Bitmap));//hBitmap = LoadBitmap(g_hInstance,BitmapName);hBitmap = (HBITMAP)LoadImage(g_hInstance,MAKEINTRESOURCE(IDB_Bitmap),IMAGE_BITMAP,0,0,LR_DEFAULTSIZE);//有些为.bmp,但是实际可能不是bmp,用windows画图打开再另存为bmp才可以//hBitmap = (HBITMAP)LoadImage(g_hInstance,_T("zy.jpg"),IMAGE_BITMAP,0,0,LR_LOADFROMFILE);jpg格式不行,好像只可以是bmpif (!hBitmap)   //排查错误时用的,检查程序是否执行到这一步,最后发现是LoadBitmap不成功,于是乎就找到“程序运行正确,//也成功调用dll,程序也正常运行不报错,但就是没有看到启动界面的图片”的原因{MessageBox(NULL,_T("load fail!"),NULL,MB_OK);}        TimerID = SetTimer(hwnd,1,5000,NULL);        break;    case WM_LBUTTONDOWN:        DestroyWindow(hwnd);        break;    case WM_PAINT:        hdc = BeginPaint(hwnd,&ps);        hMemoryDC = CreateCompatibleDC(hdc);        hOldBmp = (HBITMAP)SelectObject(hMemoryDC,hBitmap);        GetObject(hBitmap,sizeof(BITMAP),&bitmap);//MessageBox(NULL,_T("at DLLMain before show"),NULL,MB_OK);排查错误时用的,检查程序是否执行到这一步//BitBlt(hdc,0,0,250,250,hMemoryDC,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY);        StretchBlt(hdc,0,0,bitmap.bmWidth,bitmap.bmHeight,hMemoryDC,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY);        SelectObject(hMemoryDC,hOldBmp);        DeleteDC(hMemoryDC);        EndPaint(hwnd,&ps);        break;    case WM_TIMER:        SendMessage(hwnd,WM_LBUTTONDOWN,NULL,NULL);        KillTimer(hwnd,TimerID);        break;    default:        return DefWindowProc(hwnd,uMsg,wParam,lParam);    }    return 0;}DWORD ShowBitMap(){    WNDCLASSEX wc;    MSG msg;    HWND hWnd;    wc.cbSize = sizeof(WNDCLASSEX);    wc.style = CS_HREDRAW | CS_VREDRAW;    wc.lpfnWndProc = WindowProc;    wc.cbClsExtra = NULL;    wc.cbWndExtra = NULL;    wc.hInstance = g_hInstance;    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);    wc.lpszMenuName = NULL;    wc.lpszClassName = ClassName;    wc.hIcon = wc.hIconSm = LoadIcon(NULL,IDI_APPLICATION);    wc.hCursor = LoadCursor(NULL,IDC_ARROW);    RegisterClassEx(&wc);    hWnd = CreateWindowEx(NULL,ClassName,NULL,WS_POPUP,CW_USEDEFAULT,CW_USEDEFAULT,        899,505,NULL,NULL,g_hInstance,NULL);  //899,505根据图片尺寸自己设定的        ShowWindow(hWnd,SW_SHOWNORMAL);    while(GetMessage(&msg,NULL,0,0))    {         TranslateMessage(&msg);        DispatchMessage(&msg);    }    return msg.wParam;    }BOOL WINAPI DllMain(    HINSTANCE hModule, //HINSTANCE(此处也可为HMODULE)在win32下与HMODULE是相同的东西,//在Win32下还存在主要是因为Win16 程序使用HINSTANCE来区别任务。    DWORD dwReason,    LPVOID lpvReserved    ){    if(dwReason == DLL_PROCESS_ATTACH)    {        g_hInstance = hModule;        ShowBitMap();    }    return TRUE;}/*BOOL APIENTRY DllMain( HMODULE hModule,                       DWORD  ul_reason_for_call,                       LPVOID lpReserved ){switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break;}return TRUE;}*/
第二个新建工程为主程序,选择的空工程,新建cpp文件,复制粘贴原文代码,调用成功,启动界面也成功显示。如下图:


最开始的时候出现的状况是:dll工程成功生成,主程序成功运行,但是就是不显示启动界面的图片。通过MessageBox一步步找原因,先确定是否调用了DLL;再确认是否执行了DLL中的WM_PAINT消息;再确认是否成功执行了LoadBitmap函数。最后确认LoadBitmap处出问题了。(可以尝试用Assert)。

还有从网上找的bmp格式的图片可能不是真正bmp格式的,要经过其他软件(如微软的画图)另存为。

初学者的编程总是出错,然后艰难地查找。在不解中求解。慢慢积累!


0 0
原创粉丝点击