启动画面让等待框移动

Splash Screen Make Wait box move

我有以下代码,应该在 window 的左下角绘制一个小方框,每 200 毫秒向右移动 50 个像素,然后在到达后重新出现在左侧右侧。

为什么我的小矩形不动?它一直画在同一个位置。

    case WM_PAINT:
    if (hBitmap != NULL)
    {
        // Paint the bitmap.            
        PAINTSTRUCT    ps;
        HDC            hdc;         
        HDC            hdcMem;
        HGDIOBJ        oldBitmap;
        //
        hdc = BeginPaint(hwnd, &ps);
        // Create a dc in memory to paint on.
        hdcMem = CreateCompatibleDC(hdc);
        // Select the bitmap.
        oldBitmap = SelectObject(hdcMem, hBitmap);          
        // Copy bitmap to splash screen window.
        BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);
        // Fill rectangle.
        HBRUSH hbr = CreateSolidBrush(RGB(42, 59, 87));
        SelectObject(hdc, hbr);            
        FillRect(hdc, &rc, hbr);
        // Cleanup.         
        SelectObject(hdcMem, oldBitmap);
        DeleteObject(hbr);
        DeleteObject(oldBitmap);
        DeleteDC(hdcMem);
        EndPaint(hwnd, &ps);            
    }
    break;
case WM_TIMER:
    timeCount++;
    addLeft += 50;
    if (addLeft == 300)
    {
        addLeft = 0;
    }
    // Move rectangle.
    rc.left += addLeft; 
    rc.right += addLeft;
    // Refresh the window.
    UpdateWindow(hwnd);



   // Timer and RECT from the top of the code page, and WinMain:
      UINT_PTR ptrTimer;
      const int TIMER_INTERVAL = 200;
      const int MAX_TIME_COUNT = 100;
      int timeCount;

   // the timer works, but here's the code anyway.
     ptrTimer = SetTimer(hwnd, 1, TIMER_INTERVAL, (TIMERPROC)NULL);

    RECT rc;
rc.left = 141;
rc.top = 232;
rc.right = rc.left + 15;
rc.bottom = rc.top + 15;

感谢您的任何回复,

马特

编辑:感谢 hf.enigma 的回复。这就是我在阅读您的 post 之前所做的事情。如果其他人想要这样做,这会起作用,但是还有几个句柄和 GDI 对象需要清除。我是 C++ 的新手,所以如果有人在这里看到内存泄漏,请告诉我。谢谢

case WM_PAINT:
    if (hBitmap != NULL)
    {
        // Paint the bitmap.            
        PAINTSTRUCT    ps;
        HDC            hdc;         
        HDC            hdcMem;
        HGDIOBJ        oldBitmap;
        //
        hdc = BeginPaint(hwnd, &ps);
        hdcMem = CreateCompatibleDC(hdc); // a device context (dc) in memory to paint on.
        oldBitmap = SelectObject(hdcMem, hBitmap);          
        // Copy bitmap to splash screen window.
        BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);
        EndPaint(hwnd, &ps);
        // Cleanup.         
        SelectObject(hdcMem, oldBitmap);
        DeleteObject(oldBitmap);
        DeleteDC(hdcMem);           
        HBRUSH hbr ;            
        // Fill rectangle.
        RECT f;
        GetClientRect(hwndBox, &f);
        hdc = BeginPaint(hwndBox, &ps);         
        hdcMem = CreateCompatibleDC(hdc);
        hbr = CreateSolidBrush(RGB(42, 59, 87));
        SelectObject(hdc, hbr);  
        FillRect(hdc, &f, hbr);
        EndPaint(hwnd, &ps);        
        // Cleanup.         
        SelectObject(hdcMem, oldBitmap);
        DeleteObject(oldBitmap);
        DeleteDC(hdcMem);
        DeleteObject(hbr);
    }




case WM_TIMER:      
    timeCount++;        
    if (addLeft == 60)
    {
        addLeft = 10000;
        ival = 2;
    }
    // 'Hide' the box for 2/3 of timer interval when
    // it reaches the right side.
    if (addLeft == 10000)
        {           
            addLeft = 0;
            ival = 1;
        }
    //      
    switch (ival)
    {       
    case 2:
        addLeft += 12;          
        break;
    case 3:
        ival = 0;
        break;
    }
    ival++;
    if (ival == 2)
    {           
        // Move rectangle.
        MoveWindow(hwndBox, rcleft + addLeft, rctop, 12, 12, true);
    }       
    if (timeCount == MAX_TIME_COUNT)
    {
        DestroyWindow(hwnd);
    }       
    break;

画完后要复制位图,用hdcMem画,像这样:

...

