如何:在通用 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 而不是 LoadLibraryLoadLibraryEx

如果您有 DLL 或靜態程式庫的原始程式碼,您可以使用編譯器選項,將 /ZW 它重新編譯為 UWP 專案。 然後,您可以使用 方案總管 來新增 參考,並在 C++ UWP 應用程式中使用它。 使用匯出程式庫連結 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.hdllmain.cpp ) 中所有其他專案都是標準 Win32 專案範本的一部分。 此程式碼會定義宏 GIRAFFE_API ,其會在定義時 GIRAFFE_EXPORTS 解析為 __declspec(dllexport) 。 也就是說,它會在專案建置為 DLL 時定義,但不是當用戶端使用 giraffe.h 標頭時定義。 此 DLL 可以在 UWP 專案中使用,而不需變更原始程式碼。 只有某些專案設定和屬性需要變更。

當您有使用 公開函 __declspec(dllexport) 式的原生 DLL 時,適用下列程式。

移植原生 DLL 到 UWP 而不需建立新專案

  1. 在 Visual Studio 中開啟 DLL 專案。

  2. 請開啟此 DLL 專案的 [專案屬性],並將組態設定為 [所有組態]

  3. 在 [專案屬性] 中,於 [C/C++]>[一般] 索引標籤中,將 [使用 Windows 執行階段延伸模組] 設定為 [是 (/ZW)]。 此屬性會啟用元件延伸模組 (C++/CX)。

  4. 方案總管 中,選取專案節點、開啟快捷方式功能表,然後選擇 [ 卸載專案 ]。 接著,在卸載的專案節點上開啟捷徑功能表,然後選擇要編輯的專案檔。 找出 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 專案。

  5. 請確定先行編譯標頭檔的名稱正確。 在 [ 先行編譯頭 檔] 區段中,如果您看到類似以下的錯誤,您可能需要將先行編譯標頭檔 pch.h 變更 stdafx.h 或其他方式:

    錯誤 C2857: 在原始程式檔中找不到使用命令列選項指定的 /Ycpch.h '#include' 語句

    問題是較舊的專案範本會針對先行編譯標頭檔使用不同的命名慣例。 Visual Studio 2019 和更新版本專案使用 pch.h

  6. 組建專案。 您可能會收到有關不相容命令列選項的一些錯誤。 例如,現已淘汰但曾頻繁使用的選項 [啟用最少重建 (/Gm)] 在許多舊版 C++ 專案中是預設設定,但和 /ZW 不相容。

    當您針對 通用 Windows 平臺 進行編譯時,無法使用某些函式。 您會看到任何問題的編譯器錯誤。 解決這些錯誤,直到您有全新的組建為止。

  7. 若要在 UWP 應用程式中以相同的方案使用 DLL,請開啟 UWP 專案節點的捷徑功能表,然後依序選擇 [新增]>[參考]

    在 [專案]>[解決方案] 下選取此 DLL 專案旁邊的核取方塊,然後選擇 [確定] 按鈕。

  8. 將程式庫的標頭檔包含在 UWP 應用程式的檔案中 pch.h

    #include "..\Giraffe\giraffe.h"
    
  9. 如往常般在 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++ 靜態程式庫

  1. 在 UWP 專案的專案屬性中,選擇左窗格中的 [組態屬性]>[連結器]>[輸入]。 在右窗格的 [其他相依性] 屬性中,將路徑新增至程式庫。 例如,對於專案中放置其輸出 <SolutionFolder>\Debug\MyNativeLibrary\MyNativeLibrary.lib 的程式庫,請新增相對路徑 Debug\MyNativeLibrary\MyNativeLibrary.lib

  2. 新增 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 時,可以新增 ref 類型和其他 C++/CX 建構,這些建構可供任何 UWP 應用程式程式碼中的用戶端使用。 您可以從 C#、Visual Basic 或 JavaScript 存取這些類型。 基本程序如下:

  • 建立Windows 執行階段元件 (通用 Windows) 專案,
  • 將靜態程式庫的程式碼複製到其中,然後
  • 解決選項所 /ZW 造成之編譯器的任何錯誤。

若要將 C++ 程式庫移植到 Windows 執行階段元件

  1. 建立Windows 執行階段元件 (通用 Windows) 專案。

  2. 關閉專案。

  3. Windows 檔案總管 中,找出新的專案。 然後,找出包含您要移植之程式碼的 C++ 程式庫專案。 從您的 C++ 程式庫專案複製原始程式檔 (標頭檔、程式碼檔案和任何其他資源,包括子目錄中)。 將它們貼到新的專案資料夾中,請務必保留相同的資料夾結構。

  4. 重新開啟Windows 執行階段元件專案。 在 方案總管 中 開啟專案節點的快捷方式功能表,然後選擇 [ 新增 > 現有專案]。

  5. 從原始專案中選取要新增的所有檔案,然後選擇 [確定]。 必要時,對子資料夾重複上述步驟。

  6. 您現在已有重複的節點。 如果有一個以上的先行編譯標頭(例如 stdafx.hpch.h ),請選擇一個來保留。 將任何必要的程式碼 (例如 include 陳述式) 複製到您要保留的檔案中。 然後,刪除另一個,並在專案屬性的 [先行編譯標頭 ] 底下 ,確定標頭檔的名稱正確無誤。

    如果您已變更要做為先行編譯標頭使用的檔案,請確定每個檔案的先行編譯標頭檔選項都正確無誤。 接著選取每個 .cpp 檔案,開啟其屬性視窗,並確定所有檔案都設定為 [使用] (/Yu), 但先行編譯標頭除外,這應該設定為 [建立] (/Yc)。

  7. 建置專案,並解決任何錯誤。 這些錯誤可能是使用 /ZW 選項所造成,或可能是由新版本的 Windows SDK 所造成。 或者,它們可能會反映相依性,例如程式庫相依的標頭檔,或舊專案與新專案之間的專案設定差異。

  8. 將公用 ref 類型新增至您的專案,或將一般類型轉換成 ref 類型。 使用這些類型,將進入點公開至您想要從 UWP 應用程式呼叫的功能。

  9. 藉由從 UWP 應用程式專案中加入元件的參考來測試該元件,並加入一些程式碼來呼叫您建立的公用 API。

另請參閱

移植到通用 Windows 平台