共用方式為


如何:使用 P/Invoke 封送處理字串

使用 .NET Framework Platform Invoke (P/Invoke) 支援,可以使用 CLR 字串類型 System::String 呼叫接受 C 樣式字串的原生函式。 建議您盡可能使用 C++ Interop 功能,而不是 P/Invoke。 因為 P/Invoke 提供很少的編譯時間錯誤報表,不是型別安全,而且可能很繁瑣來實作。 如果 Unmanaged API 封裝為 DLL,且原始程式碼無法使用,則 P/Invoke 是唯一的選項。 否則,請參閱 使用 C++ Interop (隱含 P/Invoke)

Managed 和 Unmanaged 字串會以不同的方式在記憶體中配置,因此將字串從 Managed 傳遞至 Unmanaged 函式需要 MarshalAsAttribute 屬性來指示編譯器插入必要的轉換機制,以便正確且安全地封送處理字串資料。

如同只使用內建資料類型的函式, DllImportAttribute 用來將 Managed 進入點宣告至原生函式。 傳遞字串的函式可以使用型別的 String 控制碼,而不是將這些進入點定義為採用 C 樣式字串。 使用此類型會提示編譯器插入執行必要轉換的程式碼。 針對接受字串之 Unmanaged 函式中的每個函式引數,使用 MarshalAsAttribute 屬性工作表示 String 物件應該封送處理為原生函式做為 C 樣式字串。

封送處理器會在隱藏包裝函式常式中包裝對 Unmanaged 函式的呼叫。 包裝函式常式會釘選,並將 Managed 字串複製到 Unmanaged 內容中本機配置的字串。 然後,本機複本會傳遞至 Unmanaged 函式。 當 Unmanaged 函式傳回時,包裝函式會刪除資源。 或者,如果它位於堆疊上,當包裝函式超出範圍時,就會回收它。 Unmanaged 函式不負責此記憶體。 Unmanaged 程式碼只會在其自己的 CRT 所設定的堆積中建立和刪除記憶體,因此使用不同 CRT 版本的封送處理器永遠不會發生問題。

如果您的 Unmanaged 函式會以傳回值或 out 參數的形式傳回字串,封送處理器會將它複製到新的 Managed 字串,然後釋放記憶體。 如需詳細資訊,請參閱 使用平臺叫 用封送處理行為 和 封送處理資料。

範例

下列程式碼包含 Unmanaged 模組和受控模組。 Unmanaged 模組是定義稱為 TakesAString 之函式的 DLL。 TakesAString 接受 C 樣式的窄字串,格式為 char*

// TraditionalDll2.cpp
// compile with: /LD /EHsc
#include <windows.h>
#include <stdio.h>
#include <iostream>

using namespace std;

#define TRADITIONALDLL_EXPORTS
#ifdef TRADITIONALDLL_EXPORTS
#define TRADITIONALDLL_API __declspec(dllexport)
#else
#define TRADITIONALDLL_API __declspec(dllimport)
#endif

extern "C" {
   TRADITIONALDLL_API void TakesAString(char*);
}

void TakesAString(char* p) {
   printf_s("[unmanaged] %s\n", p);
}

Managed 模組是匯入函式的 TakesAString 命令列應用程式,但會將它定義為採用 Managed System.String 而不是 char* 。 屬性 MarshalAsAttribute 是用來指出呼叫 時 TakesAString 應如何封送處理 Managed 字串。

// MarshalString.cpp
// compile with: /clr
using namespace System;
using namespace System::Runtime::InteropServices;

value struct TraditionalDLL
{
   [DllImport("TraditionalDLL2.dll")]
      static public void
      TakesAString([MarshalAs(UnmanagedType::LPStr)]String^);
};

int main() {
   String^ s = gcnew String("sample string");
   Console::WriteLine("[managed] passing managed string to unmanaged function...");
   TraditionalDLL::TakesAString(s);
   Console::WriteLine("[managed] {0}", s);
}

這項技術會在 Unmanaged 堆積上建構字串的複本,因此原生函式對字串所做的變更不會反映在字串的 Managed 複本中。

傳統 指示詞不會向 Managed 程式碼 #include 公開 DLL 的一部分。 事實上,DLL 只會在執行時間存取,因此不會在編譯時期偵測到使用 DllImport 匯入的函式問題。

另請參閱

在 C++ 中使用明確的 P/Invoke ( DllImport 屬性)