GacUI源码简析(一)

  • Post author:
  • Post category:其他



GacUI源码简析(一)

本文中介绍的 GacUI 源代码来自于 https://github.com/vczh-libraries/GacUI。

在 GacUI 中,`WinMain` 开始后,第一个执行的函数为 `SetupWindowsDirect2DRenderer` :

int SetupWindowsDirect2DRenderer()
{
    CoInitializeEx(NULL, COINIT_MULTITHREADED);
    HINSTANCE hInstance=(HINSTANCE)GetModuleHandle(NULL);
    WinDirect2DApplicationDirect2DObjectProvider objectProvider;
    SetWindowsDirect2DObjectProvider(&objectProvider);
    return WinMainDirect2D(hInstance, &RendererMainDirect2D);
}

可以通过 `WinDirect2DApplicationDirect2DObjectProvider` 获取到许多 factory(如 D2D1Factory,DWriteFactory etc.)但是注意的是,这些资源并不存储在 `WinDirect2DApplicationDirect2DObjectProvider` 中。接着将目光转到 `WinMainDirect2D`:

int WinMainDirect2D(HINSTANCE hInstance, void(*RendererMain)())
{
    EnableCrossKernelCrashing();
    // create controller
    INativeController* controller=CreateWindowsNativeController(hInstance);
    SetCurrentController(controller);
    {
        // install listener
        Direct2DWindowsNativeControllerListener listener;
        controller->CallbackService()->InstallListener(&listener);
        direct2DListener=&listener;
        // main
        RendererMain();
        // uninstall listener
        direct2DListener=0;
        controller->CallbackService()->UninstallListener(&listener);
    }
    // destroy controller
    DestroyWindowsNativeController(controller);
    return 0;
}

可以注意到,首先它调用了`CreateWindowsNativeController` 得到了一个 Controller,这个 Controller 真可谓是 GacUI 的核心!但是先不急,我们先来看看 `SetCurrentController` 干了什么:

INativeController* currentController=0;

INativeController* GetCurrentController()
{
    return currentController;
}

void SetCurrentController(INativeController* controller)
{
    currentController=controller;
}

可以看出,在 GacUI 中有一个全局变量 `currentController` 。现在看看这个 Controller 到底是何方神圣:

class WindowsController : public Object, public virtual INativeController, public virtual INativeWindowService
{
protected:
            WinClass                            windowClass;
            WinClass                            godClass;
            HINSTANCE                           hInstance;
            HWND                                godWindow;
            Dictionary<HWND, WindowsForm*>      windows;
            INativeWindow*                      mainWindow;
            HWND                                mainWindowHandle;

            WindowsCallbackService              callbackService;
            WindowsResourceService              resourceService;
            WindowsAsyncService                 asyncService;
            WindowsClipboardService             clipboardService;
            WindowsImageService                 imageService;
            WindowsScreenService                screenService;
            WindowsInputService                 inputService;
            WindowsDialogService                dialogService;

可以看出:

  1. 它掌管了窗口的

    WinClass

    以及

    GodWindow



    WindowClass

  2. 它还掌管了众多

    Service

    ,例如

    InputService

    ,处理输入;

    ClipBoardService

    ,处理剪贴板;
  3. 维护了一个

    Dictionary<HWND,WinForm*>

    ,以便在处理

    WndProc

    时根据

    hWnd

    迅速找出对应的

    WinForm

`WinClass`是对 `WNDCLASSEX` 的一个包装,但是要重点注意这里:

WindowsController(HINSTANCE _hInstance)
                    :hInstance(_hInstance)
                    ,windowClass(L"VczhWindow", false, false, WndProc, _hInstance)
                    ,godClass(L"GodWindow", false, false, GodProc, _hInstance)
                    ,...

可以看到,`VczhWindow` 的窗口过程注册为 `WndProc`:

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
            WindowsController* controller=dynamic_cast<WindowsController*>(GetCurrentController());
            if(controller)
            {
                LRESULT result=0;
                if(controller->HandleMessage(hwnd, uMsg, wParam, lParam, result))
                {
                    return result;
                }
            }
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

也就是说,当消息来到时,先获取上文提到的,全局存储的 Controller,再将真正的消息处理转交给 Controller。而在 Controller 中就可以看到,最后将消息转发给了 `WinForm`:

