可以通过不同的方式在通用 Windows 平台 (UWP) 项目中使用现有的 C++ 代码。 有些方式不需要在启用组件扩展 (C++/CX) 的情况下(即使用
/ZW
选项)重新编译代码,有些则需要。 可能需要将代码保留在标准 C++ 中,或者为某些代码保留经典的 Win32 编译环境。 如果做出了适当的体系结构选择,则你仍可以这样做。 假设你的所有代码包含 UWP UI 以及向 C#、Visual Basic 和 JavaScript 调用方公开的类型。 这些代码应位于 Windows 应用项目和 Windows 运行时组件项目中。 仅从 C++(包括 C++/CX)调用的代码可以位于使用
/ZW
选项编译的项目中,也可以位于标准 C++ 项目中。 对于未使用禁止的 API 的纯二进制代码,可以通过将其作为静态库链接来使用它们。 或者,可将其与应用一起打包为内容并将其加载到 DLL 中。
要在 UWP 环境中运行桌面程序,最简单的方法或许是使用桌面桥技术。 这些技术包括 Desktop App Converter,此工具可将现有应用程序打包为 UWP 应用,而无需更改代码。 有关详细信息,请参阅
桌面桥
。
本文的余下内容介绍如何将 C++ 库(DLL 和静态库)移植到通用 Windows 平台。 你可能需要移植代码,以便将核心 C++ 逻辑用于多个 UWP 应用。
UWP 应用在受保护的环境中运行。 因此,不允许发出许多可能危及平台安全的 Win32、COM 和 CRT API 调用。
/ZW
编译器选项可以检测此类调用并生成错误。 可以使用应用程序上的应用认证工具包检测调用禁止的 API 的代码。 有关详细信息,请参见
Windows 应用认证工具包
。
如果源代码适用于库,则你可以尝试消除禁止的 API 调用。 有关禁止的 API 列表,请参阅
用于 UWP 应用的 Win32 和 COM API
以及
通用 Windows 平台应用中不支持的 CRT 函数
。 可通过
UWP 应用中的 Windows API 替代项
,找到一些替代项。
如果只是尝试从通用 Windows 项目添加引用到经典桌面库,你将得到一条显示库不兼容的错误消息。 如果它是静态库,你可以通过将该库(
.lib
文件)添加到链接器输入来链接到该库,如同在经典 Win32 应用程序中所做的那样。 如果只有二进制库可用,则这是唯一选项。 静态库将链接到应用的可执行文件。 但是,对于在 UWP 应用中使用的 Win32 DLL,必须通过将其包含在项目中并将其标记为“内容”,来将其打包到应用中。 若要在 UWP 应用中加载 Win32 DLL,还必须调用
LoadPackagedLibrary
而不是
LoadLibrary
或
LoadLibraryEx
。
如果你有适用于 DLL 或静态库的源代码,可以使用
/ZW
编译器选项将其重新编译为 UWP 项目。 然后,可以使用解决方案资源管理器添加对它的引用并在 C++ UWP 应用中使用它。 使用导出库链接 DLL。
若要向其他语言中的调用方公开功能,则可以将库转换为 Windows 运行时组件。 Windows 运行时组件与普通的 DLL 的不同之处在于它们包括
.winmd
文件格式的元数据,这些元数据以 .NET 和 JavaScript 的使用者需要的方式介绍内容。 若要将 API 元素公开给其他语言,可以添加 C++/CX 构造(例如 ref 类)并将其公开。 在 Windows 10 以及更高版本中,建议使用
C++/WinRT 库
而不是 C++/CX。
前面的讨论不适用于 COM 组件,COM 组件必须以不同方式处理。 如果 EXE 或 DLL 中有 COM 服务器,可以在通用 Windows 项目中使用它。 将其打包为
免注册 COM 组件
,将其作为内容文件添加到项目中,然后使用
CoCreateInstanceFromApp
将其实例化。 有关详细信息,请参阅
在 Windows 应用商店 C++ 项目中使用 Free-COM DLL
。
如果你想要将现有的 COM 库移植到 UWP,也可以将其转换为 Windows 运行时组件。 我们建议将 C++/WinRT 库用于此类端口,但也可以使用
Windows 运行时 C++ 模板库 (WRL)
。 WRL 已弃用,它不支持 ATL 和 OLE 的所有功能。 此类端口是否可行取决于组件所需的 COM、ATL 和 OLE 功能。
无论选择哪种开发方案,都应该了解一些宏定义。 可以在代码中使用这些宏,以便在经典桌面 Win32 和 UWP 中按条件编译代码。
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PC_APP)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
这些语句分别适用于 UWP 应用、Windows Phone 应用商店应用、都适用或都不适用(仅针对经典 Win32 桌面)。 这些宏仅在 Windows SDK 8.1 和更高版本中可用。
本文包含以下过程:
在 UWP 应用中使用 Win32 DLL
在 UWP 应用中使用本机 C++ 静态库
将 C++ 库移植到 Windows 运行时组件
在 UWP 应用中使用 Win32 DLL
为了提高安全性和可靠性,通用 Windows 应用在受限的运行时环境中运行。 不能像在经典 Windows 桌面应用程序中那样使用任何本机 DLL。 如果你有 DLL 的源代码,则可以移植此代码,以便使其在 UWP 上运行。 你首先更改几个项目设置和项目文件元数据,以将此项目标识为 UWP 项目。 使用 /ZW
选项重新编译库代码,从而启用 C++/CX。 由于与该环境相关的控制更严格,在 UWP 应用中,某些 API 调用是不被允许的。 有关详细信息,请参阅 UWP 应用的 Win32 和 COM API。
如果你的本机 DLL 使用 __declspec(dllexport)
导出函数,则可以通过将 DLL 重新编译为 UWP 项目,从 UWP 应用中调用这些函数。 例如,假设我们有一个名为 Giraffe 的 Win32 DLL 项目,它使用类似于以下标头文件的代码,可导出几个类及其方法:
// giraffe.h
// Define GIRAFFE_EXPORTS when building this DLL
#pragma once
#ifdef GIRAFFE_EXPORTS
#define GIRAFFE_API __declspec(dllexport)
#else
#define GIRAFFE_API
#endif
GIRAFFE_API int giraffeFunction();
class Giraffe
int id;
Giraffe(int id_in);
friend class GiraffeFactory;
public:
GIRAFFE_API int GetID();
class GiraffeFactory
static int nextID;
public:
GIRAFFE_API GiraffeFactory();
GIRAFFE_API static int GetNextID();
GIRAFFE_API static Giraffe* Create();
以下代码文件:
// giraffe.cpp
#include "pch.h"
#include "giraffe.h"
Giraffe::Giraffe(int id_in) : id(id_in)
int Giraffe::GetID()
return id;
int GiraffeFactory::nextID = 0;
GiraffeFactory::GiraffeFactory()
nextID = 0;
int GiraffeFactory::GetNextID()
return nextID;
Giraffe* GiraffeFactory::Create()
return new Giraffe(nextID++);
int giraffeFunction();
项目(pch.h
、dllmain.cpp
)中的其他所有内容都属于标准 Win32 项目模板。 代码定义宏 GIRAFFE_API
,定义 GIRAFFE_EXPORTS
时,该宏解析为 __declspec(dllexport)
。 也就是说,它是在项目生成为 DLL 时定义的,而不是在客户端使用 giraffe.h
标头时定义的。 此 DLL 可以在 UWP 项目中使用,而无需更改源代码。 只有某些项目设置和属性需要更改。
如果你的本机 DLL 使用 __declspec(dllexport)
公开函数,则以下过程适用。
若要在无需创建新项目的情况下将本机 DLL 移植到 UWP
在 Visual Studio 中打开你的 DLL 项目。
打开 DLL 项目中的“项目属性”,并将“配置”设置为“所有配置”。
在“项目属性”的“C/C++”>“常规”选项卡下,将“使用 Windows 运行时扩展”设置为“是(/ZW)”。 此属性启用组件扩展 (C++/CX)。
在“解决方案资源管理器”中,选择项目节点,打开快捷菜单,然后选择“卸载项目”。 然后,在卸载的项目节点上打开快捷菜单,然后选择编辑项目文件。 找到 WindowsTargetPlatformVersion
元素,并将其替换为下列元素。
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<WindowsTargetPlatformVersion>10.0.10156.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.10156.0</WindowsTargetPlatformMinVersion>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
关闭 .vcxproj
文件,再次打开快捷菜单,然后选择“重新加载项目”。
现在,解决方案资源管理器会将该项目标识为通用 Windows 项目。
请确保预编译的头文件的名称正确。 在“预编译标头”部分,可能需要将“预编译标头文件”从 pch.h
更改为 stdafx.h
;如果看到如下所示的错误,请采取其他解决方法:
错误 C2857:在源文件中没有找到用 /Ycpch.h
命令行选项指定的“#include”语句
问题在于早期的项目模板对预编译标头文件使用不同的命名约定。 Visual Studio 2019 和更高版本的项目使用 pch.h
。
生成项目。 可能会收到一些有关不兼容的命令行选项的错误。 例如,许多较旧的 C++ 项目中默认设置有“启用最小重新生成(/Gm)”这一常用选项(但现在已弃用),它与 /ZW
不兼容。
为通用 Windows 平台编译时,某些功能不可用。 你将看到有关任何问题的编译器错误。 解决这些错误,直到有一个干净的生成。
要在同一解决方案中的 UWP 应用中使用该 DLL,请打开 UWP 项目节点的快捷菜单,然后选择“添加”>“引用”。
在“项目”>“解决方案”下,选中 DLL 项目旁边的复选框,然后选择“确定”按钮。
将库的一个或多个头文件添加到 UWP 应用的 pch.h
文件中。
#include "..\Giraffe\giraffe.h"
照常将代码添加到 UWP 项目中,以从 DLL 中调用函数并创建类型。
MainPage::MainPage()
InitializeComponent();
GiraffeFactory gf;
Giraffe* g = gf.Create();
int id = g->GetID();
在 UWP 应用中使用本机 C++ 静态库
你可以在 UWP 项目中使用本机 C++ 静态库,但有一些限制和局限需要注意。 请先阅读 C++/CX 中的静态库。 你可以从 UWP 应用访问静态库中的本机代码,但不建议在此类静态库中创建公共 ref 类型。 如果使用 /ZW
选项编译静态库,则管理员(实际是经过伪装的链接器)会发出警告:
LNK4264: 正在将使用 /ZW 编译的对象文件归档到静态库中;请注意,创作 Windows 运行时类型时,建议不要与包含 Windows 运行时元数据的静态库链接
但是,无需使用 /ZW
重新编译 UWP 应用中的静态库即可使用该库。 库无法声明任何 ref 类型或使用 C++/CX 构造。 但是,如果你的目的只是使用本机代码库,则可以执行以下步骤。
若要在 UWP 项目中使用本机 C++ 静态库
在 UWP 项目的项目属性中,在左窗格中依次选择“配置属性”>“链接器”>“输入”。 在右窗格中,将路径添加到库中的“其他依赖项”属性中。 例如,对于将输出放入 <SolutionFolder>\Debug\MyNativeLibrary\MyNativeLibrary.lib
的项目中的库,请添加相对路径 Debug\MyNativeLibrary\MyNativeLibrary.lib
。
添加 include 语句,以将头文件引用到 pch.h
文件(若有)或所需的任何 .cpp
文件中,并开始添加使用库的代码。
#include "..\MyNativeLibrary\MyNativeLibrary.h"
不要在“解决方案资源管理器”的“引用”节点中添加引用。 该机制仅适用于 Windows 运行时组件。
将 C++ 库移植到 Windows 运行时组件
假设你要使用 UWP 应用的静态库中的本机 API。 如果你有本机库的源代码,可将代码移植到 Windows 运行时组件。 它不再是静态库;它将变成一个可以在任何 C++ UWP 应用中使用的 DLL。 此过程说明如何创建使用 C++/CX 扩展的新 Windows 运行时组件。 有关创建使用 C++/WinRT 的组件的信息,请参阅使用 C++/WinRT 的 Windows 运行时组件。
使用 C++/CX 时,可以添加可供任何 UWP 应用代码中的客户端使用的 ref 类型和其他 C++/CX 构造。 可以从 C#、Visual Basic 或 JavaScript 访问这些类型。 基本过程:
创建 Windows 运行时组件(通用 Windows)项目;
将静态库的代码复制到该项目;
解决 /ZW
选项在编译器中导致的错误。
若要将 C++ 库移植到 Windows 运行时组件
创建 Windows 运行时组件(通用 Windows)项目。
关闭该项目。
在“Windows 文件资源管理器”中找到新项目。 然后找到 C++ 库项目,该项目包含你想要移植的代码。 从 C++ 库项目复制源文件(头文件、代码文件和任何其他资源,包括子目录中的资源)。 将它们粘贴到新项目文件夹,确保保留相同的文件夹结构。
重新打开 Windows 运行时组件项目。 在“解决方案资源管理器”中打开项目节点的快捷菜单,然后选择“添加”>“现有项”。
从原始项目中选择要添加的所有文件,然后选择“确定”。 如果子文件夹需要,则重复。
你现在可能有一些重复代码。 如果有多个预编译标头(例如 stdafx.h
和 pch.h
),请选择要保留的一个标头。 将任何所需的代码(比如 include 语句)复制到你要保留的标头中。 然后删除另一个标头,并在“预编译标头”下的项目属性中,确保头文件的名称正确。
如果更改了要用作预编译标头的文件,请确保预编译标头选项适用于每个文件。 依次选择每个 .cpp
文件,打开其属性窗口,并确保所有项都设置为“使用 (/Yu)”(预编译标头除外,其应设置为“创建 (/Yc)”)。
生成项目并解决任何错误。 这些错误可能是因使用 /ZW
选项而导致的,或者是由新的 Windows SDK 版本导致的。 或者,它们可能反映了依赖关系,例如库依赖的头文件,或者旧项目与新项目之间的项目设置差异。
将公共 ref 类型添加到项目,或将普通类型转换为 ref 类型。 使用这些类型将入口点公开到要从 UWP 应用调用的功能中。
通过从 UWP 应用项目添加对组件的引用来测试该组件,然后添加某些代码来调用你创建的公共 API。
移植到通用 Windows 平台