次の方法で共有


方法: マーシャリング ライブラリを拡張する

このトピックでは、マーシャリング ライブラリを拡張して、データ型間の変換をさらに多く提供する方法について説明します。 ユーザーは、ライブラリで現在サポートされていない任意のデータ変換用にマーシャリング ライブラリを拡張できます。

マーシャリング ライブラリは、marshal_context クラスを使う方法と使わない方法の 2 つの方法で拡張できます。 新しい変換にコンテキストが必要かどうかを判断するには、「C++ におけるマーシャリングの概要」トピックをご覧ください。

どちらの場合も、最初に新しいマーシャリング変換用のファイルを作成します。 これは、標準のマーシャリング ライブラリ ファイルの整合性を維持するために行います。 プロジェクトを別のコンピューターまたは別のプログラマーに移植する場合は、新しいマーシャリング ファイルをプロジェクトの残りの部分と一緒にコピーする必要があります。 この方法を使うと、プロジェクトを受け取ったユーザーは、新しい変換を受け取ることを保証され、ライブラリ ファイルを変更する必要はありません。

コンテキストを必要としない変換でマーシャリング ライブラリを拡張するには

  1. 新しいマーシャリング関数を格納するファイル (MyMarshal.h など) を作成します。

  2. 1 つ以上のマーシャリング ライブラリ ファイルをインクルードします。

    • 基本データ型の場合は marshal.h。

    • Windows のデータ型の場合は marshal_windows.h。

    • C++ 標準ライブラリのデータ型の場合は marshal_cppstd.h。

    • ATL のデータ型の場合は marshal_atl.h。

  3. これらの手順の最後にあるコードを使って、変換関数を記述します。 このコードで、TO は変換後の型、FROM は変換前の型、from は変換対象のパラメーターです。

  4. 変換ロジックに関するコメントを、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.
      }
   }
}

コンテキストを必要とする変換でマーシャリング ライブラリを拡張するには

  1. 新しいマーシャリング関数を格納するファイル (MyMarshal.h など) を作成します

  2. 1 つ以上のマーシャリング ライブラリ ファイルをインクルードします。

    • 基本データ型の場合は marshal.h。

    • Windows のデータ型の場合は marshal_windows.h。

    • C++ 標準ライブラリのデータ型の場合は marshal_cppstd.h。

    • ATL のデータ型の場合は marshal_atl.h。

  3. これらの手順の最後にあるコードを使って、変換関数を記述します。 このコードで、TO は変換後の型、FROM は変換前の型、toObject は結果を格納するポインター、fromObject は変換対象のパラメーターです。

  4. 初期化に関するコメントを、toPtr を適切な空の値に初期化するコードに置き換えます。 たとえば、ポインターの場合は、それを NULL に設定します。

  5. 変換ロジックに関するコメントを、from パラメーターを TO 型のオブジェクトに変換するコードに置き換えます。 この変換されたオブジェクトは、toPtr に格納されます。

  6. toObject の設定に関するコメントを、toObject を変換されたオブジェクトに設定するコードに置き換えます。

  7. ネイティブ リソースのクリーンアップに関するコメントを、toPtr によって割り当てられたメモリを解放するコードに置き換えます。 toPtrnew を使ってメモリを割り当てた場合は、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

関連項目

C++ におけるマーシャリングの概要