本文中介绍的 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;
可以看出:
WinClass
以及 GodWindow
的 WindowClass
;Service
,例如 InputService
,处理输入;ClipBoardService
,处理剪贴板;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();
}
}
}
}
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
了起来。