日期:2014-05-16  浏览次数:20805 次

你的第一Windows程序——管理应用程序状态

MSDN原文(英文)

管理应用程序状态

一个窗口过程仅仅是一个为每个消息获取调用函数,所以它本质上是无状态的。因此,你需要一个方法来跟踪你的应用程序从一个函数调用下一个函数的状态。

最简单的方法是把一切都放在全局变量中。这对于小程序已经足够了,并且许多SDK示例都使用这种方式。然而在一个大型程序,它会导致全局变量的扩散。此外,你可能有几个窗口,每个都有其自己的窗口过程,跟踪哪个窗口应该访问哪些变量变得混乱和易出错。

CreateWindowEx函数提供了一种方法可以将任何数据结构传递给一个窗口,当这个函数被调用,以下两个消息发送到你。
  • WM_NCCREATE
  • WM_CREATE
这些消息按列出的顺序发送(在CreateWindowEx期间这发送的不止这两个消息,但我们在这个讨论里可以忽略)。

WM_NCCREATE和WM_CREATE消息在窗口可见前发送,这是制定和初始化你的UI的好地方——例如,确定窗口的初始布局。

函数CreateWindowEx的最后一个参数是void*类型的指针,在这个参数你可以传递任何你想要的指针值,当窗口过程处理WM_NCCREATE和WM_CREATE消息,它可以从消息数据中提取这个值。

让我们看看如何使用这个参数将应用程序数据传递到你窗口。首先定义一个类或结构保存状态信息:
// 定义一个结构保存一些状态信息

struct StateInfo {

    // ... (结构成员没有显示)

};

当你调用CreateWindowsEx函数,将结构指针传递给最后的void*参数。
StateInfo *pState = new (std::nothrow) StateInfo;
    
    if (pState == NULL)
    {
        return 0;
    }

    // 在这里初始化结构成员 (示例没有显示).

    HWND hwnd = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class
        L"Learn to Program Windows",    // Window text
        WS_OVERLAPPEDWINDOW,            // Window style

        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

        NULL,       // Parent window    
        NULL,       // Menu
        hInstance,  // Instance handle
        pState      // 额外的数据信息
        );

当你收到WM_NCCREATE和WM_CREATE消息,每个消息的 lParam的参数指向CREATESTRUCT结构的指针。而后,CREATESTRUCT结构包含的指针传入CreateWindowEx。
图示CREATESTRUCT结构布局
 
以下是如何提取数据结构的指针,首先,通过计算lParam参数获得CREATESTRUCT结构。
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);

CREATESTRUCT
结构 lpCreateParams 成员CreateWindowEx 指定原始 void 指针。计算lpCreateParams 获取数据结构的指针。
pState = reinterpret_cast<StateInfo*>(pCreate->lpCreateParams);

下一步,调用 SetWindowLoingPtr函数并把指针传入你的数据结构。
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pState);

最后一个函数的调用的目的是将StateInfo指针储存在窗口的实例数据中。一旦你这样做,你总是可以从窗口调用GetWindowLongPtr得到指针。
LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
    StateInfo *pState = reinterpret_cast<StateInfo*>(ptr);

每个窗口都有自己的实例数据,所以你可以创建多个窗口,每个窗口都有自己的数据结构的实例。这种方法是特别有用的,如果你定义一个窗口类和创建该类的多个窗口。比如你创建了一个自定义控件类。GetWindowsLongPtr调用可以很方便的封装在一个小的辅助函数。
inline StateInfo* GetAppState(HWND hwnd)
{
    LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
    StateInfo *pState = reinterpret_cast<StateInfo*>(ptr);
    return pState;
}

现在你可以写你的窗口过程如下: