如何:扩展封送处理库
本主题说明如何扩展封送处理库,从而在数据类型之间提供更多转换。 用户可以为库当前不支持的任何数据转换扩展封送处理库。
可以通过两种方式之一扩展封送处理库 — 既可以使用也可以不使用 marshal_context Class。 请查看 Overview of Marshaling in 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 函数返回被转换数据的句柄。 这样做是为了防止创建数据的另一个副本。 直接返回变量将产生与之相关的不必要的性能开销。
下面的示例将员工信息从托管数据类型转换为本机数据类型。 此转换需要封送处理上下文。
// 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;
}