如何:扩展封送处理库

本主题介绍如何扩展封送库,以在数据类型之间提供更多转换。 用户可以为封送库当前不支持的任何数据转换扩展库。

可以通过这两种方式之一扩展封送库:使用或不使用 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++ 中的封送处理概述