在使用托管c++前,先说明一下 托管c++ 与 本地c++ 的类型转换方法:
C++ 中的封送处理概述
在混合模式下,有时必须封送本机和托管类型之间的数据。 Visual Studio 2008 中引入封送处理库以帮助将封送,并将数据转换的简单方式。 封送处理库包含一组函数和一个marshal_context
执行常见类型的封送处理的类。 在这些标头中定义库包括 msclr目录为您的 Visual Studio 版本:
Header | 描述 |
---|---|
marshal.h | marshal_context 类和无上下文的封送处理函数 |
marshal_atl.h | 用于封送处理 ATL 类型的函数 |
marshal_cppstd.h | 用于封送处理标准 c + + 类型的函数 |
marshal_windows.h | 用于封送处理 Windows 类型的函数 |
默认路径msclr文件夹是类似下面有具体取决于哪个版本和内部版本号:
cmd复制
C:\\Program Files (x86)\\Microsoft Visual Studio\\Preview\\Enterprise\\VC\\Tools\\MSVC\\14.15.26528\\include\\msclr
可以使用封送处理库,带或不带marshal_context 类。 某些转换需要上下文。 可以使用实现其他转换marshal_as函数。 下表列出了当前支持的转换、 是否需要上下文和封送文件必须包括:
从类型 | 若要键入 | 封送方法 | 包含文件 |
---|---|---|---|
System:: string ^ | const char * | marshal_context | marshal.h |
const char * | System:: string ^ | marshal_as | marshal.h |
Char * | System:: string ^ | marshal_as | marshal.h |
System:: string ^ | const wchar_t* | marshal_context | marshal.h |
const wchar_t * | System:: string ^ | marshal_as | marshal.h |
wchar_t * | System:: string ^ | marshal_as | marshal.h |
System::IntPtr | 句柄 | marshal_as | marshal_windows.h |
句柄 | System::IntPtr | marshal_as | marshal_windows.h |
System:: string ^ | BSTR | marshal_context | marshal_windows.h |
BSTR | System:: string ^ | marshal_as | marshal.h |
System:: string ^ | bstr_t | marshal_as | marshal_windows.h |
bstr_t | System:: string ^ | marshal_as | marshal_windows.h |
System:: string ^ | std:: string | marshal_as | marshal_cppstd.h |
std:: string | System:: string ^ | marshal_as | marshal_cppstd.h |
System:: string ^ | std:: wstring | marshal_as | marshal_cppstd.h |
std:: wstring | System:: string ^ | marshal_as | marshal_cppstd.h |
System:: string ^ | CStringT<char > | marshal_as | marshal_atl.h |
CStringT<char > | System:: string ^ | marshal_as | marshal_atl.h |
System:: string ^ | CStringT < wchar_t > | marshal_as | marshal_atl.h |
CStringT < wchar_t > | System:: string ^ | marshal_as | marshal_atl.h |
System:: string ^ | CComBSTR | marshal_as | marshal_atl.h |
CComBSTR | System:: string ^ | marshal_as | marshal_atl.h |
仅当你将封送从托管到本机数据类型并将转换为本机类型不具有的析构函数的自动清理时,封送处理需要上下文。 封送处理上下文销毁其析构函数中的已分配的本机数据类型。 因此,仅删除上下文之前,需要上下文的转换将始终有效。 若要保存任何封送的值,必须将值复制到自己的变量中。
marshal_as
此方法将本机和托管环境之间的数据转换。
示例
此示例是从封送const char*
到System::String
变量类型。
复制
// marshal_as_test.cpp
// compile with: /clr
#include <stdlib.h>
#include <string.h>
#include <msclr\marshal.h>
using namespace System;
using namespace msclr::interop;
int main() {
const char* message = "Test String to Marshal";
String^ result;
result = marshal_as<String^>( message );
return 0;
}
marshal_context 类
此类将在本机和托管环境之间转换数据。对于需要上下文的数据转换,请使用 marshal_context
类。
示例
此示例将为从 System::String
到 const char *
变量类型的封送处理创建上下文。 转换数据在删除上下文的行之后是无效的。
复制
// marshal_context_test.cpp
// compile with: /clr
#include <stdlib.h>
#include <string.h>
#include <msclr\marshal.h>
using namespace System;
using namespace msclr::interop;
int main() {
marshal_context^ context = gcnew marshal_context();
String^ message = gcnew String("Test String to Marshal");
const char* result;
result = context->marshal_as<const char*>( message );
delete context;
return 0;
}
------------------------------------------ 我是分隔线 ----------------------------------------------------------
在C#调用C++托管库的过程中,C++托管库一般只是封装为真正C++动态链接库(DLL)的一个外壳,其不做任何逻辑功能,但是会存在数据类型及结构上的转换问题,本文就是用于介绍数据类型转换过程中需要考虑的问题。
数据类型很多,限于篇幅原因,文章主要讲解几种常见且较复杂的数据类型。
1. C#句柄与C++窗体句柄类型的转换, IntPtr<—>HWND;
2. C# String类型 与 C++ String 类型转换
3. C#数据地址与C++指针转换
4. C#结构体与C++结构体转换
一、C#句柄IntPtr与C++窗体句柄HWND的转换
在C++托管代码中可以直接引入C#数据类型IntPtr, 然后可以转换成HWND类型,转换过程如下:
C#调用环境接口:CSharp_InterfaceCLR(IntPtr InputDataSharp)
C++ CLR 托管环境接口:CPP_InterfaceCLR(IntPtr InputDataClr)
C++环境中接口:CPP_Interface(HWND InputData);
CSharp_InterfaceCLR(InputDataSharp)
--->CPP_InterfaceCLR(InputDataClr) //InputDataSharp 可以直接赋值给 InputDataClr
--->CPP_Interface((HWND) (InputDataClr.ToInt32())); //把InputDataClr.ToInt32()强制转换成HWND
二、C# String类型 与 C++ String 类型转换
在讲解转换之前,需要说一个符号^,C++/CLI中使用gcnew关键字表示在托管堆上分配内存,并且为了与以前的指针区分,用^来替换* ,就语义上来说区别大致如下:
1)gcnew返回的是一个句柄(Handle),而new返回的是实际的内存地址;
2)gcnew创建的对象由虚拟机托管,而new创建的对象必须自己来管理和释放。
从开发者的角度,不论是句柄还是其他的类型,总会是对某块内存地址的引用,可以理解为指针。
C#调用环境接口:CSharp_InterfaceCLR(string InputDataSharp)
C++ CLR 托管环境接口:CPP_InterfaceCLR(String^ InputDataClr)
C++环境中接口:CPP_Interface(string InputData);
CSharp_InterfaceCLR(InputDataSharp)
--->CPP_InterfaceCLR(InputDataClr) //InputDataSharp 可以直接赋值给 InputDataClr
//把String^类型的string转换为C++ string的过程如下:
std::string InputData = marshal_as<std::string>(InputDataClr);
--->CPP_Interface(InputData);
//注意在托管代码中需要添加如下信息:
#include <msclr\marshal_cppstd.h>
using namespace msclr::interop;
using namespace System;
三、 C#数据地址与C++指针转换
C#中的变量地址可以通过IntPtr传递到CLR,然后CLR(托管C++)转换为一个数值,在C++代码中直接通过指针强制转换即可完成地址在C#与C++直接的转换。
具体过程可以如下:
C#调用环境接口:CSharp_InterfaceCLR(IntPtr InputDataSharp)
C++ CLR 托管环境接口:CPP_InterfaceCLR(IntPtr InputDataClr)
C++环境中接口:CPP_Interface(Void* InputData);
CSharp_InterfaceCLR(InputDataSharp)
--->CPP_InterfaceCLR(InputDataClr) //InputDataSharp 可以直接赋值给 InputDataClr
--->CPP_Interface((void*)InputDataClr.ToInt32()); //把InputDataClr.ToInt32()强制转换成void*, 也可以通过InputDataClr.ToPointer(),然后在C++代码里面把void*转成相应的类型
//c#中使用指针:在需要使用指针的地方 加 unsafe
关于其他指针类型转换需要注意:
unsigned char** ppImage <===> IntPtr ppImage 双指针类型参数,都可以用 ref IntPtr
int& Variable <===> ref int Variable
int*, int& <===> ref int
char* 的操作 <===> c#:StringBuilder;
函数指针使用: c++: typedef int (*function)(int); <===>c#:public delegate int function(int);
关于函数指针的使用会在C++调用C#注册的回调函数一章中详细说明。
四、 C#结构体与C++结构体转换
C++中 Struct需要在C#里重新定义一个Struct与之对应起来。
说明如下:
typedef struct
{
char CharArray[256];
INT32 IntegerVariable;
INT32 IntegerVariable1;
UINT16 UnsignedIntegerVariable;
bool BoolType;
//string StringVariable 不可以用string类型,可以使用char*代替
} STRUCT;
//法1.
[StructLayout(LayoutKind.Sequential)]
public struct STRUCT
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[] CharArray;
public Int32 IntegerVariable;
public UInt16 UnsignedIntegerVariable;
public Boolean BoolType;
}
可以使用一个C#函数做转换
public StructType ConverBytesToStructure<StructType>(byte[] bytesBuffer)
{
// check the length。
if (bytesBuffer.Length != Marshal.SizeOf(typeof(STRUCT)))
{
throw new ArgumentException("bytesBuffer is not same as structObject for the length of byte.");
}
IntPtr bufferHandler = Marshal.AllocHGlobal(bytesBuffer.Length);
for (int index = 0; index < bytesBuffer.Length; index++)
{
Marshal.WriteByte(bufferHandler, index, bytesBuffer[index]);
}
STRUCT structObject = (STRUCT)Marshal.PtrToStructure(bufferHandler, typeof(STRUCT));
Marshal.FreeHGlobal(bufferHandler);
return structObject;
}
FileStream file = File.OpenRead(@"C:/Path");
byte[] buffer = new byte[Marshal.SizeOf(typeof(STRUCT))];
file.Read(buffer, 0, buffer.Length);
STRUCT testValue = CommonTools.ConverBytesToStructure<STRUCT>(buffer);
string sCharArray = new string(testValue.CharArray);
//法2. 就不需要再写转换函数
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct STRUCT
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string CharArray;
public Int32 IntegerVariable;
public UInt16 UnsignedIntegerVariable;
public Boolean BoolType;
}
其他数据类型的转换列表如下:
基本数据类型转换:
Windows编程数据类型
其他归类表格总结: