win10 桌面 移动

前沿拓展:

win10 桌面 移动

Windows 10的多桌面设计让用户更加方便的对任务进行分类管理,使得工作更加井然有序。但是很多用户可能还不熟悉Windows 10的这个多桌面设计,如果想在在不同桌面之间的窗口互相切换,该如何**作呢? 方法很简单,具体**作和步骤如下: 1、先转到待移动窗口的桌面 2、点击“任务视图”按钮或者按Win+Tab,右键单击希望移动择目标桌面后单击相应编话甚还号。 注意:Windows 10的

上述是一个登录模块的例子,Module 作为基类,定义了模块的一些生命周期方法。LoginModule是对外公开的业务接口,里面仅包含外部会用到的和登录业务相关的方法。LoginModuleImplV1类是登录逻辑的具体实现,不对外公开,里面的私有成员变量和方法对外部是隐藏的,同时实现了Module和LoginModule的接口。Provider用于创建和管理Module实例。这里采用的思路是,底层模块和模块之间,上层和底层之间只依赖接口头文件,头文件内包含有限的需要对外暴露的接口。通过XModule这个框架,将实现和接口进行分离。为了将接口和实现分离,用到了 pimpl (Pointer to Implementation) 的理念,将对象的实现细节隐藏在指针背后。LoginModule接口负责定义对外公开的API,LoginModuleImplV1类负责定义LoginModule的具体实现,也就是调用的指针实际指向的对象。调用方只能知道LoginModule中公开的API,而无法知道LoginModuleImplV1的实现细节,可以降低调用方的使用门槛,也可以降低错误使用的可能性。pimpl不仅解除了接口和实现之间的耦合关系,还可以降低文件间的编译依赖关系,起到“编译防火墙”的作用,可以提高一定的编译效率。

// LoginModuleProvider 通过宏自动生成X_MODULE_PROVIDER_DEFINE_SINGLE(LoginModule, MIN_VERSION, MAX_VERSION);// LoginModuleImplV1Provider 通过宏自动生成X_MODULE_DEFINE_SECONDARY_PROVIDER(LoginModuleImplV1, LoginModule);

XModule的模版开发方式,会增加很多类文件,为了方便,通过宏来控制Provider类的自动生成。其中MIN_VERSION和MAX_VERSION是该Module接口能支持的最小和最大的版本范围,可以限制后期dll插件化加载时,不加载在版本之外的dll,避免产生冲突和错误,目前Provider的GetVersion使用的是MAX_VERSION。

// 由 X_MODULE_DEFINE_SECONDARY_PROVIDER 宏自动生成class DLLEXPORT LoginModuleImplV1Provider : public LoginModuleProvider {public: LoginModule* Create() const { LoginModuleImplV1* p = new LoginModuleImplV1(); ((Module*)p)->OnCreate();return p; } };

LoginModuleImplV1Provider可以通过调用Create方法拿到对应的LoginModuleImplV1实例。

x_module::ModuleCenter* module_center = x_module::ModuleCenter::GetInstance();module_center->AcceptProviderType<LoginModuleProvider>();

ModuleCenter是所有Module的管理类,先通过x_module::ModuleCenter::GetInstance()拿到ModuleCenter的实例,它是一个跨dll的单例。第二要用之前的LoginModuleProvider去注册一个Module类型到ModuleCenter中。LoginModuleProvider中定义了支持的Module类型,以及最小版本和最大版本,如果后续扫描到的dll中提供的对应类型的Provider中GetVersion返回的值不在最大版本和最小版本之间,那么就不会被允许加载进来。

module_center->AddProvider(new LoginModuleImplV1Provider());

通过这种方式,可以将LoginModuleImplV1Provider注册到ModuleCenter中,第二创建并管理LoginModuleImplV1的实例。但是这样就显式地依赖了LoginModuleImplV1Provider,违反了前面说过的依赖倒置原则,对开闭原则也不友好,因为这样就只能通过修改代码来实现扩展了。

#include <x_module/connector.h>#include “login_module/login_module_impl.h”X_MODULE_CONNECTORbool XModuleConnect(x_module::Owner& owner) { owner.add(new LoginModuleImplV1Provider());return true;}

为了在加载dll时,来注册Provider,增加了一个connector.cc,添加一个XModuleConnect方法,让dll被加载之后,能够找到XModuleConnect这个符号方法,并进行调用,在XModuleConnect被调用的时候,会调用AddProvider将Provider进行注册。

std::string path = GetProgramDir();module_center->Install(path, “login_module”);

由于目前login_module.dll是直接放在exe同目录的,所以这里直接获取了一下exe绝对路径,第二调用Install方法,将路径和dll名login_module传入进去,这样就完成了注册。

