VB6到C ++ / CLI包装托管C#方法

时间:2015-10-15 23:36:07

标签: c# vb6 c++-cli

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#方法,以便它可以作用于内存中的非托管结构?在此先感谢大家。

1 个答案:

答案 0 :(得分:1)

如果要将结构映射到现有的非托管内存,则它不能包含托管类型(对象引用或不满足相同条件的结构)。

string是托管类型,包含UTF-16字符。您的数据结构包含8位C ++ char。所以它不合适。

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;
}