GacUI源码简析(一)

4 篇文章 0 订阅
3 篇文章 0 订阅

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 以及 GodWindowWindowClass
  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 又被微软废了。。。);
  • 开始 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;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了起来。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CANopen协议是一种应用于控制系统中的开放式网络通讯协议,CANopen协议栈是一种实现CANopen协议的软件库,而CanFestival是其中的一种开源实现。CanFestival协议栈提供了一套用于实现CANopen从站设备的软件工具,用户可以利用CanFestival来开发符合CANopen协议的从站设备。 CanFestival协议栈的实现主要由以下几个步骤构成:首先,用户需要配置CanFestival协议栈,包括设置从站设备的节点ID、通信速率等参数;然后,用户需要定义从站设备的对象字典,包括输入对象、输出对象、PDO对象等;接着,用户需要编写应用程序,根据需求实现从站设备的控制逻辑;最后,用户需要编译链接代码,并将生成的可执行文件下载到从站设备中运行。 CanFestival协议栈的源码主要包括以下几个模块:CO_Data模块负责管理CANopen通信过程中的数据;对于CAN总线的操作交由CO_CAN模块处理;NMT模块实现CANopen网络管理功能,负责从站设备的初始化和启动过程;SDO模块用于实现从站设备上的服务数据对象传输;PDO模块处理实时数据的传输;EMCY模块处理紧急事件报告;SYNC模块管理同步传输数据;TIME模块提供时间基准。 值得注意的是,CanFestival协议栈的实现需要根据具体的硬件平台和应用场景进行调整和修改,用户在使用时需要充分理解CANopen协议的原理和特点,结合具体需求进行定制和优化。 CanFestival协议栈的开源性质意味着用户可以根据需要对其进行二次开发和定制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值