// Fill rectangle.
HBRUSH hbr = CreateSolidBrush(RGB(42, 59, 87));
SelectObject(hdcMem, hbr);            
FillRect(hdcMem, &rc, hbr);

// Copy bitmap to splash screen window.
BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);

// Cleanup.
....

这可以像 hf.enigma 所说的那样绘制,但必须为每个绘制事件调用两次 FillRect()。没有 'erasing' 前一个块,window 最终在底部有一个长矩形,类似于进度条。

我最终制作了一个小于 window 我想要的块的大小,并调用了 MoveWindow() 函数。对我来说,这是无需使用 MFC 或 CLI 即可完成此操作的最简单且最易于维护的方法。

如果这可以帮助其他人,这是我为此使用的所有代码。

//This displays a splash screen, and starts
//another application that takes a long time
//to load.

#pragma once
#include "resource.h"
#include "PathStatus.h"
#include "StartApp.h"
#include "CenterWindow.h"
#include <string>
#include <stdlib.h>
#include <iostream>
using namespace std;
const LPWSTR CLASS_NAME(L"TMS_Logs");
const LPWSTR PATH_SUFFIX (L"\bin\tl.exe");
const int LEN_APP_NAME = 12; // length of "TMS_Logs.exe".
bool b;
int ival;
char *appPath;
HWND hwnd; // Main window.
HBITMAP hBitmap = NULL; // Bitmap painted in window.
HWND hwndBox; // Moving box shows app. is loading.
HWND hwndButton; // Close box.
int bmWidth;
int bmHeight;
UINT_PTR ptrTimer;
const int TIMER_INTERVAL = 50;
const int MAX_TIME_COUNT = 650;
int timeCount;
int addLeft;
int rcleft, rctop, rcright, rcbottom;
RECT rcMover;
RECT rcMover2;
bool exitApp = false;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{   
rcleft = 157;
rctop = 220;
rcright = rcleft + 15;
rcbottom = rctop + 15;
//
rcMover.left = rcleft;
rcMover.bottom = rcbottom;
rcMover.top = rctop;
rcMover.right = rcright;
//
rcMover2.bottom = rcbottom;
rcMover2.top = rctop;
rcMover.left + rcleft + 50;
rcMover.right = rcleft + 50 + 15;

// Get the commandline.
const wchar_t *args = GetCommandLineW();    
wstring s(args);       
// With no external arguments, GetCommandLineW() returns the
// full path with quotes around it, plus one space after the
// end qoute, e.g., '"c:\fullpath.exe" '.
// Remove the starting and ending quote chars, the space at
// the end, and and the exe path.   
// 
int len = s.length();
s = s.substr(1, len - 3 - LEN_APP_NAME) + PATH_SUFFIX;  
// Convert the wide string to a regular char array. 
// Convert the wstring to c-string.
const wchar_t *path = s.c_str();
// Convert the WCHAR to char* and store it in the 
// 'appPath' variable.
const size_t BUFFER_SIZE = 200;
size_t i;
appPath = (char*)malloc(BUFFER_SIZE);
wcstombs_s(&i, appPath, BUFFER_SIZE, path, BUFFER_SIZE);
// Create window class objects.
WNDCLASSEX wc;
MSG msg;    
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc; // Sets callback function.
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDICN_WSDOT));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = CLASS_NAME; 
wc.hIconSm = wc.hIcon;
if (!RegisterClassEx(&wc))
{
    MessageBox(NULL, L"Window Registration Failed!", L"Error!",
        MB_ICONEXCLAMATION | MB_OK);        
    return -1;
}

// Load the resource bitmap.
hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDBMP_TL7));
// Make a bitmap to hold the returned width and height.
// This bitmap will be deleted when DeleteObject is called
// for the hBitmap handle.
BITMAP bm;  
if (hBitmap != NULL && GetObject(hBitmap, sizeof(bm), &bm))
{   
    bmWidth = bm.bmWidth;
    bmHeight = bm.bmHeight;     
}
else
{
    MessageBox(hwnd, L"Error loading splash screen bitmap.",
        L"Error", MB_ICONEXCLAMATION | MB_OK);
    return -1;
}
// Center the window on the monitor
// where the mouse is.
RECT rc;
CenterRectToMonitor(&rc, bmWidth, bmHeight);
//
hwnd = CreateWindowEx(WS_EX_PALETTEWINDOW, wc.lpszClassName, // Uses the properties set in Window Class wc.
    L"TMS Logs", WS_POPUP, rc.left, rc.top, bmWidth, bmHeight, NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
    MessageBox(NULL, L"Error creating Window.", L"Error",
        MB_ICONEXCLAMATION | MB_OK);
    return -1;
}   
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
//bool started = true;
bool started = false;
bool ret;
int x, w, y, h;
x = 369;
y = 10;
w = 19;
h = 19;
// Make close box in upper-right corner.
hwndButton = CreateWindow( 
    L"BUTTON",  // Predefined class.
    L"X",      // Button text 
    WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,  // Styles 
    369,         // x position 
    10,          // y position 
    19,        // Button width
    19,        // Button height
    hwnd,     // Parent window
    NULL,       // No menu.
    (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), 
    NULL);      // Pointer not needed.

