-
-
Notifications
You must be signed in to change notification settings - Fork 441
Description
Summary
Silk.NET.Assimp works only for x64 processes and crashes in x86 processes. I assume that the calli IL method does not use Cdecl calling convention to call the native functions.
Steps to reproduce
- Download the new https://github.com/ab4d/Ab3d.DXEngine.Assimp project (assimp importer for Ab3d.DXEngine rendering engine)
- Open the Ab3d.DXEngine.Assimp.net50.sln solution
- Set target platform to x86 for both projects in the solution
- Start the Ab3d.DXEngine.Assimp.Samples project and load a file by clicking on "Load file" button
- 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.' exception is thrown when calling ImportFile.
Comments
As seen from the decompiled code the Silk.NET.Assimp is using calli IL method to call native assimp functions. The parameters are correctly passed to the native method but from the IL code I cannot see if calli is using StdCall or Cdecl (when creating the calli IL method it is possible to specify the calling convention - see https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.ilgenerator.emitcalli?redirectedfrom=MSDN&view=net-5.0#overloads)
C# uses StdCall calling convention by default, but C++ uses Cdecl by default.
I did a quick test and in the x64 process the functions in the native assimp library can be called by using StdCall or Cdecl. But when using x86, then the functions can be only called by using Cdecl (StdCall produces an exception).
The following is the used test code (note that it uses VTable from Silk.NET.Assimp):
`[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate Scene* ImportFileDelegate_cdecl(byte* fileName, uint flags);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate Scene* ImportFileDelegate_stdcall(byte* fileName, uint flags);
private unsafe void DirectAssimpMethodCallTest()
{
var assimp = Silk.NET.Assimp.Assimp.GetApi();
var importFilePtr = assimp.CurrentVTable.Load("aiImportFile");
byte* fileNamePtr = (byte*)SilkMarshal.StringToPtr(@"testModelFile");
//var functionPtr = Marshal.GetDelegateForFunctionPointer(importFilePtr, typeof(ImportFileDelegate));
var functionPtr_cdecl = Marshal.GetDelegateForFunctionPointer<ImportFileDelegate_cdecl>(importFilePtr);
var functionPtr_stdcall = Marshal.GetDelegateForFunctionPointer<ImportFileDelegate_stdcall>(importFilePtr);
var scene1 = functionPtr_cdecl(fileNamePtr, 0);
var scene2 = functionPtr_stdcall(fileNamePtr, 0);
}`
You can copy this code into the MainWindow.xaml.cs in Ab3d.DXEngine.Assimp.Samples project to test it.
Please check the code generator that is used for Silk.NET.Assimp and see if the calli method uses Cdecl call (I was not able to find that in the Silk.NET source).
Additional recommendation
The Silk.NET.Assimp uses a dependency on Ultz.Native.Assimp.
I do not like this because this forces the user to use the native assimp that is provided by the Ultz.Native.Assimp. In my opinion, the Silk.NET.Assimp should be only a wrapper without the native library and the application that uses the wrapper should decide which native library it would use - so the application should depend on Ultz.Native.Assimpis (or any other source of the native library).
What is more, the current version of Ultz.Native.Assimp does not work for .Net framework projects - in this case, the native library is not copied to the output folder (this works for .Net core and .Net 5.0 projects). So a .Net framework project that uses Silk.NET.Assimp also depends on Ultz.Native.Assimp despite this library does not work and the project needs to provide its own native library.