    WindowsForm* window=windows.Values().Get(index);
    skipDefaultProcedure=window->HandleMessage(hwnd, uMsg, wParam, lParam, result);

`WinForm` 拿到消息后,会转发给 Listener:

case WM_MBUTTONDBLCLK:
{
            NativeWindowMouseInfo info=ConvertMouse(wParam, lParam, false, nonClient);
            for(vint i=0;i<listeners.Count();i++)
            {
                listeners[i]->MiddleButtonDoubleClick(info);
            }
}
break;

消息就这样转发出去了!



Direct2DWindowsNativeControllerListener

则存储了

Direct2D、3D

需要的一些 Factory:

class Direct2DWindowsNativeControllerListener : public Object, public INativeControllerListener
{
public:
        Dictionary<INativeWindow*, Ptr<Direct2DWindowsNativeWindowListener>>        nativeWindowListeners;
        ComPtr<ID2D1Factory>                                                        d2dFactory;
        ComPtr<IDWriteFactory>                                                      dwrite;
        ComPtr<ID3D11Device>                                                        d3d11Device;
        ...

至于`direct2DListener`,不言自明,又是全局变量(这里插一句:可能 Javaer 又要批判没有使用所谓的 Singleton 了吧:)

接着,会调用 `renderMain()`,可以看到,`RendererMainDirect2D` 又调用了 `GuiApplicationMain`,`GuiApplicationMain` 本身又调用了 `GuiApplicationInitialize`,似乎终于要到了跑起来的时候了!

void GuiApplicationInitialize()
            {
                Ptr<theme::ITheme> theme;
                {
                    WString osVersion=GetCurrentController()->GetOSVersion();
                    vint index=osVersion.IndexOf(L';');
                    if (index == -1)
                    {
                        theme=new win8::Win8Theme;
                    }
                    else
                    {
                        WString osMainVersion=osVersion.Sub(0, index);
                        if(osMainVersion==L"Windows 8" || osMainVersion==L"Windows Server 2012")
                        {
                            theme=new win8::Win8Theme;
                        }
                        else
                        {
                            theme=new win7::Win7Theme;
                        }
                    }
                }

                GetCurrentController()->InputService()->StartTimer();
                GuiApplication app;
                application=&app;

                GetPluginManager()->Load();
                GetGlobalTypeManager()->Load();
                theme::SetCurrentTheme(theme.Obj());
                GuiMain();
                theme::SetCurrentTheme(0);
                DestroyPluginManager();
                DestroyGlobalTypeManager();
                ThreadLocalStorage::DisposeStorages();
            }
        }
    }
}
  • 首先,根据系统版本选择主题(目前 GacUI 还没有兼容 Windows 10,因为

    VerifyVersionInfo

    又被微软废了。。。);
  • 开始

    Timer



    Timer



    InputService

    负责:

void WindowsInputService::StartTimer()
            {
                if(!IsTimerEnabled())
                {
                    SetTimer(ownerHandle, 1, 16, NULL);
                    isTimerEnabled=true;
                }
            }

            void WindowsInputService::StopTimer()
            {
                if(IsTimerEnabled())
                {
                    KillTimer(ownerHandle, 1);
                    isTimerEnabled=false;
                }
            }



Timer

消息的处理又由

CallbackService

负责:

void WindowsCallbackService::InvokeGlobalTimer()
{
        for(vint i=0;i<listeners.Count();i++)
        {
                listeners[i]->GlobalTimer();
        }
}

顺便说一句,GacUI 的渲染不是在

WM_PAINT

中完成的,是使用这个

Timer

,每 16ms 触发一次。

  • 老伎俩,定义一个

    GuiApplication

    ,并把全局的

    application

    设置好;
  • 执行

    GuiMain

    。通常在

    GuiMain

    中,会出现:
GetApplication()->Run(&window);

这个

Run

十分直白:

void GuiApplication::Run(GuiWindow* _mainWindow)
{
            if(!mainWindow)
            {
                mainWindow=_mainWindow;
                GetCurrentController()->WindowService()->Run(mainWindow->GetNativeWindow());
                mainWindow=0;
            }
}

接着会调用 Controller 的

Run


void Run(INativeWindow* window)
{
        mainWindow=window;
        mainWindowHandle=GetWindowsForm(window)->GetWindowHandle();
        mainWindow->Show();
        MSG message;
        while(GetMessage(&message, NULL, 0, 0))
        {
            TranslateMessage(&message);
            DispatchMessage(&message);
            asyncService.ExecuteAsyncTasks();
        }
}

呼,程序终于

Run

了起来。



版权声明:本文为LYP951018原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。