// Make a box that moves along the bottom of 
// the window while the other app. loads.       
x = rcleft;
y = rctop;
w = 10;
h = 10; 
hwndBox = CreateWindow(L"static", 0, WS_CHILD | WS_VISIBLE, x, y, w, h, hwnd, NULL,
    (HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE), NULL); 
// Create a timer to close this window after
// several seconds, in case the main app. does
// not send the close window message to this app.
// the WM_TIMER message is handled in the WndProc
// callback function.
ptrTimer = SetTimer(hwnd, 1, TIMER_INTERVAL, (TIMERPROC)NULL);
// Start message Loop.
while ((ret = GetMessage(&msg, NULL, 0, 0)) !=  0)
{
    if (ret == -1)
    {                  
        MessageBox(NULL,
            L"Error in Splash screen window message loop, at:\n\n'GetMessage(&msg, NULL, 0, 0))'.\n\nMessage return value was -1.",
            L"TMS Logs", MB_ICONEXCLAMATION | MB_OK);
        // Exit this app.
        return -1;
    }
    // TranslateMessage(&Msg); // Not needed with what I have here.
    DispatchMessage(&msg);
    // Check app. path and start the other app.     
    if (!started)
    {           
        started = true;
        if (Path_Accessible(appPath))
        {
            // Start the main app.
            if (!StartApplication(appPath))
            {
                // The folder or file is missing, or
                // the user does not have security
                // permissions to the sub-folder.
                MessageBox(hwnd, L"Unexpected error at: StartApplication().",
                    L"Error", MB_ICONEXCLAMATION | MB_OK);
                if (appPath)
                {
                    free(appPath);
                }
                return -1;
            }
        }
        else
        {               
            // Display an error message.                
            char msg[350];
            char* prefix = "Error: Can't find 'tl.exe'.\n\n It's missing from: '";
            //char* suffix = " '\ncannot be accessed, or does not exist.";
            int count = sizeof(msg);
            strncpy(msg, prefix, count);
            strncat(msg, appPath, count);
            //strncat(msg, suffix, count);              
            // Convert c-string to wide char. string.
            count = strlen(msg) + 1;
            wchar_t* wMsg = new wchar_t[count];
            size_t returnCount;
            int ret = mbstowcs_s(&returnCount, wMsg, count, msg, _TRUNCATE);
            if (ret == 0 && returnCount == count)
            {
                MessageBox(hwnd, wMsg, L"Error", MB_ICONEXCLAMATION | MB_OK);               
            }
            else
            {
                // Error
                MessageBox(hwnd, L"Error: The application path cannot be accessd, or does not exist.", L"Error", MB_ICONEXCLAMATION | MB_OK);
            }
            delete[] wMsg;
            if (appPath)
            {
                free(appPath);
            }
            return -1;
        }
    }
}
if (appPath)
{
    free(appPath);      
}       
return msg.wParam;
}

// Process messages.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
    // The close button is the only child window,
    // so no need to check wparam.
    DestroyWindow(hwnd);
    break;
case WM_CLOSE:
    DestroyWindow(hwnd);
    break;
case WM_DESTROY:                
    DestroyWindow(hwndButton);
    DestroyWindow(hwndBox);
    if (hBitmap != NULL)
    {
        if (!DeleteObject(hBitmap))
        {
            MessageBox(hwnd, L"Error at: WndProc(). Failed to delete the application bitmap.",
                L"Error", MB_OK);
        }
    }
    if (ptrTimer)
    {   
        if (!KillTimer(hwnd, ptrTimer))
        {
            MessageBox(hwnd, L"Error at :WndProc(). Failed to free the application timer.",
                L"Error", MB_OK);
        }
    }
    PostQuitMessage(0);
    break;
