2010-07-22 26 views
3

我有以下的簽名非託管C++函數:調用從C#C++函數 - 不平衡棧

int function(char* param, int ret) 

我試圖把它從C#:

unsafe delegate int MyFunc(char* param, int ret); 

...

int Module = LoadLibrary("fullpathToUnamanagedDll"); 
IntPtr pProc = GetProcAddress(Module, "functionName"); 
MyFunc func = (MyFunc)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(pProc, typeof(MyFunc)); 

unsafe 
{ 
    char* param = null; 
    int ret = 0; 
    int result = func(param, ret); 
} 

據我可以從舊的C++項目規範告訴兩個null爲param和0代表ret是該函數的有效輸入。當我嘗試調用它,它似乎工作,但在離開我得到以下錯誤:

PInvokeStackImbalance was detected

A call to PInvoke function '...::Invoke' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

我已經試過相當多的東西,我能想到關(不安全是不得已而爲之),但我無法找到以任何方式運行該功能而不會產生不平衡的堆棧。有什麼我可以嘗試嗎?

+0

您是否必須將其作爲委託導入?一個正常的「靜態外部」聲明會更容易。 – 2010-07-22 09:00:11

+0

@亨克Holterman:我想不是,我收到了一個同事的代碼,我認爲他有這樣做的理由。但是,我必須在運行時動態加載函數,而不必知道Dll名稱(路徑)或函數名稱。儘管如此,我在設計時也知道函數簽名。它能以不同的方式完成嗎? – 2010-07-22 10:47:22

+0

'動態'是一個很好的理由,我認爲@leppies的答案是你必須去的地方。 – 2010-07-22 11:01:03

回答

2

IIRC,您需要用調用約定修飾委託簽名。不幸的是,這隻能通過IL完成,或者使用Reflection.Emit生成存根。

你可以試試這個:

protected static Type MakeDelegateType(Type returntype, List<Type> paramtypes) 
{ 
    ModuleBuilder dynamicMod = ... ; // supply this 

    TypeBuilder tb = dynamicMod.DefineType("delegate-maker" + Guid.NewGuid(), 
     TypeAttributes.Public | TypeAttributes.Sealed, typeof(MulticastDelegate)); 

    tb.DefineConstructor(MethodAttributes.RTSpecialName | 
     MethodAttributes.SpecialName | MethodAttributes.Public | 
     MethodAttributes.HideBySig, CallingConventions.Standard, 
     new Type[] { typeof(object), typeof(IntPtr) }). 
     SetImplementationFlags(MethodImplAttributes.Runtime); 

    var inv = tb.DefineMethod("Invoke", MethodAttributes.Public | 
     MethodAttributes.Virtual | MethodAttributes.NewSlot | 
     MethodAttributes.HideBySig, 
     CallingConventions.Standard ,returntype,null, 
     new Type[] 
     { 
      // this is the important bit 
      typeof(System.Runtime.CompilerServices.CallConvCdecl) 
     }, 
     paramtypes.ToArray(), null, null); 

    inv.SetImplementationFlags(MethodImplAttributes.Runtime); 

    var t = tb.CreateType(); 
    return t; 
} 
+0

對不起,但我需要一些幫助來理解代碼。我的Dll文件(或路徑)和函數名稱在哪裏進來? – 2010-07-22 11:11:01

+0

@Rekreativc:就像你做的一樣。我的方法得到的結果是在檢索函數指針時發送的。也就是說,它將是Delegate類型的,你需要在其上調用'DynamicInvoke'。你可以製作另一個Reflection.Emit包裝來直接調用它。 – leppie 2010-07-22 11:14:39

+1

@Rekreativc:是的,然後在結果調用DynamicInvoke。 – leppie 2010-07-22 11:38:47

-1

有兩件事情需要注意的:在C#位和你的DLL和如何之間的調用約定的字符*數據通過這個接口編組。如果你得到這些錯誤之一,那麼你會得到堆棧腐敗投訴。在定義你的界面的時候,如果你可以將數據塊的大小限制在固定的範圍內,那就更容易了。設置最大字符串長度。

這裏的靜態版本,其中DLL名稱是固定的,你的字符串作爲一個byte []處理,並僅限於2K字節的大小,你可以從這個出圖中的動態版本:

private const string MYDLL = @"my.dll"; 

    [DllImport(MYDLL, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)] 
    public static extern int DataBlockDownload([MarshalAs(UnmanagedType.U4)] int A, [MarshalAs(UnmanagedType.LPArray, SizeConst = 2048)] byte[] B, [MarshalAs(UnmanagedType.U4)] int C); 

    // NOTE: The data block byte array is fixed at 2Kbyte long!! 
public delegate int DDataBlockCallback([MarshalAs(UnmanagedType.U4)] int A, [MarshalAs(UnmanagedType.LPArray, SizeConst = 2048)] byte[] B, [MarshalAs(UnmanagedType.U4)] int C); 

如果您想保留字符類型,您可能還想指定您使用的字符集,如上所述。

如果您的C++代碼作爲參數進入C++代碼,或者C++代碼將它傳遞迴託管的世界,那麼您不會說您正在處理的char *數據。閱讀C#關鍵字ref和out,以避免char *類型和unsafe修飾符。

隨着一些谷歌搜索,你應該能夠弄明白。

+0

問題是你不能在C#中爲委託指定一個調用約定,這就是導致問題的原因。 – leppie 2010-07-22 11:15:44

7

我知道這個問題是一歲多了,但不是動態構建類型更簡單的方法是在你的委託使用UnmanagedFunctionPointer屬性來聲明調用約定,如:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
unsafe delegate int MyFunc(char* param, int ret); 

MSDN

Controls the marshaling behavior of a delegate signature passed as an unmanaged function pointer to or from unmanaged code.

+1

+1:謝謝,這**通過'typedef void(* InvokeCallack)(void * opaque);'回調定義解決了**問題。實際的回調是一種C++編譯方法。我在C++/CLI中定義了一個具有簽名'delegate void InvokeCallack(IntPtr opaque)'的委託,''用你的屬性'[UnmanagedFunctionPointer(CallingConvention :: Cdecl)]'現在我沒有問題在UI線程中調用委託使用Control :: BeginInvoke。奇怪的是,相同的代碼在沒有該屬性的VS2008中工作。在VS2010中,屬性是必需的。 – ceztko 2012-04-14 12:16:00