如何:扩展封送处理库
若要了解有关 Visual Studio 2017 RC 的最新文档,请参阅 Visual Studio 2017 RC 文档。
本主题说明如何扩展封送处理库,从而在数据类型之间提供更多转换。 用户可以为库当前不支持的任何数据转换扩展封送处理库。
可以通过两种方式之一扩展封送处理库 — 既可以使用也可以不使用 marshal_context 类。 请查看 C++ 中的封送处理概述主题,确定新转换是否需要上下文。
在这两种情况下,首先需要为新封送处理转换创建一个文件。 这样做是为了保留标准封送处理库文件的完整性。 如果需要将一个项目导入到另一台计算机或转到另一程序员,则必须将新的封送处理文件与项目的其余部分一起复制。 通过这种方式,可以保证接收项目的用户接收新转换,并且不必修改任何库文件。
使用不需要上下文的转换扩展封送处理库
-
创建一个文件来存储新的封送处理函数,例如,MyMarshal.h。
-
包括以下一个或多个封送库文件:
-
用于基类型的 marshal.h。
-
用于 Windows 数据类型的 marshal_windows.h。
-
用于 STL 数据类型的 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
-
包括以下一个或多个封送库文件:
-
用于基类型的 marshal.h。
-
用于 Windows 数据类型的 marshal_windows.h。
-
用于 STL 数据类型的 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; }