AccessViolationException using unmanaged C++ DLL - c#

I'm trying, for the first time, to use an unmanaged C++ DLL ("res_lib") in a C# application. I've used cppsharp to generate the PInvoke code: for example, one of the functions/methods I'm trying to call is get_system_snapshot. From the .h file, this is defined as
SYS_INT SYS_ERR get_system_snapshot(SNAPSHOT_PARMS* snapshotp);
SYS_INT and SYS_ERR equate to a int32_t. SNAPSHOT_PARMS is
typedef struct SNAPSHOT_PARMS
{
SYS_ULONG size;
SYS_UINT count;
SYS_CHAR serial_no[600];
} SYS_PACK_DIRECTIVE SYS_SNAPSHOT_PARMS;
cppsharp has turned this into the following code snippets:
DllImport
[SuppressUnmanagedCodeSecurity]
[DllImport("res_lib", CallingConvention = CallingConvention.StdCall,
EntryPoint="get_system_snapshot")]
internal static extern int GetSystemSnapshot(IntPtr snapshotp);
Object
public unsafe partial class SNAPSHOT_PARMS : IDisposable
{
[StructLayout(LayoutKind.Explicit, Size = 608)]
public partial struct __Internal
{
[FieldOffset(0)]
internal uint size;
[FieldOffset(4)]
internal uint count;
[FieldOffset(8)]
internal fixed sbyte serial_no[600];
[SuppressUnmanagedCodeSecurity]
[DllImport("res_lib", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.ThisCall,
EntryPoint="??0SNAPSHOT_PARMS##QAE#ABU0##Z")]
internal static extern global::System.IntPtr cctor(global::System.IntPtr instance, global::System.IntPtr _0);
}
}
public SNAPSHOT_PARMS()
{
__Instance = Marshal.AllocHGlobal(sizeof(global::res_lib.SNAPSHOT_PARMS.__Internal));
__ownsNativeInstance = true;
NativeToManagedMap[__Instance] = this;
}
Main code
static void Main(string[] args)
{
SNAPSHOT_PARMS p = new SNAPSHOT_PARMS();
var result = res_lib.res_lib.GetSystemSnapshot(p);
}
public static unsafe int GetSystemSnapshot(global::res_lib.SNAPSHOT_PARMS snapshotp)
{
var __arg0 = ReferenceEquals(snapshotp, null) ? global::System.IntPtr.Zero : snapshotp.__Instance;
var __ret = __Internal.GetSystemSnapshot(out __arg0);
return __ret;
}
When calling the function, I get the infamous:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I've tried changing the CallingConvention from StdCall to Cdecl, introducing [In] and [Out] to the DllImport etc, but all to no avail. Can anyone see anything obviously wrong with the code - as may be apparent, this is all new to me, and perhaps I'm asking a bit much for cppsharp to generate code that won't need tweaked.
EDIT The original C++ documentation has an example, where the struct is initialised by
#define INIT_STRUCT(struct_p) { memset(struct_p, 0, sizeof(*(struct_p))); (struct_p)->size = sizeof(*(struct_p)); }
and is used
SNAPSHOT_PARMS snapshot_parms;
SYS_ERR result;
INIT_STRUCT(&snapshot_parms);
result = get_system_snapshot(&snapshot_parms);

From the C++ declarations, this should suffice:
[StructLayout(LayoutKind.Sequential)]
unsafe struct SNAPSHOT_PARMS {
public int size;
public int count;
public fixed byte serial_no[600];
}
[DllImport("res_lib", EntryPoint = "get_system_snapshot")]
static extern int GetSystemSnapshot(ref SNAPSHOT_PARMS snapshot);
Use as
var s = new SNAPSHOT_PARMS { size = Marshal.SizeOf<SNAPSHOT_PARMS>() };
int result = GetSystemSnapshot(ref s);
// check result, use s

Related

C# call C Function pass struct array: data scrambled