case WM_PAINT:
    if (hBitmap != NULL)
    {
        // Paint the bitmap.            
        PAINTSTRUCT    ps;
        HDC            hdc;         
        HDC            hdcMem;
        HGDIOBJ        oldBitmap;
        //
        hdc = BeginPaint(hwnd, &ps);
        hdcMem = CreateCompatibleDC(hdc); // a device context (dc) in memory to paint on.
        oldBitmap = SelectObject(hdcMem, hBitmap);          
        // Copy bitmap to splash screen window.
        BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);
        EndPaint(hwnd, &ps);
        // Cleanup.         
        SelectObject(hdcMem, oldBitmap);
        DeleteObject(oldBitmap);
        DeleteDC(hdcMem);           
        HBRUSH hbr ;            
        // Fill rectangle.
        RECT f;
        GetClientRect(hwndBox, &f);
        hdc = BeginPaint(hwndBox, &ps);         
        hdcMem = CreateCompatibleDC(hdc);
        hbr = CreateSolidBrush(RGB(42, 59, 87));
        SelectObject(hdc, hbr);  
        FillRect(hdc, &f, hbr);
        EndPaint(hwnd, &ps);        
        // Cleanup.         
        SelectObject(hdcMem, oldBitmap);
        DeleteObject(oldBitmap);
        DeleteDC(hdcMem);
        DeleteObject(hbr);
    }
    break;
case WM_TIMER:      
    timeCount++;        
    if (addLeft == 60)
    {
        addLeft = 10000;
        ival = 2;
    }
    // 'Hide' the box for 2/3 of timer interval when
    // it reaches the right side.
    if (addLeft == 10000)
        {           
            addLeft = 0;
            ival = 1;
        }
    //      
    switch (ival)
    {       
    case 2:
        addLeft += 12;          
        break;
    case 3:
        ival = 0;
        break;
    }
    ival++;
    if (ival == 2)
    {           
        // Move rectangle.
        MoveWindow(hwndBox, rcleft + addLeft, rctop, 12, 12, true);
    }       
    if (timeCount == MAX_TIME_COUNT)
    {
        DestroyWindow(hwnd);
    }       
    break;
default:
    return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;

}

这里是引用H文件的代码。

StartApp.H

#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude DDE, RPC, Shell, Sockets, etc.
#define NOCOMM // Exclude serial communication APIs.
#include <Windows.h>
#include <cstdlib>

// Calls the CreateProcess function to start another app.
// ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682512(v=vs.85).aspx
bool StartApplication(const char* appPath)
{   
// The CreateProcess function requires
// wide char. array, so convert appPath.
size_t count = strlen(appPath) + 1;
wchar_t* path = new wchar_t[count];
size_t returnCount;
int ret = mbstowcs_s(&returnCount, path, count, appPath, _TRUNCATE);
if (ret != 0 || returnCount != count)
{
    // Error converting C-string.
    delete[] path;
    return false;
}
// Required objects.
STARTUPINFO si;
PROCESS_INFORMATION pi;
// Set the size of the objects.
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the program at 'path'.
bool ok = CreateProcess(
    path,        // module name/app. path.
    NULL,        // Command line/path, if null app. path is used.
    NULL,        // Process handle, use null.
    NULL,        // Thread handle, use null.
    FALSE,       // Set handle inheritance to FALSE.
    0,           // No creation flags.
    NULL,        // Use parent's environment block.
    NULL,        // Use parent's starting directory. 
    &si,         // Pointer to STARTUPINFO structure.
    &pi);        // Pointer to PROCESS_INFORMATION structure.
// Close process and thread handles. 
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
delete[] path;
return ok;
}

CenterWindow.H

void CenterRectToMonitor(LPRECT prc, int rcWidth, int rcHeight)
{
POINT pt;   
GetCursorPos(&pt);
HMONITOR mon;
mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(mon, &mi);
*prc = mi.rcMonitor;
//
// Center the window rectangle to the monitor rectangle.
prc->left   = prc->left + (prc->right  - prc->left - rcWidth) / 2;
prc->top    = prc->top  + (prc->bottom - prc->top  - rcHeight) / 2;
prc->right  = prc->left + rcWidth;
prc->bottom = prc->top  + rcHeight;     
}

PathStatus.H

#include <sys/stat.h>
const int SUCCESS = 0; // Indicates stat buffer was successfully set.

// Method to check if a file or folder
// exists and is accessible, using the stat
// structure and stat function, ref: 
// http://pubs.opengroup.org/onlinepubs/009695399/functions/stat.html
bool Path_Accessible(const char* path)
{
if (path == 0)
{
    return false;
}

struct stat path_status;    
int result = stat(path, &path_status);
// 
// The _S_IFREG flag indicates the path is a
// regular file and not a directory.
//
// To check for a directory, use '_S_IFDIR'.    
return result == SUCCESS &&
    (path_status.st_mode & _S_IFREG);
}

这是我的启动画面的样子。请注意,小框从现在位置的左侧开始,向右移动一点,然后重复直到另一个应用程序。这个开始向 window.

发送 WM_CLOSE 消息