VB6 到 C++/CLI 围绕托管 C# 方法的包装器
VB6 to C++/CLI wrapper around managed C# method
关于 SO 和网络(例如 pinvoke.net)有大量描述从托管代码调用非托管代码的重要信息。我处于一种独特的情况,我需要为旧版 VB6 应用程序实施 API,由于业务原因无法更改。为了简洁起见,假设 VB6 应用执行以下操作。
'Legacy VB6
Public Declare Function GetData Lib "NewDLL" (ByVal szDataID As String,
ByRef DataStruct As TDataStruct) As Long
Type TDataStruct
szSKU As String * 10
szTypeInfo As String * 20
End Type
这是我处理它的方法。
C++/CLI 实现。
//NewDLL.h
typedef struct DATASTRUCT
{
char szSKU [10];
char szTypeInfo [20];
];
//NewDLL.cpp - C++/CLI
#include "NewDLL.h"
using namespace System;
extern "C" __declspec(dllexport)
Int32 __stdcall GetData(LPTSTR szDataID, DATASTRUCT* dataStruct)
{
String^ m_DataID;
m_DataID = gcnew String(szDataID);
NewDotNetDll::GetDataClass::GetData(m_DataID, dataStruct);
return (true);
}
.NET 实现。
//DataStruct.cs
using etc...
using System.Runtime.InteropServices;
namespace NewDotNetDll
{
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
public struct DataStruct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string SkuID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string TypeInfo;
}
}
//GetDataClass.cs
using etc...
using System.Runtime.InteropServices;
namespace NewDotNetDll
{
public class GetDataClass
{
public static Int32 GetData(
[MarshalAs(UnmanagedType.LPStr)] string skuID,
[Out, MarshalAs(UnmanagedType.LPStruct)] DataStruct dataStruct)
{
Int32 rc = 0;
//to do...
return rc;
}
}
}
问题 - 如何通过引用将 VB 的 TDataStruct 传递给我的 C# 方法,以便它可以作用于内存中的非托管结构?提前谢谢大家。
如果要将结构映射到现有的非托管内存,它不能包含托管类型(对象引用或不满足相同条件的结构)。
string
是托管类型,它包含 UTF-16 字符。您的数据结构包含 8 位 C++ char
s。所以不合适。
byte[]
也是 托管类型,因为数组实际上是对 C# 中对象的引用。所以你也不能使用那个。
让我们用 fixed-size buffers:
来处理
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public unsafe struct DataStruct
{
public fixed byte SkuID[10];
public fixed byte TypeInfo[20];
}
现在我们可以在 C# 中声明一个指向结构的指针:
public unsafe static Int32 GetData(string skuID, DataStruct *dataStruct)
{
dataStruct->SkuID[0] = 65;
dataStruct->SkuID[1] = 0;
return 42;
}
此时不需要使用 MarshalAsAttribute
。
这里有一个字符串处理的例子(将skuID
复制到dataStruct->SkuID
):
public unsafe static Int32 GetData(string skuID, DataStruct *dataStruct)
{
var skuIdBytes = Encoding.ASCII.GetBytes(skuID);
if (skuIdBytes.Length >= 10)
throw new ArgumentOutOfRangeException(nameof(skuID), "skuID is too long");
Marshal.Copy(skuIdBytes, 0, new IntPtr(dataStruct->SkuID), skuIdBytes.Length);
dataStruct->SkuID[skuIdBytes.Length + 1] = 0;
return 42;
}
关于 SO 和网络(例如 pinvoke.net)有大量描述从托管代码调用非托管代码的重要信息。我处于一种独特的情况,我需要为旧版 VB6 应用程序实施 API,由于业务原因无法更改。为了简洁起见,假设 VB6 应用执行以下操作。
'Legacy VB6
Public Declare Function GetData Lib "NewDLL" (ByVal szDataID As String,
ByRef DataStruct As TDataStruct) As Long
Type TDataStruct
szSKU As String * 10
szTypeInfo As String * 20
End Type
这是我处理它的方法。
C++/CLI 实现。
//NewDLL.h
typedef struct DATASTRUCT
{
char szSKU [10];
char szTypeInfo [20];
];
//NewDLL.cpp - C++/CLI
#include "NewDLL.h"
using namespace System;
extern "C" __declspec(dllexport)
Int32 __stdcall GetData(LPTSTR szDataID, DATASTRUCT* dataStruct)
{
String^ m_DataID;
m_DataID = gcnew String(szDataID);
NewDotNetDll::GetDataClass::GetData(m_DataID, dataStruct);
return (true);
}
.NET 实现。
//DataStruct.cs
using etc...
using System.Runtime.InteropServices;
namespace NewDotNetDll
{
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
public struct DataStruct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string SkuID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string TypeInfo;
}
}
//GetDataClass.cs
using etc...
using System.Runtime.InteropServices;
namespace NewDotNetDll
{
public class GetDataClass
{
public static Int32 GetData(
[MarshalAs(UnmanagedType.LPStr)] string skuID,
[Out, MarshalAs(UnmanagedType.LPStruct)] DataStruct dataStruct)
{
Int32 rc = 0;
//to do...
return rc;
}
}
}
问题 - 如何通过引用将 VB 的 TDataStruct 传递给我的 C# 方法,以便它可以作用于内存中的非托管结构?提前谢谢大家。
如果要将结构映射到现有的非托管内存,它不能包含托管类型(对象引用或不满足相同条件的结构)。
string
是托管类型,它包含 UTF-16 字符。您的数据结构包含 8 位 C++ char
s。所以不合适。
byte[]
也是 托管类型,因为数组实际上是对 C# 中对象的引用。所以你也不能使用那个。
让我们用 fixed-size buffers:
来处理[StructLayout(LayoutKind.Sequential, Pack = 4)]
public unsafe struct DataStruct
{
public fixed byte SkuID[10];
public fixed byte TypeInfo[20];
}
现在我们可以在 C# 中声明一个指向结构的指针:
public unsafe static Int32 GetData(string skuID, DataStruct *dataStruct)
{
dataStruct->SkuID[0] = 65;
dataStruct->SkuID[1] = 0;
return 42;
}
此时不需要使用 MarshalAsAttribute
。
这里有一个字符串处理的例子(将skuID
复制到dataStruct->SkuID
):
public unsafe static Int32 GetData(string skuID, DataStruct *dataStruct)
{
var skuIdBytes = Encoding.ASCII.GetBytes(skuID);
if (skuIdBytes.Length >= 10)
throw new ArgumentOutOfRangeException(nameof(skuID), "skuID is too long");
Marshal.Copy(skuIdBytes, 0, new IntPtr(dataStruct->SkuID), skuIdBytes.Length);
dataStruct->SkuID[skuIdBytes.Length + 1] = 0;
return 42;
}