如何:在 通用 Windows 平台 應用程式中使用現有的C++程序代碼
在 通用 Windows 平台 (UWP) 專案中,您可以使用現有的C++程序代碼。 某些方式不需要重新編譯程式代碼以啟用元件延伸模組(C++/CX)(也就是 /ZW
使用 選項),有些則為 。 您可能需要將程式代碼保留在標準C++,或保留某些程式代碼的傳統 Win32 編譯環境。 您仍然可以使用適當的架構選擇來執行此動作。 請考慮您的所有程序代碼,其中包含公開給 C#、Visual Basic 和 JavaScript 呼叫端的 UWP UI 和類型。 此程式代碼應該位於 Windows 應用程式專案和 Windows 執行階段元件專案中。 您只從 C++ 呼叫的程式代碼(包括 C++/CX)可以位於使用 /ZW
選項或標準C++專案編譯的專案中。 將它連結為靜態庫,即可使用不使用不允許 API 的二進位程序代碼。 或者,您可以將應用程式封裝為內容,並將其載入 DLL 中。
在 UWP 環境中執行傳統型程式的最簡單方式,可能是使用傳統型橋接器技術。 它們包含 Desktop App Converter,其會將您現有的應用程式封裝為 UWP 應用程式,不需要變更程式代碼。 如需詳細資訊,請參閱傳統型橋接器。
本文的其餘部分將討論如何將C++連結庫(DLL 和靜態庫)移植到 通用 Windows 平台。 您可能想要移植程式碼,讓核心C++邏輯可以與多個UWP應用程式搭配使用。
UWP Apps 會在受保護的環境中執行。 因此,不允許許多可能危害平臺安全性的 Win32、COM 和 CRT API 呼叫。 編譯 /ZW
程式選項可以偵測這類呼叫併產生錯誤。 您可以使用應用程式上的應用程式認證套件來偵測呼叫不允許 API 的程式碼。 如需詳細資訊,請參閱 Windows 應用程式認證套件。
如果連結庫可以使用原始程式碼,您可以嘗試排除不允許的 API 呼叫。 如需不允許的 API 清單,請參閱 通用 Windows 平台 應用程式中不支援的 UWP 應用程式和 CRT 函式的 Win32 和 COM API。 UWP 應用程式中的 Windows API 替代方案也提供了幾種替代方案。
如果您只是嘗試將通用 Windows 專案的參考新增至傳統桌面連結庫,您會收到錯誤訊息,指出連結庫不相容。 如果是靜態連結庫,您可以將連結庫(.lib
檔案)新增至連結器輸入,以與傳統 Win32 應用程式中的相同方式連結至連結庫。 如果只有二進位連結庫可用,則是唯一的選項。 靜態庫會連結至應用程式的可執行檔。 不過,您在 UWP 應用程式中取用的 Win32 DLL 必須封裝到應用程式中,方法是將它包含在專案中,並將它標示為內容。 若要在 UWP 應用程式中載入 Win32 DLL,您也必須呼叫 LoadPackagedLibrary
而不是 LoadLibrary
或 LoadLibraryEx
。
如果您有 DLL 或靜態庫的原始程式碼,您可以使用編譯程式選項,將 /ZW
它重新編譯為 UWP 專案。 然後,您可以使用 方案總管 來新增參考,並在 C++ UWP app 中使用。 使用匯出連結庫連結 DLL。
若要向其他語言的呼叫者公開功能,您可以將程式庫轉換成 Windows 執行階段元件。 Windows 執行階段元件與一般 DLL 不同,因為它們包含以 .NET 和 JavaScript 取用者所需方式描述內容的檔案形式.winmd
元數據。 若要將 API 元素公開給其他語言,您可以新增 C++/CX 建構,例如 ref 類別,並將它們公開。 在 Windows 10 和更新版本中,我們建議 使用 C++/WinRT 連結庫 ,而不是C++/CX。
上述討論不適用於必須以不同方式處理的 COM 元件。 如果您在 EXE 或 DLL 中有 COM 伺服器,您可以在通用 Windows 專案中使用它。 將它封裝為 無註冊的 COM 元件、將它新增至您的專案作為內容檔案,並使用 CoCreateInstanceFromApp
具現化它。 請參閱 Using Free-COM DLL in Windows Store C++ Project (在 Microsoft Store 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 Store 應用程式、兩者都適用或兩者均不適用 (僅傳統 Win32 桌面)。 這些巨集僅適用於 Windows SDK 8.1 和更新版本。
本文包含下列程式:
在 UWP 應用程式中使用 Win32 DLL
為了獲得更佳的安全性和可靠性,通用 Windows 應用程式會在受限制的運行時間環境中執行。 您不只可以使用傳統 Windows 傳統型應用程式中的任何原生 DLL。 如果您有 DLL 的原始程式碼,就可以移植程式碼,使其在 UWP 上執行。 首先可變更幾個專案設定和專案檔案中繼資料,將該專案識別為 UWP 專案。 您將使用 /ZW
選項重新編譯連結庫程序代碼,以啟用C++/CX。 UWP 應用程式中不允許某些 API 呼叫,因為與該環境相關聯的控件更嚴格。 如需詳細資訊,請參閱 適用於UWP應用程式的 Win32 和 COM API。
如果您有使用 __declspec(dllexport)
來匯出函式的原生 DLL,您可以將 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 專案中使用,而不需變更原始程式碼。 只有某些項目設定和屬性需要變更。
當您有使用 公開函 __declspec(dllexport)
式的原生 DLL 時,適用下列程式。
移植原生 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
。組建專案。 您可能會收到有關不相容命令行選項的一些錯誤。 例如,現已淘汰但曾頻繁使用的選項 [啟用最少重建 (/Gm)] 在許多舊版 C++ 專案中是預設設定,但和
/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 執行階段中繼資料的靜態程式庫連結
不過,您可以在 UWP 應用程式中使用靜態庫,而不需使用 /ZW
重新編譯它。 您的連結庫無法宣告任何 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 執行階段元件。 它不再是靜態庫;您將將其轉換成 DLL,您可以在任何C++ UWP app 中使用。 此程式描述如何建立使用 C++/CX 擴充功能的新 Windows 執行階段 元件。 如需建立使用 C++/WinRT 之元件的詳細資訊,請參閱使用 C++/WinRT Windows 執行階段 元件。
當您使用 C++/CX 時,您可以新增 ref 類型和其他C++/CX 建構,這些建構可供任何 UWP 應用程式程式代碼中的用戶端使用。 您可以從 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。