방법: P/Invoke를 사용하여 문자열 마샬링

C 스타일 문자열을 허용하는 네이티브 함수는 .NET Framework Platform Invoke(P/Invoke) 지원을 사용하여 CLR 문자열 형식 System::String 을 사용하여 호출할 수 있습니다. 가능한 경우 P/Invoke 대신 C++ Interop 기능을 사용하는 것이 좋습니다. P/Invoke는 컴파일 시간 오류 보고를 거의 제공하지 않으므로 형식이 안전하지 않으며 구현하는 데 지루할 수 있습니다. 관리되지 않는 API가 DLL로 패키지되고 소스 코드를 사용할 수 없는 경우 P/Invoke가 유일한 옵션입니다. 그렇지 않으면 C++ Interop 사용(암시적 P/Invoke)을 참조하세요.

관리되는 문자열과 관리되지 않는 문자열은 메모리에 다르게 배치되므로 관리되는 함수에서 관리되지 않는 함수로 문자열을 전달하려면 문자열 데이터를 올바로 안전하게 마샬링하는 데 필요한 변환 메커니즘을 삽입하도록 컴파일러에 지시하는 특성이 필요합니다 MarshalAsAttribute .

내장 데이터 형식 DllImportAttribute 만 사용하는 함수와 마찬가지로 네이티브 함수에 관리되는 진입점을 선언하는 데 사용됩니다. 문자열을 전달하는 함수는 이러한 진입점을 C 스타일 문자열을 사용하는 것으로 정의하는 대신 형식에 대한 핸들 String 을 사용할 수 있습니다. 이 형식을 사용하면 필요한 변환을 수행하는 코드를 삽입하라는 메시지가 컴파일러에 표시됩니다. 문자열을 사용하는 관리되지 않는 함수의 각 함수 인수에 대해 특성을 사용하여 MarshalAsAttribute 개체를 네이티브 함수에 C 스타일 문자열로 마샬링해야 함을 String 나타냅니다.

마샬러는 숨겨진 래퍼 루틴에서 관리되지 않는 함수에 대한 호출을 래핑합니다. 래퍼 루틴은 관리되는 문자열을 고정하고 관리되지 않는 컨텍스트에서 로컬로 할당된 문자열에 복사합니다. 그런 다음 로컬 복사본이 관리되지 않는 함수에 전달됩니다. 관리되지 않는 함수가 반환되면 래퍼는 리소스를 삭제합니다. 또는 스택에 있는 경우 래퍼가 범위를 벗어날 때 회수됩니다. 관리되지 않는 함수는 이 메모리를 담당하지 않습니다. 관리되지 않는 코드는 자체 CRT로 설정된 힙에서만 메모리를 만들고 삭제하므로 다른 CRT 버전을 사용하는 마샬러에는 문제가 없습니다.

관리되지 않는 함수가 반환 값 또는 out 매개 변수로 문자열을 반환하는 경우 마샬러는 문자열을 새 관리되는 문자열로 복사한 다음 메모리를 해제합니다. 자세한 내용은 플랫폼 호출을 사용하여 기본 마샬링 동작데이터 마샬링을 참조하세요.

예시

다음 코드는 관리되지 않는 모듈과 관리되는 모듈로 구성됩니다. 관리되지 않는 모듈은 라는 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);
}

관리되는 모듈은 함수를 가져오 TakesAString 지만 대신 관리 System.String 되는 것으로 정의하는 명령줄 애플리케이션입니다 char*. 이 MarshalAsAttribute 특성은 호출될 때 TakesAString 관리되는 문자열을 마샬링하는 방법을 나타내는 데 사용됩니다.

// 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);
}

이 기술은 관리되지 않는 힙에서 문자열의 복사본을 생성하므로 네이티브 함수에 의한 문자열 변경 내용은 문자열의 관리되는 복사본에 반영되지 않습니다.

DLL의 어떤 부분도 기존 #include 지시문에 의해 관리 코드에 노출되지 않습니다. 실제로 DLL은 런타임에만 액세스하므로 컴파일 시간에 사용하여 DllImport 가져온 함수의 문제가 검색되지 않습니다.

참고 항목

C++에서 명시적 P/Invoke 사용(DllImport 특성)