GacUI源码简析(一)

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      windows;
            INativeWindow*                      mainWindow;
            HWND                                mainWindowHandle;

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

可以看出:

  1. 它掌管了窗口的 WinClass 以及 GodWindowWindowClass
  2. 它还掌管了众多 Service,例如 InputService,处理输入;ClipBoardService,处理剪贴板;
  3. 维护了一个 Dictionary,以便在处理 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(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;iMiddleButtonDoubleClick(info);
            }
}
break;

消息就这样转发出去了!

Direct2DWindowsNativeControllerListener 则存储了 Direct2D、3D 需要的一些 Factory:
class Direct2DWindowsNativeControllerListener : public Object, public INativeControllerListener
{
public:
        Dictionary>        nativeWindowListeners;
        ComPtr                                                        d2dFactory;
        ComPtr                                                      dwrite;
        ComPtr                                                        d3d11Device;
        ...

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

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

void GuiApplicationInitialize()
            {
                Ptr 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 又被微软废了。。。);
  • 开始 TimerTimerInputService 负责:

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;iGlobalTimer();
        }
}

顺便说一句,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了起来。

你可能感兴趣的:(C++,Direct2D,Win32)