2015-10-15 52 views
1

在SO和网上有很多很棒的信息(例如pinvoke.net),它描述了从托管代码调用非托管代码。我处于一种独特的情况,我需要为原有的VB6应用程序实现一个API,该应用程序由于商业原因而无法更改。为了简洁起见,可以说VB6应用程序执行以下操作。VB6到托管C#方法的C++/CLI包装器

'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

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

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

下面是字符串处理(复制到skuIDdataStruct->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; 
} 
+0

卢卡斯,太感谢你了。我仍然遇到一个小问题 - 当我构建我的C++/CLIP包装器时,错误C2664不能将参数2从'DATASTRUCT *'转换为'NewDotNetDll :: DataStruct *'。我的托管DataStruct已根据您的建议进行了修改 - 它不包含托管类型。 – 8Pod

+0

哦,是的,我忽略了这一点。你必须施放:'reinterpret_cast (dataStruct)'应该做到这一点(因为类型与编译器无关)。 –

+0

卢卡斯 - 非常感谢你! – 8Pod

相关问题