第六章 Multi-SDI

 

Multi-SDI

 

 

IE 浏览器可以同时打开多个窗口 , 这些打开的窗口是同一个应用程序管理的多个顶层 SDI 窗口 , 每一窗口对应一个线程 , 而不是每个窗口对应一个单独的应用程序 . 利用 WTL Multi-SDI 的支持你也可以使用这个特性 . Multi-SDI 应用程序的设计原理非常简单 :

·         每个顶层窗口 ( 及其子窗口 ) 运行一个单线程 .  

·         每个单线程都是一个 UI 线程 , 有自己独立的消息循环 , 负责处理其所属窗口的消息分发和消息映射 . 这意味着 , 所有线程在进程级别共享资源 , 并且当某个线程过于繁忙时 , 其它线程不受任何影响 , 仍然能快速响应用户请求 .

·         因此 , 每个单线程的顶层窗口对于用户来说 , 就像是一个独立的应用程序 .

创建 Multi-SDI 应用程序的关键是设计一个线程管理类,负责 :

1)      为每个请求的线程创建一个 SDI 框架窗口 .

2)      管理所有线程句柄及活动线程数 .

3)      随时掌握命令行参数以及窗口初始尺寸

WTL 并不提供任何基本的辅助类来支持 Multi-SDI 应用程序 . 然而 , WTL 应用程序向导 WTLAppWizard( 文章末尾详细讨论 ) 可以为 Multi-SDI 应用程序自动产生代码 . WTL 应用程序向导产生的线程管理类如下 :

 

class CThreadManager {

public:

// thread init param

    struct _RunData

    {

        LPTSTR lpstrCmdLine;

        int nCmdShow;

    };

 

DWORD m_dwCount;     //count of threads

    HANDLE m_arrThreadHandles[MAXIMUM_WAIT_OBJECTS - 1];

 

    CThreadManager() : m_dwCount(0) {}

    DWORD AddThread(LPTSTR lpstrCmdLine, int nCmdShow);

    void RemoveThread(DWORD dwIndex);

    int Run(LPTSTR lpstrCmdLine, int nCmdShow);

};

 

int WINAPI WinMain(…) {

    hRes = _Module.Init(NULL, hInstance);

 

    CThreadManager mgr;

    int nRet = mgr.Run(lpstrCmdLine, nCmdShow);

 

    _Module.Term();

    return nRet;

}

 

CThreadManager::Run 成员函数创建一个新的 UI 线程 , 然后调用 MsgWaitForMultipleObjects 无限等待 , MsgWaitForMultipleObjects 函数 的特点是它不但可以等待内核对象 , 还可以等消息 . 也就是当有消息到来时 , 该函数也一样可以返回 , 并处理消息 . 如果有线程终结了 , 它就递减线程数 , 然后返回继续等待 . 当所有线程完成后 , Run() 函数返回 , 应用程序结束 . 另一方面 , 如果 MsgWaitForMultipleObjects 确定收到了 WM_USER 消息 (WTL 的线程管理类用此消息来请求新的 UI 线程 ), 则线程管理类创建一个新 UI 线程 .

int CThreadManager::Run(LPTSTR lpstrCmdLine, int nCmdShow) {

    MSG msg;

    // force message queue to be created

    ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);

 

    AddThread(lpstrCmdLine, nCmdShow);

 

    int nRet = m_dwCount;

    DWORD dwRet;

    while(m_dwCount > 0)

    {

        dwRet = ::MsgWaitForMultipleObjects(m_dwCount,

                  m_arrThreadHandles, FALSE, INFINITE, QS_ALLINPUT);

 

        if(dwRet == 0xFFFFFFFF)

{

             ::MessageBox(NULL, _T("ERROR: Wait for multiple 

                  objects failed!!!"), _T("MultiSDI_HTMLView"), MB_OK);

}

        else if(dwRet >= WAIT_OBJECT_0 && dwRet <= (WAIT_OBJECT_0 +

                 m_dwCount - 1))

{

             RemoveThread(dwRet - WAIT_OBJECT_0);

}

        else if(dwRet == (WAIT_OBJECT_0 + m_dwCount))

        {

            ::GetMessage(&msg, NULL, 0, 0);

            if(msg.message == WM_USER)

                AddThread("", SW_SHOWNORMAL);

            else

                ::MessageBeep((UINT)-1);

        }

        else

            ::MessageBeep((UINT)-1);

    }

    return nRet;

}

 

注意 , WTL 应用程序向导生成的代码中使用的是 WM_USER 消息而不必是特殊的用户自定义消息 . 这是因为代码中仅仅是主线程用此消息来创建新 UI 线程 , 并且新建的 UI 线程自己并不创建任何窗口 , 所以通常情况下不会引起自定义消息的冲突 .

 

AddThread() 例程中把线程过程函数 (thread procedure) 地址传递给 Win32 API CreateThread() 来创建线程 . 线程管理类的静态方法 RunThread() 中创建了 SDI 框架窗口 , 并调用 Win32 API ShowWindow() 显示 , 然后启动消息循环 .

 

// CThreadManager::RunThread

static DWORD WINAPI RunThread(LPVOID lpData)

{

    CMessageLoop theLoop;

    _Module.AddMessageLoop(&theLoop);

 

    _RunData* pData = (_RunData*)lpData;

 

    CMainFrame wndFrame;

    if(wndFrame.CreateEx() == NULL)

    {

        ATLTRACE(_T("Frame window creation failed!/n"));

        return 0;

    }

 

    wndFrame.ShowWindow(pData->nCmdShow);

    ::SetForegroundWindow(wndFrame);    // Win95 needs this

    delete pData;

 

    int nRet = theLoop.Run();

    _Module.RemoveMessageLoop();

    return nRet;

}

 

Multi-SDI 应用程序框架窗口菜单项 File/New 的消息映射函数中 , 需要投递一个 WM_USER 消息到主线程 , 以便通知线程管理类实例创建一个新的拥有框架窗口的 UI 线程 .

 

 

LRESULT CMainFrame::OnFileNewWindow(WORD, WORD, HWND, BOOL&) {

    ::PostThreadMessage(_Module.m_dwMainThreadID, WM_USER, 0, 0L);

    return 0;

}

 

Figure 8a: Multi-SDI Application

multi-sdi-1

 

 

 

Figure 8b: Multi-SDI Application

 

multi-sdi-2

Multi-SDI 应用程序最适合于总是以单应用程序实例运行 . 也就是当用户双击 YourMultiSdi.exe , 如果已经有此程序的实例在运行 , 那么启动的应该是一个新线程 , 而不是又启动一个新的应用程序实例 . WTL Multi-SDI 并不提供此功能 . 不过使用 DDE 很容易实现 , 可以参考本文章附带的例子SingleInstance.

 

Creating MDI Apps

To be continued···

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值