如何使用 C# 建立 cdecl callback 給 unmanaged code 使用?
SQLite.NET 所採用的方法如下:
- 用 ildasm 反組譯 DLL 產生 IL 碼
- 用 Perl script 為 delegate 的 Invoke() 加上 modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)
- 再用 ilasm 組譯出新的 DLL
參考下列詳細的方法 (摘自 SQLite.NET 的文件檔):
The C# does not allow you to specify the calling convention of the callback. It is just one of the C# limitations. IL, managed C++ and the runtime itself supports the cdecl calling convention for delegates through modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) on the internal Invoke method of the delegate. Run ildasm on a small example in managed C++ if you want to know the exact syntax. You can use the following trick if you want to write all your code in C#: - Create a private placeholder attribute to mark your C# delegates that needs cdecl calling convention - Compile your C# to an assembly - Use the ildasm dissasembler to store the resulting assembly as raw IL. - Modify the IL to change the calling convention on the delegates marked with the placeholder attribute - Use ilasm to compile the modified raw IL again. The whole process can be automated. You can find example of how to automate the process in the Shared Source CLI samples. The Shared Source CLI (aka Rotor) can be downloaded from http://msdn.microsoft.com/net/sscli . The following files have the interesting stuff about the cdecl calling convention for delegates: sscli\docs\techinfo\native_managed_interop.html sscli\samples\pigui\tk\makefile.inc sscli\samples\pigui\tk\callconvattribute.cs sscli\samples\pigui\tk\callconvhack.pl sscli\samples\pigui\tk\tclnative.cs You will need perl (e.g. from http://www.activestate.com) if you want to use the script as is. It should be fairly easy to rewrite callconvhack.pl in any other language though. Here are the code snipets if you are on a slow link and don't have time to download 10+MB Rotor tarball at the moment: makefile.inc: -------------------- .... csc /out:myassembly.dll callconvattribute.cs mydelegate.cs ... ildasm myassembly.dll /out:myassembly.il1 perl callconvhack.pl myassembly.il2 ilasm /DLL /QUIET myassembly.il2 .... -------------------- mydelegate.cs: -------------------- .... [CallConvCdecl] internal delegate int MyDelegate(int param); .... -------------------- callconvattribute.cs: -------------------- // cdecl calling convetion for delegates - the signature of delegates // marked with this attribute is changed to cdecl in callconvhack.pl using System; [Serializable, AttributeUsage (AttributeTargets.Delegate)] public sealed class CallConvCdeclAttribute : Attribute { public CallConvCdeclAttribute() { } } -------------------- callconvhack.pl: -------------------- # change references to CallConvAttribute into modopt([mscorlib]System.Runtime.CompilerServices.*) while() { if (m/\.class .* CallConv.*Attribute/) { $incallconvattributeimpl=1; next; } if (m/\/\/ end of class CallConv.*Attribute/) { $incallconvattributeimpl=0; next; } if (m/\.custom instance void CallConv(.*)Attribute/) { $pendingcallconv = $1; next; } if ($pendingcallconv) { if (m/.*Invoke\(.*/) { print "modopt([mscorlib]System.Runtime.CompilerServices.CallConv" . $pendingcallconv . ")\n"; $pendingcallconv = ""; } } print unless $incallconvattributeimpl; } -------------------- This posting is provided "AS IS" with no warranties, and confers no rights.