2011-10-20 111 views
1

我需要從C#中進入C++並帶回一個結構的二維數組。我有一切設置,如果我附加一個調試器一切似乎是正確的,除了我的二維數組不適當編組。如果我在調用本地方法之前使用值加載它,然後從本機端查看數組,我會在VS的監視窗口中看到很多「無效」指針。然後C++代碼繼續前進,並使用值加載數組,但在編組回到C#期間,出現內存訪問衝突。我不想做這個作爲1d數組。從C#到本機C++的統一二維數組結構

這裏是我的C++結構和方法的定義:

struct DoubleStringStruct 
{ 
    BSTR Value; 
    BSTR NumberFormat; 
}; 

HRESULT WINAPI NativeArrayHandler(LONG rMax, LONG cMax, DoubleStringStruct** values) 
{ 
    for(LONG rn=1; rn <= rMax; rn++) 
    { 
     for (LONG cn = 1; cn <= cMax; cn++) 
     { 
       DoubleStringStruct s; 
       s.Value = _wcsdup(L"Test"); 
       s.NumberFormat = _wcsdup(L"Test"); 
       values[rn][cn] = s; 
     } 
    } 

    return S_OK; 
} 

和我的C#代碼:

[StructLayout(LayoutKind.Sequential)] 
public struct DoubleStringStruct 
{ 
    [MarshalAs(UnmanagedType.BStr)] 
    public string value; 
    [MarshalAs(UnmanagedType.BStr)] 
    public string numberFormat; 
} 


[System.Runtime.InteropServices.DllImport(c_dllName)] 
public static extern void NativeArrayHandler(int hMax, int cMax, DoubleStringStruct[,] args); 

public void sometMethod() 
{ 
    DoubleStringStruct[,] someDSS= new DoubleStringStruct[4,3]; 

    NativeArrayHandler(4, 3, someDSS); 
} 
+0

FWIW - 我注意到在陣列尺寸參數是通過正確地傳來的本機代碼。 – Dlongnecker

+0

嘗試使用數組([[] []')而不是多維數組([[,]')並查看是否有效。 – Ryan

+0

Hrmm ...我認爲這是不可能的,直到我看到這個人,但我希望有更多的東西「automagic」http://stackoverflow.com/questions/6327196/problem-marshalling-c-sharp -jagged陣列到C – Dlongnecker

回答

0

HRM,以及漢斯帕桑特幫助我得到這個答案,所以道具給他。

有三個問題,我的代碼

1)_wcsdup返回一個wchar_t的*,但我的結構包含BSTR,這實在是一個WHCAR *

2)編組不會創建一個二維數組我們,而不是一個必須以有趣的方式索引的1d數組。下面注意。

3)我需要確保我在本地代碼中創建的任何內存都被自己或Marshaller清理乾淨。例如,幾乎所有我在問題中使用的本機內存都不會被釋放,導致巨大的內存泄漏。目前,當本機代碼返回到託管代碼時,我失去了需要釋放的所有內存指針。我將這個調用轉換爲本地代碼時解決了這個問題。本地代碼執行其工作,將功能調用回託管,返回,並允許本機代碼做家務。做這種家務活動的一個簡單方法是使用CComSafeArray和CComBSTR的功能,它們將自行管理。 (我知道我應該能夠簡單地將CComSafeArray傳遞給編組人員,他們將在.net代碼中清理乾淨,但我一直無法弄清楚如何做到這一點)。

不幸的是,二維數組的編組需要自定義封送處理,這會導致太多的COM調用滿足我的口味。因此,根據Hans Passant的建議,我編了一個一維數組並對其進行索引。此外,由於時間限制,我爲DoubleStringStruct中的每個字符串創建了一個數組,但我可以使DoubleStringStruct COMVisible,然後我可以將它編組在單個數組中。

下面是我最終的代碼。

extern "C" __declspec(dllexport) 
HRESULT WINAPI NativeArrayHandler(LONG rMax, LONG cMax, void (WINAPI*callback)(SAFEARRAY*, SAFEARRAY*)) 
{ 
    CComSafeArray<BSTR> valuesArr = CComSafeArray<BSTR>(rMax*cMax); 
    CComSafeArray<BSTR> formatsArr = CComSafeArray<BSTR>(rMax*cMax); 


    for(LONG rn=0; rn < rMax; rn++) 
    { 
     for (LONG cn = 0; cn < cMax; cn++) 
     { 
       int index = cMax * rn + cn; 
       valuesArr[index] = CComBSTR(L"Test"); 
       formatsArr[index] = CComBSTR(L"Test"); 
     } 
    } 

    callback(valuesArr, formatsArr); 

    valuesArr.Destroy(); 
    formatsArr.Destroy(); 

    return S_OK; 
} 

和C#

static void Main(string[] args) 
{ 
     NativeArrayHandler(4, 3, (v, f) => { printArrays(4, 3, v, f); }); 
} 


public static void printArrays(int rmax, int cmax, string[] valuesArr, string[] formatsArr) 
{ 
    // can print the arrays in managed code here 
} 


    [System.Runtime.InteropServices.DllImport("dll location")] 
    public static extern void NativeArrayHandler(int hMax, int cMax, NativeArrayHandlerCallback cb); 


    public delegate void NativeArrayHandlerCallback(
     [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] string[] arr1, 
     [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] string[] arr2);