AcitveX提供跨语言的开发方式,支持C++、C#、Pascal等语言开发,但不同语言对BSTR生成的‘头文件’多有不同,本文意在阐明BSTR在不同开发框架下的封装与解包。
一、.Net中使用,以Winform为例(WPF通用)
c#调用windowsApi
C#调用windows API的一些方法 - 南极山 - 博客园
windows API原型
SysAllocStringByteLen function (oleauto.h) - Win32 apps | Microsoft Docs
使用案例
注意bool数组转bstr写法,需要双字节对齐
/// <summary>
/// 各类数组与binary string的数据封装
/// </summary>
public class InteropEncDotNet //InteropMarshaling
{
[DllImport("OleAut32", ExactSpelling = true, SetLastError = true)]
public static extern IntPtr SysAllocStringByteLen(string psz, Int32 len);
/// <summary>
/// int数组转bstr
/// </summary>
/// <param name="intArray">数组值</param>
/// <param name="elementCount">数组长度</param>
/// <returns>binary string</returns>
public static string GetStringFromIntArray(int[] intArray, int elementCount = 0)
{
if (elementCount == 0)
elementCount = intArray.Length;
int size = Marshal.SizeOf(typeof(int)) * elementCount;
IntPtr ip = SysAllocStringByteLen(null, size);
Marshal.Copy(intArray, 0, ip, elementCount);
string bstr = Marshal.PtrToStringBSTR(ip);
Marshal.FreeBSTR(ip);
return bstr;
}
/// <summary>
/// bstr转int数组
/// </summary>
/// <param name="str">binary string</param>
/// <param name="elementCount">目标数组长度</param>
/// <returns>目标数组值</returns>
public static int[] GetIntArrayFromString(string str, int elementCount)
{
int[] intArray3 = new int[elementCount];
IntPtr ip_rev = Marshal.StringToBSTR(str);
Marshal.Copy(ip_rev, intArray3, 0, elementCount);
Marshal.FreeBSTR(ip_rev);
return intArray3;
}
/// <summary>
/// float数组转bstr
/// </summary>
/// <param name="floatArray">数组值</param>
/// <param name="elementCount">数组长度</param>
/// <returns>binary string</returns>
public static string GetStringFromFloatArray(float[] floatArray,int elementCount = 0)
{
if (elementCount == 0)
elementCount = floatArray.Length;
int size = Marshal.SizeOf(typeof(float)) * elementCount;
IntPtr ip = SysAllocStringByteLen(null, size);
Marshal.Copy(floatArray, 0, ip, elementCount);
string bstr = Marshal.PtrToStringBSTR(ip);
Marshal.FreeBSTR(ip);
return bstr;
}
/// <summary>
/// bstr转float数组
/// </summary>
/// <param name="str">binary string</param>
/// <param name="elementCount">目标数组长度</param>
/// <returns>目标数组值</returns>
public static float[] GetFloatArrayFromString(string str, int elementCount)
{
float[] arrfloat = new float[elementCount];
IntPtr ip = Marshal.StringToBSTR(str);
Marshal.Copy(ip, arrfloat, 0, elementCount);
Marshal.FreeBSTR(ip);
return arrfloat;
}
/// <summary>
/// bool数组转bstr
/// </summary>
/// <param name="boolArray">数组值</param>
/// <param name="elementCount">数组长度</param>
/// <returns>binary string</returns>
public static string GetStringFromBoolArray(bool[] boolArray, int elementCount = 0)
{
//Marshal.SizeOf(typeof(bool)) = 4个字节,但C++中为1字节
if (elementCount == 0)
elementCount = boolArray.Length;
int size = 1 * elementCount;
if ((size & (int)1) == 1)
size += 1; //必须双字节对齐
IntPtr ip = SysAllocStringByteLen(null, size);
for (int i = 0; i < elementCount; i++)
{
Marshal.WriteByte(ip, i, boolArray[i] ? (byte)1 : (byte)0);
}
string bstr = Marshal.PtrToStringBSTR(ip);
Marshal.FreeBSTR(ip);
return bstr;
}
/// <summary>
/// bstr转bool数组
/// </summary>
/// <param name="str">binary string</param>
/// <param name="elementCount">目标数组长度</param>
/// <returns>目标数组值</returns>
public static bool[] GetBoolArrayFromString(string str, int elementCount)
{
bool[] arrBool = new bool[elementCount];
IntPtr ip = Marshal.StringToBSTR(str);
byte[] tmp = new byte[1];
for (int i = 0; i < elementCount; i++)
{
arrBool[i] = Marshal.ReadByte(ip, i) == (byte)1 ? true : false;
}
Marshal.FreeBSTR(ip);
return arrBool;
}
}
测试转换是否可行
void TestConvert()
{
long allTestCount = 0;
int errCount = 0;
//使用marshal方法转换
for (int i = int.MinValue; i < int.MaxValue; i++)
{
IntPtr ip = SysAllocStringByteLen("", Marshal.SizeOf(typeof(int)));
Marshal.WriteInt32(ip, i);
string strInt = Marshal.PtrToStringBSTR(ip);
Marshal.FreeBSTR(ip);
//反算
IntPtr ip_rev = Marshal.StringToBSTR(strInt);
int i_rev = Marshal.ReadInt32(ip_rev);
Marshal.FreeBSTR(ip_rev);
if (i != i_rev)
{
Console.WriteLine("转换错误, i= " + i + " i_rev = " + i_rev);
++errCount;
}
++allTestCount;
}
Console.WriteLine("测试结束,错误转换率:" + errCount + "/" + allTestCount);
}
二、C++中使用,以MFC为例
A:应用层 --> ActiveX (封装:自定义数组传入COM组件)
//a1: 申请bstr内存空间
BSTR strFacePoints = ::SysAllocStringLen(NULL, sizeof(M_POINT) * 4);
//a2: 赋值bstr
M_POINT facePoints[4];
memcpy(strFacePoints, facePoints, sizeof(M_POINT) * 4);
//a3: 使用bstr
pEncCtrl->DrawFace(faceStyleId, &strFacePoints, 4);
//a4: 释放bstr内存空间
::SysFreeString(strFacePoints);
B:ActiveX --> 应用层(解包:COM组件传出的自定义数组)
//b1: 申请bstr内存空间
int poCount = pncCtrl->GetFaceCoorCount(faceId);
BSTR strFacePoints = ::SysAllocStringLen(NULL, sizeof(M_POINT) * poCount);
//b2: 使用bstr
pEncCtrl->GetFaceCoor(faceStyleId, &strFacePoints);
//b3: bstr解析出可用数组
M_POINT* facePoints = new M_POINT[poCount];
memcpy(facePoints, strFacePoints, sizeof(M_POINT) * 4);
//b4: 释放bstr内存空间
::SysFreeString(strFacePoints);
//b5: 使用完坐标数组并释放
delete[] facePoints;
三、Pascal-Delphi中使用
delphi中使用比较简单
procedure TFromDemo.TestBSTR();
var arrPoints: array of TPoint; //点数组
var pPointBString: Pwidestring;
begin
SetLength(arrPoints, 2);
arrPoints[0].x := 1;
arrPoints[0].y := 1;
arrPoints[1].x := 2;
arrPoints[1].y := 2;
pPointBString := Pwidestring(@arrPoints);
TEST_BSTR_FUNCTION(pPointBString^);
end;