方法: マーシャリング ライブラリを拡張する
このトピックでは、マーシャリング ライブラリを拡張して、データ型間の変換をさらに多く提供する方法について説明します。 ユーザーは、ライブラリで現在サポートされていない任意のデータ変換用にマーシャリング ライブラリを拡張できます。
マーシャリング ライブラリは、marshal_context クラスを使う方法と使わない方法の 2 つの方法で拡張できます。 新しい変換にコンテキストが必要かどうかを判断するには、「C++ におけるマーシャリングの概要」トピックをご覧ください。
どちらの場合も、最初に新しいマーシャリング変換用のファイルを作成します。 これは、標準のマーシャリング ライブラリ ファイルの整合性を維持するために行います。 プロジェクトを別のコンピューターまたは別のプログラマーに移植する場合は、新しいマーシャリング ファイルをプロジェクトの残りの部分と一緒にコピーする必要があります。 この方法を使うと、プロジェクトを受け取ったユーザーは、新しい変換を受け取ることを保証され、ライブラリ ファイルを変更する必要はありません。
コンテキストを必要としない変換でマーシャリング ライブラリを拡張するには
新しいマーシャリング関数を格納するファイル (MyMarshal.h など) を作成します。
1 つ以上のマーシャリング ライブラリ ファイルをインクルードします。
基本データ型の場合は marshal.h。
Windows のデータ型の場合は marshal_windows.h。
C++ 標準ライブラリのデータ型の場合は marshal_cppstd.h。
ATL のデータ型の場合は marshal_atl.h。
これらの手順の最後にあるコードを使って、変換関数を記述します。 このコードで、TO は変換後の型、FROM は変換前の型、
from
は変換対象のパラメーターです。変換ロジックに関するコメントを、
from
パラメーターを TO 型のオブジェクトに変換し、変換されたオブジェクトを返すコードに置き換えます。
namespace msclr {
namespace interop {
template<>
inline TO marshal_as<TO, FROM> (const FROM& from) {
// Insert conversion logic here, and return a TO parameter.
}
}
}
コンテキストを必要とする変換でマーシャリング ライブラリを拡張するには
新しいマーシャリング関数を格納するファイル (MyMarshal.h など) を作成します
1 つ以上のマーシャリング ライブラリ ファイルをインクルードします。
基本データ型の場合は marshal.h。
Windows のデータ型の場合は marshal_windows.h。
C++ 標準ライブラリのデータ型の場合は marshal_cppstd.h。
ATL のデータ型の場合は marshal_atl.h。
これらの手順の最後にあるコードを使って、変換関数を記述します。 このコードで、TO は変換後の型、FROM は変換前の型、
toObject
は結果を格納するポインター、fromObject
は変換対象のパラメーターです。初期化に関するコメントを、
toPtr
を適切な空の値に初期化するコードに置き換えます。 たとえば、ポインターの場合は、それをNULL
に設定します。変換ロジックに関するコメントを、
from
パラメーターを TO 型のオブジェクトに変換するコードに置き換えます。 この変換されたオブジェクトは、toPtr
に格納されます。toObject
の設定に関するコメントを、toObject
を変換されたオブジェクトに設定するコードに置き換えます。ネイティブ リソースのクリーンアップに関するコメントを、
toPtr
によって割り当てられたメモリを解放するコードに置き換えます。toPtr
がnew
を使ってメモリを割り当てた場合は、delete
を使ってメモリを解放します。
namespace msclr {
namespace interop {
template<>
ref class context_node<TO, FROM> : public context_node_base
{
private:
TO toPtr;
public:
context_node(TO& toObject, FROM fromObject)
{
// (Step 4) Initialize toPtr to the appropriate empty value.
// (Step 5) Insert conversion logic here.
// (Step 6) Set toObject to the converted parameter.
}
~context_node()
{
this->!context_node();
}
protected:
!context_node()
{
// (Step 7) Clean up native resources.
}
};
}
}
例: マーシャリング ライブラリを拡張する
次の例は、コンテキストを必要としない変換でマーシャリング ライブラリを拡張するものです。 この例のコードは、従業員情報をネイティブ データ型からマネージド データ型に変換します。
// MyMarshalNoContext.cpp
// compile with: /clr
#include <msclr/marshal.h>
value struct ManagedEmp {
System::String^ name;
System::String^ address;
int zipCode;
};
struct NativeEmp {
char* name;
char* address;
int zipCode;
};
namespace msclr {
namespace interop {
template<>
inline ManagedEmp^ marshal_as<ManagedEmp^, NativeEmp> (const NativeEmp& from) {
ManagedEmp^ toValue = gcnew ManagedEmp;
toValue->name = marshal_as<System::String^>(from.name);
toValue->address = marshal_as<System::String^>(from.address);
toValue->zipCode = from.zipCode;
return toValue;
}
}
}
using namespace System;
using namespace msclr::interop;
int main() {
NativeEmp employee;
employee.name = "Jeff Smith";
employee.address = "123 Main Street";
employee.zipCode = 98111;
ManagedEmp^ result = marshal_as<ManagedEmp^>(employee);
Console::WriteLine("Managed name: {0}", result->name);
Console::WriteLine("Managed address: {0}", result->address);
Console::WriteLine("Managed zip code: {0}", result->zipCode);
return 0;
}
前の例の marshal_as
関数は、変換されたデータへのハンドルを返します。 これは、データの追加コピーが作成されないようにするために行われました。 変数を直接返すと、それに関連して不要なパフォーマンス コストが発生します。
Managed name: Jeff Smith
Managed address: 123 Main Street
Managed zip code: 98111
例: 従業員情報を変換する
次の例では、従業員情報をマネージド データ型からネイティブ データ型に変換します。 この変換には、マーシャリング コンテキストが必要です。
// MyMarshalContext.cpp
// compile with: /clr
#include <stdlib.h>
#include <string.h>
#include <msclr/marshal.h>
value struct ManagedEmp {
System::String^ name;
System::String^ address;
int zipCode;
};
struct NativeEmp {
const char* name;
const char* address;
int zipCode;
};
namespace msclr {
namespace interop {
template<>
ref class context_node<NativeEmp*, ManagedEmp^> : public context_node_base
{
private:
NativeEmp* toPtr;
marshal_context context;
public:
context_node(NativeEmp*& toObject, ManagedEmp^ fromObject)
{
// Conversion logic starts here
toPtr = NULL;
const char* nativeName;
const char* nativeAddress;
// Convert the name from String^ to const char*.
System::String^ tempValue = fromObject->name;
nativeName = context.marshal_as<const char*>(tempValue);
// Convert the address from String^ to const char*.
tempValue = fromObject->address;
nativeAddress = context.marshal_as<const char*>(tempValue);
toPtr = new NativeEmp();
toPtr->name = nativeName;
toPtr->address = nativeAddress;
toPtr->zipCode = fromObject->zipCode;
toObject = toPtr;
}
~context_node()
{
this->!context_node();
}
protected:
!context_node()
{
// When the context is deleted, it will free the memory
// allocated for toPtr->name and toPtr->address, so toPtr
// is the only memory that needs to be freed.
if (toPtr != NULL) {
delete toPtr;
toPtr = NULL;
}
}
};
}
}
using namespace System;
using namespace msclr::interop;
int main() {
ManagedEmp^ employee = gcnew ManagedEmp();
employee->name = gcnew String("Jeff Smith");
employee->address = gcnew String("123 Main Street");
employee->zipCode = 98111;
marshal_context context;
NativeEmp* result = context.marshal_as<NativeEmp*>(employee);
if (result != NULL) {
printf_s("Native name: %s\nNative address: %s\nNative zip code: %d\n",
result->name, result->address, result->zipCode);
}
return 0;
}
Native name: Jeff Smith
Native address: 123 Main Street
Native zip code: 98111