auto* p_login_module = module_center->ModuleFromProtocol<LoginModule, LoginModuleProvider>();if (p_login_module == ptr) { (*move_result)->Error(“-100”, “login module 为空”);return;}bool islogin = p_login_module->IsLogin();

在使用时,只需要LoginModule和LoginModuleProvider这两个抽象,就能获取真实的LoginModuleImplV1这个实例,调用方仅需关心LoginModule所公开的API,完全屏蔽了对实现的依赖。后续底层扩展成了LoginModuleImplV2,只要LoginModule的公开API不变,对上层是无感知的。这种方式完全遵循了前面提到的设计原则,对团队内的多人维护以及后续的更新迭代都带来了稳定的保障。

基于vcpkg的C++依赖管理

模块拆分之后,带来的副作用就是依赖管理会变得更加复杂,到C++这边就是CMakeLists的膨胀。从移动端的角度来看这个问题,Android可以通过Gradle来管理依赖,依赖库构建成aar之后上传到Maven仓库,implementation ‘androidx.recyclerview:recyclerview:1.1.0’像这样通过包名、库名和版本号来依赖具体的库。iOS有CocoaPods,通过添加pod ‘AFNetworking’, ‘~> 2.6’到Podfile来完成依赖的添加。前端也有NPM这样的包管理器,所有依赖都在package.json这个文件中声明和管理。Flutter侧也可以通过pubspec来管理各个依赖库。为了获得一致的体验,解决C++侧依赖管理的痛点,我们引入了微软官方推出的vcpkg,vcpkg的清单模式可以得到类似的体验。

依赖库配置

这里以fish-ffi-module模块为例子,文件结构如下,其中include文件里面是对外公开的头文件,src文件包含当前库内部使用的代码,cmake文件下的config.cmake.in模版文件用于生成xxx-config.cmake的文件,用于被find_package找到。

.├── CMakeLists.txt├── LICENSE├── cmake│ └── config.cmake.in├── include│ └── fish_ffi_module.h├── src│ ├── connector.cc│ ├── fish_ffi_module_impl_v1.cc│ └── fish_ffi_module_impl_v1.h├── vcpkg-configuration.json└── vcpkg.json

vcpkg-configuration.json配置了私有源,后面会讲到。vcpkg.json文件,声明了当前库所依赖的其他库,即vcpkg的依赖清单,其中”dependencies”字段声明了所使用的依赖名称。

{“name”: “fish-ffi-module”,“version”: “1.0.0”,“description”: “A fish-ffi module based on fish-ffi-sdk.”,“homepage”: “”,“dependencies”: [“fish-ffi-sdk”,“x-module”,“flutter-sdk” ]}

CMake工程最重要的就是CMakeLists文件了,里面配置了编译相关的设置,添加了相关的注释来帮助理解。

cmake_minimum_required(VERSION 3.15)# 仓库版本常量,升级时修改set(FISH_FFI_MODULE_VERSION “1.0.0”)project(fish-ffi-module VERSION ${FISH_FFI_MODULE_VERSION} DESCRIPTION “A fish-ffi module based on fish-ffi-sdk.” HOMEPAGE_URL “” LANGUAGES CXX)option(BUILD_SHARED_LIBS “Build using shared libraries” ON)# vcpkg清单中添加依赖之后,通过find_package就能找到find_package(fish-ffi-sdk CONFIG REQUIRED)find_package(flutter-sdk CONFIG REQUIRED)find_package(x-module CONFIG REQUIRED)# configure_package_config_file 生成config要用到include(CMakePackageConfigHelpers)# install 安装要用到include(GNUInstallDirs)# 当前库的头文件和源文件aux_source_directory(include HEADER_LIST)aux_source_directory(src SRC_LIST)add_library(fish-ffi-module SHARED${HEADER_LIST}${SRC_LIST})# 设置别名add_library(fish-ffi-module::fish-ffi-module ALIAS fish-ffi-module)# 设置动态库导出宏,PRIVATE为编译时,INTERFACE为运行时if (BUILD_SHARED_LIBS AND WIN32)target_compile_definitions(fish-ffi-module PRIVATE “FISH_FFI_MODULE_EXPORT=__declspec(dllexport)” INTERFACE “FISH_FFI_MODULE_EXPORT=__declspec(dllimport)”)endif ()target_compile_features(fish-ffi-module PUBLIC cxx_std_17)# 添加头文件target_include_directories(fish-ffi-module PUBLIC lt;BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/> lt;INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)# 链接库文件target_link_libraries(fish-ffi-module PRIVATE fish-ffi-sdk::fish-ffi-sdk)target_link_libraries(fish-ffi-module PRIVATE flutter-sdk::flutter-sdk)target_link_libraries(fish-ffi-module PRIVATE x-module::x-module)# 基于config.cmake.in的模板生成xxx-config.cmake的文件configure_package_config_file( cmake/config.cmake.in${CMAKE_CURRENT_BINARY_DIR}/fish-ffi-module-config.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_DATADIR}/fish-ffi-module NO_SET_AND_CHECK_MACRO)# 生成xx-config-version.cmake文件write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/fish-ffi-module-config-version.cmake VERSION ${FISH_FFI_MODULE_VERSION} COMPATIBILITY SameMajorVersion)# 将上面生成的两个config文件,安装到share/fish-ffi-module下install( FILES${CMAKE_CURRENT_BINARY_DIR}/fish-ffi-module-config.cmake${CMAKE_CURRENT_BINARY_DIR}/fish-ffi-module-config-version.cmake DESTINATION${CMAKE_INSTALL_DATADIR}/fish-ffi-module)# 安装头文件install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})# install targetinstall(TARGETS fish-ffi-moduleEXPORT fish-ffi-module-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})# 导出install(EXPORT fish-ffi-module-targets NAMESPACE fish-ffi-module:: DESTINATION ${CMAKE_INSTALL_DATADIR}/fish-ffi-module)