a simple Codesnippet should demonstrate the problem:
C:
typedef struct {
//unsigned short tno ;
unsigned long avf ;
}
MY_MAZ_TD;
__declspec(dllexport) void CMazGetAllToolData(MY_MAZ_TD* maz_td)
{
maz_td[0].avf = 4;
maz_td[1].avf = 5;
}
C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MY_MAZ_TD
{
//public ushort tno; /* Tool number */
public ulong avf;
}
[DllImport(TEST_DLL, CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern int CMazGetAllToolData([In, Out] MY_MAZ_TD[] maz_td);
MY_MAZ_TD[] my_maz_td = new MY_MAZ_TD[num];
CMazGetAllToolData(my_maz_td);
After calling CMazGetAllToolData:
my_maz_td[0].avf = 21474836484;
my_maz_td[1].avf = 0;
Actually I have much more values in the structure (like the commented tno). But even with only one variable in the struct, the values get scrambled. What is wrong here?

How to map double-C-struct-pointer in C#?

I have the following C-calls to libACL:
extern int acl_get_entry(acl_t acl, int entry_id, acl_entry_t *entry_p);
extern int acl_get_permset(acl_entry_t entry_d, acl_permset_t *permset_p);
And I've tracked the typedefs to
typedef struct __acl_permset_ext *acl_permset_t;
typedef struct __acl_entry_ext *acl_entry_t;
typedef struct __acl_ext *acl_t;
in /usr/include/acl/libacl.h and /usr/include/sys/acl.h
So unless I made an error, this means the above native calls are equivalent to:
extern int acl_get_entry(__acl_ext *acl, int entry_id, __acl_entry_ext **entry_p);
extern int acl_get_permset(__acl_ext *entry_d, __acl_permset_ext **permset_p);
Now I'm a bit at a loss about how to map these to C#...
I first thought I could just do this:
// extern int acl_get_entry(acl_t acl, int entry_id, acl_entry_t *entry_p);
[SuppressUnmanagedCodeSecurity]
[DllImport("acl", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "acl_get_entry")]
internal static extern int acl_get_entry(__acl_ext* acl, AclEntryConstants entry_id, ref __acl_entry_ext entry_p); // Double pointer, correct ???
And that even works, at least apparently.
But when I do the same with acl_get_permset
// extern int acl_get_permset(acl_entry_t entry_d, acl_permset_t *permset_p);
[SuppressUnmanagedCodeSecurity]
[DllImport("acl", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl,
EntryPoint = "acl_get_permset")]
internal static extern int acl_get_permset(__acl_entry_ext* entry_d, __acl_permset_ext** permset_p); // double pointer ?
aka
internal static extern int acl_get_permset(__acl_entry_ext* entry_d, ref __acl_permset_ext permset_p); // double pointer ?
Then it doesn't work...
I have written the following C-code to check:
int main()
{
// Get all the entries
acl_entry_t acl_entry_;
acl_permset_t permission_set;
acl_tag_t acl_kind_tag;
const char* _filename = "/root/Desktop/CppSharp.txt";
acl_t acl_file = acl_get_file(_filename, ACL_TYPE_ACCESS);
int found = acl_get_entry(acl_file, ACL_FIRST_ENTRY, &acl_entry_);
int a = acl_get_permset(acl_entry_, &permission_set);
int b = acl_get_tag_type(acl_entry_, &acl_kind_tag);
printf("a: %d; b: %d\n", a, b);
acl_entry new_acl;
new_acl.reading = ACL_GET_PERM(permission_set, ACL_READ);
new_acl.writing = ACL_GET_PERM(permission_set, ACL_WRITE);
new_acl.execution = ACL_GET_PERM(permission_set, ACL_EXECUTE);
return 0;
}
and that returns a non -1 value for a and b.
But my C# code, which does exactly the same (or so I thought), gets to int found = 1 (just like C), but then it returns -1 for a and b...
static unsafe void ReadACL()
{
string fileName = "/root/Desktop/CppSharp.txt";
global::acl.__acl_ext* acl_file = NativeMethods.acl_get_file(fileName, global::acl.acl_type_t.ACL_TYPE_ACCESS);
global::acl.__acl_entry_ext acl_entry_ = new global::acl.__acl_entry_ext();
int found = NativeMethods.acl_get_entry(acl_file, global::acl.AclEntryConstants.ACL_FIRST_ENTRY, ref acl_entry_);
System.Console.WriteLine(found);
global::acl.__acl_permset_ext permission_set;
acl_tag_t acl_kind_tag = acl_tag_t.ACL_UNDEFINED_TAG;
int a = NativeMethods.acl_get_permset(&acl_entry_, &permission_set);
global::acl.acl_tag_t tag_type = acl_tag_t.ACL_UNDEFINED_TAG;
int b = NativeMethods.acl_get_tag_type(&acl_entry_, &tag_type);
System.Console.WriteLine($"{a} {b}");
Also, the strangest thing - I've searched the following header files:
/usr/include/acl/libacl.h
/usr/include/sys/acl.h
and the entire /usr/include folder for __acl_permset_ext and __acl_entry_ext, but I needed to google them, as they are nowhere defined... Does the C-Compiler just use pointers, without needing the structs at all ?
Also, prior to doing it manually, I've tried to create the bindings automatically with CppSharp, but these auto-generated bindings had the very same issue...
mono ./CppSharp.CLI.exe --arch=x64 --output=/home/username/RiderProjects/TestProject/TestProject/AUTOMAPPED/ /usr/include/acl/libacl.h /usr/include/sys/acl.h
And one more thing I noticed:
What sense does it make to pass a double-pointer ?
That's like
struct x;
function(ref &x)
which makes very little sense IMHO, as your passing the address by reference.
Are these perhaps arrays ?
Like
struct[] x;
function(ref x)
Here are the constants:
// #define ACL_UNDEFINED_ID ((id_t)-1)
// acl_check error codes
public enum acl_check_errors
: int
{
ACL_MULTI_ERROR = (0x1000), // multiple unique objects
ACL_DUPLICATE_ERROR = (0x2000), // duplicate Id's in entries
ACL_MISS_ERROR = (0x3000), // missing required entry
ACL_ENTRY_ERROR = (0x4000) // wrong entry type
}
// 23.2.2 acl_perm_t values
public enum acl_perm_t
: uint
{
ACL_READ = (0x04),
ACL_WRITE = (0x02),
ACL_EXECUTE = (0x01),
// ACL_ADD = (0x08),
// ACL_DELETE = (0x10),
}
// 23.2.5 acl_tag_t values
public enum acl_tag_t
: int
{
ACL_UNDEFINED_TAG = (0x00),
ACL_USER_OBJ = (0x01),
ACL_USER = (0x02),
ACL_GROUP_OBJ = (0x04),
ACL_GROUP = (0x08),
ACL_MASK = (0x10),
ACL_OTHER = (0x20)
}
public enum acl_type_t
: uint
{
ACL_TYPE_ACCESS = (0x8000),
ACL_TYPE_DEFAULT = (0x4000)
}
// 23.2.8 ACL Entry Constants
public enum AclEntryConstants
: int
{
ACL_FIRST_ENTRY = 0,
ACL_NEXT_ENTRY = 1,
}
And this are the structs that I googled together:
// https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/301faaf37f99fc30105f261f23d44e2a0632ffc0/acl/libacl/libobj.h
// https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/301faaf37f99fc30105f261f23d44e2a0632ffc0/acl/libacl/libobj.h
// https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/301faaf37f99fc30105f261f23d44e2a0632ffc0/acl/libacl/libacl.h
// https://allstar.jhuapl.edu/repo/p1/amd64/acl/libacl.h
// https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/301faaf37f99fc30105f261f23d44e2a0632ffc0/acl/libacl/libacl.h
// https://kernel.googlesource.com/pub/scm/fs/ext2/xfstests-bld/+/301faaf37f99fc30105f261f23d44e2a0632ffc0/acl/libacl/acl_get_fd.c
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct obj_prefix
{
public ulong p_magic;
public ulong p_flags;
}
// typedef struct __acl_permset_ext *acl_permset_t;
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct __acl_permset_ext
{
// permset_t s_perm; // typedef unsigned int permset_t;
public uint s_perm;
};
// typedef struct acl_permset_obj_tag acl_permset_obj;
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct acl_permset_obj_tag
{
public obj_prefix o_prefix;
public __acl_permset_ext i;
};
// #define __U32_TYPE unsigned int
// #define __ID_T_TYPE __U32_TYPE
// __STD_TYPE __ID_T_TYPE __id_t; /* General type for IDs. */
// typedef __id_t id_t;
/* qualifier object */
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct __qualifier_ext
{
//id_t q_id;
public uint q_id;
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct qualifier_obj_tag
{
public obj_prefix o_prefix;
public __qualifier_ext i;
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct acl_entry_obj_tag
{
public obj_prefix o_prefix;
public __acl_entry_ext i;
}
// typedef struct __acl_ext *acl_t;
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct __acl_ext
{
// typedef struct acl_entry_obj_tag acl_entry_obj;
// acl_entry_obj *a_prev, *a_next;
// acl_entry_obj *a_curr;
// acl_entry_obj *a_prealloc, *a_prealloc_end;
public acl_entry_obj_tag* a_prev;
public acl_entry_obj_tag* a_next;
public acl_entry_obj_tag* a_curr;
public acl_entry_obj_tag* a_prealloc;
public acl_entry_obj_tag* a_prealloc_end;
// size_t a_used; // typedef __SIZE_TYPE__ size_t;
public ulong a_used;
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct acl_obj_tag
{
public obj_prefix o_prefix;
public __acl_ext i;
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct __acl_entry
{
acl_tag_t e_tag;
// qualifier_obj e_id; // typedef struct qualifier_obj_tag qualifier_obj;
qualifier_obj_tag e_id;
// acl_permset_obj e_perm; //typedef struct acl_permset_obj_tag acl_permset_obj;
acl_permset_obj_tag e_perm;
}
// typedef struct __acl_entry_ext *acl_entry_t;
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public unsafe struct __acl_entry_ext
{
// acl_entry_obj *e_prev, *e_next; // typedef struct acl_entry_obj_tag acl_entry_obj;
public acl_entry_obj_tag* e_prev;
public acl_entry_obj_tag* e_next;
// acl_obj *e_container; // typedef struct acl_obj_tag acl_obj;
public acl_obj_tag* e_container;
public __acl_entry e_entry;
}
From looking here, those ACL types are defined as:
struct __acl_ext;
struct __acl_entry_ext;
struct __acl_permset_ext;
typedef struct __acl_ext *acl_t;
typedef struct __acl_entry_ext *acl_entry_t;
typedef struct __acl_permset_ext *acl_permset_t;
We've been told that the struct __acl_ext exists, but we don't get to see how it's defined: we don't know what fields it has. Obviously it's properly defined in another (private) header or source file, but we don't have visibility to those: they're private.
On the face of it this seems like a problem: how can we use these structs if we don't know how large they are, or how their fields are laid out? Look further, and you can see that we only ever interact with pointers to these structs: ACL functions will give us back a pointer, which we can then pass to other ACL functions. We're never expected to dereference the pointers ourselves. The ACL code of course knows what the pointers point to, but that's hidden from us.
This is called an opaque pointer.
(This can be a useful strategy. For example, it lets the ACL library change how the struct is defined without breaking consumers. It also stops us from changing fields in these structs directly, which might break the ACL library).
So. We shouldn't be trying to define C# types for these structs at all: we're very much not meant to be doing that. The C# type for an opaque pointer is IntPtr, so let's use that:
// extern int acl_get_entry(acl_t acl, int entry_id, acl_entry_t *entry_p);
[SuppressUnmanagedCodeSecurity]
[DllImport("acl", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acl_get_entry")]
internal static extern int acl_get_entry(IntPtr acl, AclEntryConstants entry_id, out IntPtr entry_p);
// extern int acl_get_permset(acl_entry_t entry_d, acl_permset_t *permset_p);
[SuppressUnmanagedCodeSecurity]
[DllImport("acl", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acl_get_permset")]
internal static extern int acl_get_permset(IntPtr entry_d, out IntPtr permset_p);
We could use ref or out for the IntPtr. Reading the docs, it looks like the C code never reads the value of the double pointer you pass in: it just uses it as a way of passing a pointer back out. Therefore we use out.

Wrapping c++ function to c# with total dynamic size of struct

I'm struggling with wrapping C++ function to C#.
I have very basic knowledge about these sort of wrapping but here i'm trying to find the "best solution".
Let's say I only have a .dll that contains C++ function. I only know there is a function with this signature :
static void GetInfos(LibraryInfo& infos);
And of course I know there is a class LibraryInfos
class LIBRARY_EXPORT_CLASS LibraryInfo
{
public:
const char* libVersion;
const char* libName;
const char* libDate;
};
};
Now I try to use this function in a C# test project :
static void Main(string[] args)
{
// Create Pointer
IntPtr ptr;
// Call function
GetInfos(out ptr);
// Get the 100 first byte (used only to demonstrate)
byte[] buffer = new byte[100];
Marshal.Copy(ptr, buffer, 0, 100);
// Display memory content
Console.WriteLine(Encoding.ASCII.GetString(buffer));
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out IntPtr test);
This code give me as output
v.1.2.5 ?I9
First : I know this is the really bad way of doing it, marshalling an
arbitrary length as a byte[] is only here just for demonstration.
Second : I only have the version, but if I'm calling the same dll
from a C++ project, I have the 3 field with data.
Third : Why did I use Marshal copy, and this arbitrary length of 100
? Because I didn't succeed in calling PtrToStruct, here is what I
tried :
[StructLayout(LayoutKind.Sequential)]
private struct LibInformation
{
public IntPtr Version; // I tried, char[], string, and IntPtr
public IntPtr Name;
public IntPtr Date;
}
static void Main(string[] args)
{
// Create Pointer
IntPtr ptr;
// Call function
GetInfos(out ptr);
if (ptr != IntPtr.Zero)
{
LibInformation infos = (LibInformation)Marshal.PtrToStructure(ptr, typeof(LibInformation));
}
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out IntPtr test);
Then I'm not able to retrieve my Version, Name and Date.
If I use IntPtr in my struct, I dont have the length of the String so
I can't realy marshal.Copy, nor PtrToStringAuto.
If I use Char[] or string it doesn't work.
I think my issue is about not knowing the size of the final response. so my best option for now is to make C++ project, calling this function from there then wrap this struct in a better one , that I can Marshal on the other side (with Length of each member as other member).
Any thought ?
[EDITS 1 Based on jdweng comment]
[StructLayout(LayoutKind.Sequential)]
private struct LibInformation
{
public IntPtr Version; // I tried, char[], string, and IntPtr
public IntPtr Name;
public IntPtr Date;
}
static void Main(string[] args)
{
// Create Pointer
IntPtr ptr;
// Call function
GetInfos(out ptr);
var data = Marshal.PtrToStructure<LibInformation>(ptr);
var version = Marshal.PtrToStringAnsi(data.Version);
Console.WriteLine(version) // result : ""
// Use Ptr directly as string instead of struct
var version2 = Marshal.PtrToStringAnsi(ptr);
Console.WriteLine(version2) // result : "v1.2.5" but how can i access other field ?
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out IntPtr test);
[EDITS 2 Based on jdweng 2snd comment]
[StructLayout(LayoutKind.Sequential)]
private struct LibInformation
{
public IntPtr Version;
public IntPtr Name;
public IntPtr Date;
}
static void Main(string[] args)
{
// Create Pointer for my structure
IntPtr ptr;
// Create 3 pointers and allocate them
IntPtr ptrVersion = Marshal.AllocHGlobal(100);
IntPtr ptrName = Marshal.AllocHGlobal(100);
IntPtr ptrDate = Marshal.AllocHGlobal(100);
// Then declare LibInformation and assign
LibInformation infos = new LibInformation();
// Here is probably my missunderstanding (an my error)
// As I need a ptr to call my function I have to get the Ptr of my struct
IntPtr ptr = Marshal.AllocHGlobal(300);
Marshal.StructureToPtr(infos, ptr, false);
// Assign
infos.Version = ptrVersion;
infos.Name = ptrName;
infos.Date = ptrDate;
// Call function
GetInfos(out ptr);
var data = Marshal.PtrToStructure<LibInformation>(ptr);
var version = Marshal.PtrToStringAnsi(data.Version);
Console.WriteLine(version) // result : still ""
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out IntPtr test);
I finally found the way to do it, thanks to the explanation of #jdweng and the link of #PaulF
The only bad news is that I have to arbitrary allocate n bytes before calling my functions
Link :
MSDN : marshaling-classes-structures-and-unions
Explanations (jdweng):
The parameter list of a method is on the execution stack. Once you return from the method the parameters list is not valid because it can be over written by the parent code. Only the return value of a method is valid. So you have to Allocate all the data before calling the method. So you need to allocate the variables version, name, and date in unmangaged memory. Then declare LibInformation and set version, name, and date to the memory locations allocated. Finally call the method. Then to get the three variables you have to call Marshal.PtrToStruct to copy results from unmanaged memory.
Final code :
[StructLayout(LayoutKind.Sequential)]
private struct LibInformation
{
public IntPtr Version;
public IntPtr Name;
public IntPtr Date;
}
static void Main(string[] args)
{
// Create 3 pointers and allocate them
IntPtr ptrVersion = Marshal.AllocHGlobal(100);
IntPtr ptrName = Marshal.AllocHGlobal(100);
IntPtr ptrDate = Marshal.AllocHGlobal(100);
// Then declare LibInformation and assign
LibInformation infos = new LibInformation();
// Assign
infos.Version = ptrVersion;
infos.Name = ptrName;
infos.Date = ptrDate;
// Call function
GetInfos(out infos);
var version = Marshal.PtrToStringAnsi(data.Version);
var name = Marshal.PtrToStringAnsi(data.Name);
var date = Marshal.PtrToStringAnsi(data.Date);
Console.ReadLine();
}
[DllImportAttribute("MyLibrary.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetInfos")]
private static extern void GetInfos(out LibInformation test); // Changing IntPtr to LibInformation
[Edit 1 based on David Heffernan comment]
Here is the C code of a calling sample :
HComplexCTXPoint::LibraryInfo versionInfo;
HComplexCTXPoint::GetInfos(versionInfo);
std::cout << versionInfo.libName << std::endl;

C# DllImport : AccessViolationException when calling a vkCreateInstance

I'm trying to create a Vulkan wrapper in C#, but I have some problems when I call a function. I rewrote the vulkan.h header as follows :
public static class Vk {
[StructLayout(LayoutKind.Sequential)] public class Instance { }
public enum Result {
...
}
public enum StructureType {
...
}
[StructLayout(LayoutKind.Sequential)] public class ApplicationInfo {
public StructureType sType;
public IntPtr pNext;
public string pApplicationName;
public uint applicationVersion;
public string pEngineName;
public uint engineVersion;
public uint apiVersion;
}
[StructLayout(LayoutKind.Sequential)] public class InstanceCreateInfo {
public StructureType sType;
public IntPtr pNext;
public uint flags_VkInstanceCreateFlags;
public ApplicationInfo pApplicationInfo;
public uint enabledLayerCount;
public string[] ppEnabledLayerNames;
public uint enabledExtensionCount;
public string[] ppEnabledExtensionNames;
}
[DllImport("vulkan-1.dll", EntryPoint = "vkCreateInstance")]
public extern static Result CreateInstance(
InstanceCreateInfo pCreateInfo,
IntPtr AllocationCallbacks_pAllocator,
out IntPtr pInstance_Instance);
}
The original declaration in C of this function is
VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(
const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance);
Now when I call my function, I'm doing like this :
Vk.InstanceCreateInfo instance_create_info = new Vk.InstanceCreateInfo();
...
IntPtr hinstance;
Vk.Result result = Vk.CreateInstance(instance_create_info, IntPtr.Zero, out hinstance); <-- error AccessViolationException
I don't understand where is my problem, because it seems to be a valid solution : StackOverflow : AccessViolationException when calling vkEnumeratePhysicalDevices via pInvoke from c#.
I tried by initializing my IntPtr hinstance with
Marshal.AllocHGlobal(Marshal.SizeOf<Vk.Instance>());
I also tried to "convert" my instance_create_info to another IntPtr with Marshal.StructureToPtr(...); and I tried to pass instance_create_info and instance by the ref keyword. Obviously, nothing worked.
Any idea ?
EDIT :
The native function is used as follows :
//Definition
typedef struct VkApplicationInfo {
VkStructureType sType;
const void* pNext;
const char* pApplicationName;
uint32_t applicationVersion;
const char* pEngineName;
uint32_t engineVersion;
uint32_t apiVersion;
} VkApplicationInfo;`
typedef struct VkInstanceCreateInfo {
VkStructureType sType;
const void* pNext;
VkInstanceCreateFlags flags;
const VkApplicationInfo* pApplicationInfo;
uint32_t enabledLayerCount;
const char* const* ppEnabledLayerNames;
uint32_t enabledExtensionCount;
const char* const* ppEnabledExtensionNames;
} VkInstanceCreateInfo;
#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
VK_DEFINE_HANDLE(VkInstance)
//Code
VkApplicationInfo application_info{};
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
application_info.apiVersion = VK_API_VERSION;
application_info.applicationVersion = VK_MAKE_VERSION( 1, 0, 0 );
application_info.pApplicationName = "";
application_info.engineVersion = VK_MAKE_VERSION( 1, 0, 0 );
application_info.pEngineName = "";
VkInstanceCreateInfo instance_create_info{};
instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instance_create_info.pApplicationInfo = &application_info;
instance_create_info.enabledLayerCount = 0
instance_create_info.ppEnabledLayerNames = nullptr
instance_create_info.enabledExtensionCount = 0
instance_create_info.ppEnabledExtensionNames = nullptr
VkInstance _instance = nullptr;
assert( !vkCreateInstance( &instance_create_info, nullptr, &_instance ) );
it seems that you are trying to do something similar this guy made a wrapper for the c#. Can be useful for you. Go to Source/SharpVulkan/Generated/Functions.
public static unsafe Instance CreateInstance(ref InstanceCreateInfo createInfo, AllocationCallbacks* allocator = null)
{
Instance instance;
fixed (InstanceCreateInfo* __createInfo__ = &createInfo)
{
vkCreateInstance(__createInfo__, allocator, &instance).CheckError();
}
return instance;
}
[DllImport("vulkan-1.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern unsafe Result vkCreateInstance(InstanceCreateInfo* createInfo, AllocationCallbacks* allocator, Instance* instance);
internal static unsafe void EnumerateInstanceExtensionProperties(byte* layerName, ref uint propertyCount, ExtensionProperties* properties)
{
fixed (uint* __propertyCount__ = &propertyCount)
{
vkEnumerateInstanceExtensionProperties(layerName, __propertyCount__, properties).CheckError();
}
}
Hope it is helpfull :)
remove out keyword for third argument or declare it as corresponding struct.

Passing struct from unmanaged C++ to C#

Note: The final working solution is after the edit!
I hope someone can help me with a problem I've been trying to solve for the last few days.
I am trying to pass a struct from a unmanaged C++ DLL to a C# script. This is what I have so far:
C++
EXPORT_API uchar *detectMarkers(...) {
struct markerStruct {
int id;
} MarkerInfo;
uchar *bytePtr = (uchar*) &MarkerInfo;
...
MarkerInfo.id = 3;
return bytePtr;
}
C#
[DllImport ("UnmanagedDll")]
public static extern byte[] detectMarkers(...);
...
[StructLayout(LayoutKind.Explicit, Size = 16, Pack = 1)]
public struct markerStruct
{
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(0)]
public int Id;
}
...
markerStruct ByteArrayToNewStuff(byte[] bytes){
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
markerStruct stuff = (markerStruct)Marshal.PtrToStructure(
handle.AddrOfPinnedObject(), typeof(markerStruct));
handle.Free();
return stuff;
}
...
print(ByteArrayToNewStuff (detectMarkers(d, W, H, d.Length) ).Id);
The problem is that this works, but the value printed is completely off (sometimes it prints around 400, sometimes max int value).
I'm guessing that there's something wrong with how I marshalled the struct in C#. Any ideas?
Edit:
This is the working solution using ref:
C++
struct markerStruct {
int id;
};
...
EXPORT_API void detectMarkers( ... , markerStruct *MarkerInfo) {
MarkerInfo->id = 3;
return;
}
C#
[DllImport ("ArucoUnity")]
public static extern void detectMarkers( ... ,
[MarshalAs(UnmanagedType.Struct)] ref MarkerStruct markerStruct);
...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct MarkerStruct
{
public int Id;
}
...
detectMarkers (d, W, H, d.Length, ref markerInfo);
print( markerInfo.Id );
You're returning a pointer to a local variable which has already been destroyed before .NET can read it. That's a bad idea in pure C++ and a bad idea with p/invoke.
Instead, have C# pass a pointer to a structure (just use the ref keyword) and the C++ code just fill it in.
The MarkerInfo variable is local and goes out of scope when the function returns.
Don't return pointers to local variables, the objects they point to won't exist anymore.
Going to give this a whirl... thx for the post...
// new struct and generic return for items to
struct _itemStruct
{
unsigned int id; // 0 by default, so all lists should start at 1, 0 means unassigned
wchar_t *Name;
};
// for DLL lib precede void with the following...
// EXPORT_API
void getItems(std::vector<_itemStruct *> *items)
{
// set item list values here
//unsigned char *bytePtr = (unsigned char*)&items; // manual pointer return
return;
};
/* // In theory c# code will be...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct _itemStruct
{
public unsigned int Id;
public string Name;
}
[DllImport ("ListOfItems")] // for ListOfItems.DLL
public static extern void getItems(
[MarshalAs(UnmanagedType.Struct)] ref List<_itemStruct> items);
// */

Categories