2013-11-03 197 views
1

期間唯一已知我有以下問題:從C++傳遞一個結構爲C#當結構運行時

我有從非託管C++ DLL調用函數的C#應用​​程序。在dll中有一個初始化函數,它在C#和C++(基本上是一系列值和它們的類型)之間創建一個接口,並存儲在一個結構中。

之後,有一個C#應用程序發送到dll的回調函數,dll每隔一段時間調用一次,它會返回一個結構變量(或字節數組),就像它在界面中定義的那樣。

我的問題:你將如何傳遞和編組這個結構?是否有可能傳遞結構本身,或者我應該傳遞一個字節數組?

如果你傳遞一個字節數組,你會在返回到C#應用程序時編組它嗎?

我現在所擁有的:

在C#應用程序:

封送回調函數:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public delegate void ProcessOutputDelegate(???); // not sure what should be here. 

導入的DLL功能:

[DllImport("MyDLL.dll", CallingConvention=CallingConvention.Cdecl)] 
public static extern void Test(ProcessOutputDelegate ProcessOutput); 

調用的dll功能:

ProcessOutputDelegate process = new ProcessOutputDelegate(ProcessOutput); 
new thread(delegate() { Test(process); }).Start(); 

處理中的輸出:

public void ProcessOutput(???) 
{ 
    // Assume we have a list of values that describes the struct/bytes array. 
} 

在C++的dll我有以下結構(這是一個例子,作爲一個不同的dll可以在不同的運行時被調用):

struct 
{ 
    int x; 
    double y; 
    double z; 
} typedef Interface; 

並且由C#應用程序調用的函數:

__declspec(dllexport) void Test(void (*ProcessOutput)(Interface* output)) 
{ 
    int i; 
    Interface* output = (Interface*)malloc(sizeof(Interface)); 

    for (i = 0; i < 100; i++) 
    { 
     sleep(100); 
     output->x = i; 
     output->y = i/2; 
     output->z = i/3; 
     ProcessOutput(output); // or generate a bytes array out of the struct 
    } 
} 

編輯:

C#應用程序是一個通用的GUI,它假設顯示一些C++ dll執行一些繁重的計算。在初始化過程中,dll告訴GUI關於應該呈現的變量(及其類型),並且根據這些方向構建GUI(再次,計算和變量可以改變,並且值可以是整數,浮點數,字符...)。之後,dll會運行,並在每個時間步驟中調用回調函數來更新GUI。這應該適用於實現此想法的任何dll:生成一個接口,然後根據此接口發送信息。

+1

我不理解所在的C++結構 「可能會發生變化」 的部分。在普通的C++中,結構體是在編譯時定義的。它們在運行時不會奇蹟般地改變(當然它們的內容,但不是它們的結構)。你能澄清一下嗎? – Mat

+0

@Mat True,其含義是可以在每個運行時調用不同的dll,我會添加一個說明。每個DLL都會實現「測試」功能,但可能會構建不同的結構並執行不同的操作。 –

+0

所以你試圖對不同的C++ DLL使用相同的C#接口,這可能「返回」完全不同的東西?我很抱歉你的整個事情聽起來很奇怪。沒有具體的例子說明你正在申請這個項目,這將是很難(我認爲)提供好的建議。如果它完全是隨機的東西,可能與XML或JSON接口。 – Mat

回答

1

我不知道,這是否是對您的問題的直接回答,但是我通過以下方式解決了問題。

  1. 你打電話佔用資源的DLL,並得到一個IntPtr:

    (...) 
    IntPtr res = Native.GetResource(); 
    
  2. 然後,你實例爲這個IntPtr的通用包裝:

    ResourceWrapper rw = new ResourceWrapper(res); 
    
  3. 如果你想訪問在結構的特定字段中,可以在包裝上調用適當的方法:

    int field = rw.GetField(); 
    
  4. 包裝程序調用的DLL中的函數:

    (...) 
    int result = Native.GetField(res); 
    
  5. DLL 「重新objectizes」 的號召:

    __declspec(dllexport) int __stdcall GetField(MyStructure * s) 
    { 
        return s->GetField(); 
    } 
    
  6. 結構得到的數據(或拋出一個異常或設置錯誤國旗或返回錯誤等)

    int MyStructure::GetField() 
    { 
        return this->field; 
    } 
    

這種解決方案需要進行以下更改:

  • 你的數據結構應該由同一基類派生
  • 如果可能的話,他們應該成爲教學班,虛擬方法允許訪問各自不同的領域
  • 您必須實施某種安全機制,檢查是否可以訪問特定字段。最簡單的是以下內容:

    __declspec(dllexport) BOOL __stdcall GetField(MyStruct * s, int & result) 
    { 
        result = 0; 
        try 
        { 
         result = s->GetField(); 
        } 
        catch(...) 
        { 
         return FALSE; 
        } 
    
        return TRUE; 
    } 
    

    和:

    protected class Native 
    { 
        [DllImport("MyDll.dll")] 
        [return: MarshalAs(UnmanagedType.Bool)] 
        public static extern bool GetField(IntPtr res, out int result); 
    } 
    

    和:

    // Wrapper 
    int GetField() 
    { 
        int result; 
        if !(Native.GetField(res, out result)) 
         throw new InvalidOperationException("Resource does not support field!"); 
    }