[英]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.