Практическое руководство. Расширение библиотеки маршалинга

В этом разделе объясняется, как расширить библиотеку маршалинга, чтобы обеспечить больше преобразований между типами данных. Пользователи могут расширить библиотеку маршалинга для любых преобразований данных, которые в настоящее время не поддерживаются библиотекой.

Вы можете расширить библиотеку маршалинга одним из двух способов — с marshal_context классом или без нее. Ознакомьтесь с обзором маршалинга в разделе C++ , чтобы определить, требуется ли новое преобразование контекста.

В обоих случаях сначала создается файл для новых преобразований маршалинга. Это необходимо для сохранения целостности стандартных файлов библиотек маршалинга. Если вы хотите перенести проект на другой компьютер или другой программист, необходимо скопировать новый файл маршалинга вместе с остальным проектом. Таким образом, пользователь, получая проект, гарантированно получит новые преобразования и не будет изменять файлы библиотеки.

Расширение библиотеки маршалинга с преобразованием, которое не требует контекста

  1. Создайте файл для хранения новых функций маршалинга, например MyMarshal.h.

  2. Включите один или несколько файлов маршал библиотеки:

    • marshal.h для базовых типов.

    • marshal_windows.h для типов данных Windows.

    • marshal_cppstd.h для типов данных стандартной библиотеки C++.

    • marshal_atl.h для типов данных ATL.

  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. Включите один или несколько файлов маршал библиотеки:

    • marshal.h для базовых типов.

    • marshal_windows.h для типов данных Windows.

    • marshal_cppstd.h для типов данных стандартной библиотеки C++.

    • marshal_atl.h для типов данных ATL.

  3. Используйте код в конце этих шагов для записи функции преобразования. В этом коде TO — это тип для преобразования, FROM — это тип, из которого необходимо преобразовать, toObject — это указатель, в котором хранится результат, и fromObject является параметром для преобразования.

  4. Замените комментарий на инициализацию кодом, чтобы инициализировать toPtr соответствующее пустое значение. Например, если это указатель, задайте для него значение NULL.

  5. Замените комментарий логикой преобразования кодом, чтобы преобразовать from параметр в объект типа TO . Преобразованный объект будет сохранен в toPtr.

  6. Замените комментарий о настройке toObject кодом для задания toObject преобразованного объекта.

  7. Замените комментарий о очистке собственных ресурсов кодом, чтобы освободить любую память, выделенную 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

См. также

Общие сведения о маршалинге в C++