如何:在 通用 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,其會在定義時__declspec(dllexport)解析為 GIRAFFE_EXPORTS 。 也就是說,它會在專案建置為 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 或其他方式:

    錯誤 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 執行階段元件。 它不再是靜態庫;您將將其轉換成 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 執行階段元件

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

  2. 關閉專案。

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

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

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

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

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

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

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

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

另請參閱

移植到通用 Windows 平台