这里面最重要的一点是配置xx-config.cmake和xx-config-version.cmake的生成,vcpkg会在源码首次拉下来的时候进行编译,编译完在相应库的share目录生成上述两个文件,并且在CMake配置阶段执行,这样在使用find_package的时候就能获取到这个库以及对应版本号。小编综合来说一下就是,vcpkg帮助完成了代码的下载、编译和配置,第二就可以方便的链接三方库了。

自定义私有源

私有源的自定义非常简单,其实就是个Git仓库,push到私有的git托管服务上即可。只需要将依赖库的最新commit信息记录到这个仓库里面,通过模版化的配置就能完成依赖库的发布。

.├── ports│ ├── fish-ffi-module│ │ ├── portfile.cmake│ │ └── vcpkg.json│ └── x-module│ ├── portfile.cmake│ └── vcpkg.json├── versions│ ├── f-│ │ └── fish-ffi-module.json│ └── x-│ │ └── x-module.json│ └──baseline.json└── LICENSE

vcpkg里面对依赖库的定义叫port,这里定义了两个port,分别是fish-ffi-module和x-module。其中的文件说明如下:

• portfile.cmake中定义了这个库的git地址、分支、commitId、编译配置等信息

• vcpkg.json定义了这个port的依赖以及版本信息,如果有依赖,则会在编译这个库之前优先编译依赖。

• versions下的文件按首字母分类,里面定义了version和git-tree的对应关系。在port新增或更新之后,git-tree需要重新生成,通过git rev-parse HEAD:ports/x-module来生成git-tree,第二通过git commit –amend追加提交到刚刚的commit中。

在需要使用私有源的CMake工程根目录,添加vcpkg-configuration.json,里面内容如下。default-registry为默认源,指向官方的地址即可。registries下添加自定义的私有源,再通过指定packages,表示里面的库需要在这个私有源查找。这样就完成了私有源的配置。

{“default-registry”: {“kind”: “git”,“repository”: “https://github.com/microsoft/vcpkg”,“baseline”: “f4b262b259145adb2ab0116a390b08642489d32b” },“registries”: [ {“kind”: “git”,“repository”: “xxx.git”,“baseline”: “1ad54586a5a2fadb8c44d3f8f47754e849fc5a38”,“packages”: [ “x-module”, “fish-ffi-sdk”, “fish-ffi-module”] } ] }

在versions文件夹下还有一个baseline.json的文件,这个文件主要是设置基线用的,不像其他的依赖管理工具,vcpkg主要是通过这个基线来设置当前所使用的版本号的。vcpkg可以胜任依赖管理的相关工作,综上所述只是一个简单使用,相比其他平台的依赖管理工具略显繁琐,除此之外还有很多其他能力,需要到vcpkg.io的官方文档里面探索了。

小编综合来说

Flutter应用接入Windows平台,主要遇到的问题就是Windows侧的一些能力的提供,需要对齐Android和iOS的已有能力。因为使用的是C++的开发语言,对于移动端开发者并不是那么友好,学习曲线相对会比较抖。不过一旦平台侧的能力完善之后,又可以回归到Flutter这个熟悉的领域了,享受Flutter开发带来的便捷。此外Windows应用的开发不仅仅只是屏幕加大版的移动端开发,还包括不同的输入设备(键盘鼠标)、交互习惯、样式风格、**作系统特性等,为了更好的平台体验,会带来一定的适配成本,这一块后续也将持续投入。

拓展知识:

原创文章,作者:九贤生活小编,如若转载,请注明出处:http://www.wangguangwei.com/119239.html