2011-07-14 107 views
9

我只是想做一些託管/非託管互操作。爲了獲得更多的錯誤信息,我決定註冊的DLL提供的日誌回調:C#P/Invoke:Varargs委託回調


[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public unsafe delegate void LogCallback(void* arg1,int level,byte* fmt); 

這個定義的作品,但我得到「格式%s的大小=%d和得分=%d探測」字符串。 我嘗試添加__arglist關鍵字,但不允許代表使用。對我而言,它並不是那麼戲劇化,但我只是好奇它可以在C#中獲得可變參數參數。 我知道我可以使用C++進行互操作。 所以:有沒有一種方法可以純粹在C#中做到這一點,並且合理?

編輯:對於那些誰仍然沒有得到它:我不宜引進一個可變參數的函數但導出爲一個回調,然後叫我的本地代碼。我一次只能指定一個 - >只有一個可能的重載,__arglist不起作用。

+1

[This](http://www.ownedcore.com/forums/world-of-warcraft/world-of-warcraft-bots-programs/wow-memory-editing/301588-how-hooking-functions-wow- variable-arguments-c.html)可能會有用。 – IllidanS4

回答

4

沒有沒有可能的方式來做到這一點。這是不可能的原因是因爲可變參數列表的工作方式C.

在C變量參數只是作爲額外的參數推到堆棧(在我們的情況下非託管堆棧)。 C沒有在任何地方記錄堆棧上的參數數量,被調用的函數接受其最後一個形式參數(可變參數前的最後一個參數)獲取它的位置並開始從堆棧中彈出參數。

它知道從堆棧中彈出多少變量的方式完全基於約定 - 其他一些參數指示在堆棧上有多少個變量參數。對於printf,它通過解析格式字符串並在每次看到格式代碼時彈出堆棧來實現。看起來你的回調是相似的。

對於CLR來處理它,它必須能夠知道正確的約定來確定它需要拾取多少個參數。您無法編寫自己的處理程序,因爲它需要訪問您無權訪問的非託管堆棧。所以你不能從C#做到這一點。

欲瞭解更多信息,你需要閱讀C調用約定。

0

下面的文章涵蓋了略有不同的場景,可能會有所幫助:

How to P/Invoke VarArgs (variable arguments) in C#

+0

這裏有兩個問題: 1.)我正在EXPORTING函數指針/回調,而不是通過DllImport導入它,我已經嘗試過__arglist:它不適用於代表! 2.)覆蓋所有可能性不起作用,因爲我一次只能註冊一個回調。 – Zotta

+0

@Floste對不起,我想念它看起來的問題。 – Justin

+0

看起來這只是我的另一個問題,沒人能回答^^ – Zotta

0

你需要來自P/invoke編組的支持才能做到這一點。編組員不提供這種支持。因此它不能完成。

1

其實這是可能的CIL:

.class public auto ansi sealed MSIL.TestDelegate 
     extends [mscorlib]System.MulticastDelegate 
{ 
    .method public hidebysig specialname rtspecialname 
      instance void .ctor(object 'object', 
           native int 'method') runtime managed 
    { 
    } 
    .method public hidebysig newslot virtual 
      instance vararg void Invoke() runtime managed 
    { 
    } 
} 
2

這裏是對付它的辦法。它可能適用於您的情況,具體取決於您的回調參數是否與printf函數系列一起使用。

首先,進口vsprintf_vscprintfmsvcrt

[DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] 
public static extern int vsprintf(
    StringBuilder buffer, 
    string format, 
    IntPtr args); 

[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] 
public static extern int _vscprintf(
    string format, 
     IntPtr ptr); 

接下來,IntPtr ARGS指針聲明你的委託:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
public unsafe delegate void LogCallback(
    void* arg1, 
    int level, 
    [In][MarshalAs(UnmanagedType.LPStr)] string fmt, 
    IntPtr args); 

現在,當您的代理是通過本地代碼調用,只需使用vsprintf正確格式化消息:

private void LogCallback(void* data, int level, string fmt, IntPtr args) 
{ 
    var sb = new StringBuilder(_vscprintf(fmt, args) + 1); 
    vsprintf(sb, fmt, args); 

    //here formattedMessage has the value your are looking for 
    var formattedMessage = sb.ToString(); 

    ... 
} 
+0

令人驚歎!不知道你是怎麼想出來的,但它有效! – Eternal21

+0

謝謝,它確實幫助我走上了軌道!不過,我需要一種方法讓它跨越平臺......所以我自己做到了!以下是我的工作,請隨時提供:https://github.com/jeremyVignelles/va-list-interop-demo。 – cube45