簡體   English   中英

C#互操作AccessViolationException與托管回調

[英]C# interop AccessViolationException with managed callback

我有一個非托管的dll,它分配一個結構並傳遞指向該結構的指針。 我已經創建了相當於該結構的ac#,並且可以愉快地編譯並運行我的代碼以利用它。 該結構中有一個可選的指針,可讓您連接一個函數指針,該指針將在非托管代碼運行時被調用。 當我嘗試將托管委托掛接到結構的指針並將其傳遞回時,會出現AccessViolationException異常。 我想念什么?

一些細節:

非托管C代碼:

typedef struct MyStruct {
   :
   :
   int flags
   :
   int (*cback)(MyStruct *s, Other *o)
   :
} MyStruct;

相當於C#:

[StructLayout(LayoutKind.Sequential)]
public class MyStruct
{
   :
   :
   [MarshalAs(UnmanagedType.I4)]
   public int flags;
   :
   public IntPtr cback;
   :
};

我有一個指向非托管結構的指針

managedMyStruct = (MyStruct)
    Marshal.PtrToStructure(pUnmanagedMyStruct, typeof(MyStruct));                    

managedMyStruct.cback = 
    Marshal.GetFunctionPointerForDelegate(ManagedDelegateRef);                    

// Update pointer
Marshal.StructureToPtr(managedMyStruct, pUnmanagedStruct, true);

當我將pUnmanagedStruct傳遞給最終調用cback的非托管函數時,我的cback委托被調用一次,應用程序因AccessViolationException而崩潰。

任何線索都感激不盡。

一種

ManagedDelegateRef指向什么? 靜態方法還是實例方法? 如果是實例方法,請確保該實例不會被垃圾回收。

我遇到了類似的事情:我向非托管代碼傳遞了指向托管回調的指針,當調用該回調時,該函數運行了一次,然后程序崩潰了。

我沒有獲得AccessViolationException-我沒有任何異常-但也許您的問題的原因與我的相同。

解決我的問題的方法如下:

根據[1],有不同的函數調用約定:較舊的__cdecl和較新的__stdcall 非托管C / C ++默認情況下使用__cdecl ,而C#默認情況下使用__stdcall

我猜您的非托管代碼正在使用默認的__cdecl約定。 如果可以在非托管代碼中更改約定,則可能是您的解決方法。

不幸的是,我使用的是第三方DLL,無法更改其中的非托管調用約定。 我的程序需要做的是告訴C#我正在傳遞的委托是使用__cdecl約定。

不幸的是,沒有辦法直接告訴C#。 (您可能會認為有一個屬性可以使用,但顯然MS尚未為C#實現一個屬性,盡管我認為托管C ++具有一個屬性)。

為了解決這個問題,需要使用一些技巧:

需要使用Visual Studio命令提示符中的ildasm命令反編譯程序(DLL / EXE)的輸出:

cmd>  ildasm /out=output.il OUTPUT.EXE

然后,在IL代碼中將一個屬性添加到委托的Invoke方法中,以使其使用__cdecl調用約定:

// output.il
.
.
.

.class public auto ansi sealed NAMESPACE.ManagedDelegate
       extends [mscorlib]System.MulticastDelegate
{
  .custom instance void NAMESPACE.UseCCallingConventionAttribute::.ctor() = ( 01 00 00 00 ) 
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor(object 'object',
                               native int 'method') runtime managed
  {
  } // end of method ManagedDelegate::.ctor

  .method public hidebysig newslot virtual 
          instance void  Invoke(native int pUser,
                                int32 state) runtime managed
  {
  } // end of method ManagedDelegate::Invoke

  .method public hidebysig newslot virtual 
          instance class [mscorlib]System.IAsyncResult 
          BeginInvoke(native int pUser,

.
.
.

成為:

.
.
.

.class public auto ansi sealed NAMESPACE.ManagedDelegate
       extends [mscorlib]System.MulticastDelegate
{
  .custom instance void NAMESPACE.UseCCallingConventionAttribute::.ctor() = ( 01 00 00 00 ) 
  .method public hidebysig specialname rtspecialname 
          instance void  .ctor(object 'object',
                               native int 'method') runtime managed
  {
  } // end of method ManagedDelegate::.ctor

  .method public hidebysig newslot virtual 
          instance void #####modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)##### Invoke(native int pUser,
                                int32 state) runtime managed
  {
  } // end of method ManagedDelegate::Invoke

  .method public hidebysig newslot virtual 
          instance class [mscorlib]System.IAsyncResult 
          BeginInvoke(native int pUser,

.
.
.

沒有哈希。 (在此示例中,委托類型的名稱為ManagedDelegate -我不確定您的類型名稱是什么。)

(注意:[1]和[2]建議在代理上放置一個占位符屬性,以便您可以輕松地在.il文件中找到該方法; UseCCallingConventionAttribute是我的。)

然后使用ilasm重新編譯代碼文件:

cmd>  ilasm output.il

使用/DLL/EXE ,取決於您的輸出類型-默認為/EXE

這就是適用於我的項目的修復程序。

整個過程在[1]中有更詳細的概述,有人在[2]中發布了Perl腳本來完成。 我還沒有實現自動化,並且是VS n00b,所以我不知道是否可以將其作為構建的一個步驟來添加,但是確實存在。

希望這可以幫助。

[1] http://www.codeproject.com/KB/cs/cdeclcallback.aspx

[2] http://www.dotnet247.com/247reference/msgs/17/87210.aspx

暫無
暫無

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

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