簡體   English   中英

如何從C#調用具有void * callback和object參數的C ++ Dll中的函數

[英]How do I call a function in a C++ Dll from C# that has void* callback and object parameter

我正在嘗試創建一個C dll的包裝器,我試圖調用一個具有回調函數的函數,接收一個對象作為傳回的指針。

.h文件delares

extern int SetErrorHandler(void (*handler) (int, const char*, void*),
                              void *data_ptr);

處理程序是一個回調函數,在發生錯誤時調用,data_ptr是傳遞給你的任何對象(狀態),對於我的應用程序就是這個(當前對象)。

我能夠在一個dll中調用函數,它使用編組的常量類型,如簡單類型字符串,整數等。但我無法弄清楚如何編寫一個指向C#對象的指針作為狀態。

為了通過在這里搜索我已經找到的對象引用傳遞對象引用,否則似乎我需要一個結構類型來能夠編組該函數,所以我創建了一個結構來保存我的對象:

[StructLayout(LayoutKind.Sequential)]
struct MyObjectState
{
   public object state;
}

編輯:我試圖在public object state屬性上放置一個屬性: [MarshalAs(UnmanagedType.Struct, SizeConst = 4)] ,但這會產生相同的錯誤,所以我刪除了它,似乎它無論如何都不會起作用。

該struct包含一個對象屬性,用於保存回調函數的任何對象。

我在C#中聲明了委托如下:

delegate void ErrorHandler(int errorCode, IntPtr name, IntPtr data);

然后我在C#中聲明了導入函數,如下所示:

[DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int SetErrorHandler handler, IntPtr data);

然后我在我的C#代碼中創建了一個回調函數:

void MyErrorHandler(int errorCode, IntPtr name, IntPtr data)
{
    var strName = Marshal.PtrToStringAnsi(name);
    var state = new MyObjectState();
    Marshal.PtrToStructure(data, state);
    Console.WriteLine(strName);
}

我可以調用庫函數如下:

var state = new MyObjectState()
{
    state = this
};
IntPtr pStruct = Marshal.AllocHGlobal(Marshal.SizeOf(state));
Marshal.StructureToPtr(state, pStruct, true);
int ret = SetErrorHandler(MyErrorHandler, pStruct);

調用工作和回調函數被調用,但我無法訪問回調函數中的數據,當我嘗試Marshal.PtrToStructure我收到一個錯誤:

結構不能是值類。

我在這里做了很多搜索,在Marshall上發現了各種各樣的東西,但是沒有任何東西可以幫助我實現這一目標

謝謝。

你使它變得比它需要的更復雜。 您的C#客戶端不需要使用data_ptr參數,因為C#委托已經具有用於維護this指針的內置機制。

因此,您只需將IntPtr.Zero傳遞給委托即可。 在您的錯誤處理程序委托中,您只需忽略data_ptr的值,因為this將是可用的。

如果你不遵循這個描述,這里有一個簡短的程序來說明我的意思。 請注意MyErrorHandler是一個充當錯誤處理程序的實例方法,並且可以修改實例數據。

class Wrapper
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate void ErrorHandler(int errorCode, string name, IntPtr data);

    [DllImport("somedll.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern int SetErrorHandler(ErrorHandler handler, IntPtr data);

    void MyErrorHandler(int errorCode, string name, IntPtr data)
    {
        lastError = errorCode;
        lastErrorName = name;
    }

    public Wrapper()
    {
        SetErrorHandler(MyErrorHandler, IntPtr.Zero);
    }            

    public int lastError { get; set; }
    public string lastErrorName { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Wrapper wrapper = new Wrapper();
    }
}

很可能有辦法做到這一點,但我很久以前就放棄了。 我提出的解決方案略顯苛刻,但它非常有效並適用於我拋出的所有內容:

C# - >托管C ++ - >本機調用

這樣做你最終在托管C ++中編寫一個小包裝器,這是一種痛苦,但我發現它比所有編組代碼更有能力,更少痛苦。

老實說雖然我有點希望有人給出一個不回避的答案,但我已經在這方面掙扎了很長一段時間。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM