英文:
Passing array of structs from C# to C++
问题
public class PRINCIPAL_INFO
{
public int PrincipalStatus;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 39)]
public string UniqueID;
}
public class USER_INFO2
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string Name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string Password;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class ALLUSERINFO
{
public USER_INFO2 userInfo;
public PRINCIPAL_INFO principalInfo;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string Name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string Role;
}
class YourClass
{
[DllImport("YourDLLName.dll", CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetUsers([Out] ALLUSERINFO[] pInfo, uint size, out uint count);
}
英文:
I am trying to create C# code which calls a function in a DLL with a C interface. I am a C and C++ expert but my knowledge of C# is pretty limited. I have already managed to load the DLL into the C# program and to call functions whose interfaces use simple data types like int
or even strings. But this one exceeds my abilities, and I hope for some helpful hints.
The DLL declares this entry point:
BOOL WINAPI GetUsers(ALLUSERINFO* pInfo, DWORD size, DWORD* count);
where pInfo
points at a C array of structs with size
elements which the GetUsers
function fills with data. count
receives the number of array elements which were actually filled. Both pInfo
and count
are out-only.
ALLUSERINFO
is defined as
struct ALLUSERINFO : public USER_INFO2, public PRINCIPAL_INFO
{
WCHAR Name[10];
WCHAR Role[256];
};
and its bases classes are
struct USER_INFO2
{
WCHAR Name[80];
WCHAR Password[40];
};
struct PRINCIPAL_INFO
{
int PrincipalStatus;
WCHAR UniqueID[39];
};
(WCHAR
is a typedef for wchar_t
.)
I assume I must turn the base classes into embedded members of ALLUSERINFO
, but I am completely at loss regarding how to create an array of structs which contain fixed-size C strings in C# and then pass this array by pointer to its first element.
I found the StructLayout
and MarshalAs
attributes but from the docs which I read I am not even sure whether they are suitable in my case, much less how they work when it comes to an array of structs of structs, as in this case.
Thank you!
答案1
得分: 2
以下是翻译好的部分:
您的定义如下:
- C# 不支持结构体继承,您需要将它们放置为字段或属性。
- 对于内联的
WCHAR
数组,请使用UnmanagedType.ByValTStr
和SizeConst
。确保在结构体上设置CharSet = CharSet.Unicode
。
然后,您可以像这样调用它:
var buffer = new ALLUSERINFO[100];
if (!GetUsers(buffer, buffer.Length, out var count))
throw new Win32Exception(Marshal.GetLastWin32Error());
英文:
Your definitions are as follows:
- C# doesn't support struct inheritance, you need to put them as fields or properties instead.
- Use
UnmanagedType.ByValTStr
andSizeConst
for inlineWCHAR
arrays. make sure to setCharSet = CharSet.Unicode
on the struct.
[DllImport("yourdll.dll")]
static extern bool GetUsers(
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] ALLUSERINFO[] pInfo,
int size,
[Out]out int count);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct ALLUSERINFO
{
public USER_INFO2 UserInfo;
public PRINCIPAL_INFO PrincipalInfo;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string Name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string Role;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct USER_INFO2
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string Name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string Password;
};
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct PRINCIPAL_INFO
{
public int PrincipalStatus;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 39)]
public string UniqueID;
};
You then simply call it like this
var buffer = new ALLUSERINFO[100];
if(!GetUsers(buffer, buffer.Length, out var count))
throw new Win32Exception(Marshal.GetLastWin32Error());
答案2
得分: 0
以下是翻译好的部分:
最后,我找到了一个解决方案。它可能不够优雅,但它有效。
我需要以下的C#声明:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal unsafe struct USER_INFO2
{
public fixed char Name[80];
public fixed char Password[40];
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal unsafe struct PRINCIPAL_INFO
{
internal int PrincipalStatus;
public fixed char UniqueID[39];
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal unsafe struct ALLUSERINFO
{
internal USER_INFO2 UserInfo;
internal PRINCIPAL_INFO PrincipalInfo;
public fixed char Name[10];
public fixed char Role[256];
}
[DllImport("mydll.dll", CharSet = CharSet.Unicode)]
internal static extern bool GetUsers(ALLUSERINFO* pInfo, uint size, out uint count);
然后对GetUsers
的调用看起来像这样:
unsafe
{
ALLUSERINFO[] buffer = new ALLUSERINFO[maxUsers];
fixed (ALLUSERINFO* pBuffer = &buffer[0])
{
uint actualUsers;
bool res = DSQL.DSEnumAllUsers(pBuffer, maxUsers, out actualUsers);
if (!res)
return false;
// 在缓冲区中处理返回的数据
}
}
我不确定struct
声明中是否需要所有属性,关键似乎是使用了unsafe
和fixed
关键字。
感谢@Charlieface,他帮助我引导了正确的思路!
英文:
Finally I found a solution. It may not be elegant but it works.
I needed the following C# declarations:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal unsafe struct USER_INFO2
{
public fixed char Name[80];
public fixed char Password[40];
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal unsafe struct PRINCIPAL_INFO
{
internal int PrincipalStatus;
public fixed char UniqueID[39];
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal unsafe struct ALLUSERINFO
{
internal USER_INFO2 UserInfo;
internal PRINCIPAL_INFO PrincipalInfo;
public fixed char Name[10];
public fixed char Role[256];
}
[DllImport("mydll.dll", CharSet = CharSet.Unicode)]
internal static extern bool GetUsers(ALLUSERINFO* pInfo, uint size, out uint count);
Then the call to GetUsers
looks like this:
unsafe
{
ALLUSERINFO[] buffer = new ALLUSERINFO[maxUsers];
fixed (ALLUSERINFO* pBuffer = &buffer[0])
{
uint actualUsers;
bool res = DSQL.DSEnumAllUsers(pBuffer, maxUsers, out actualUsers);
if (!res)
return false;
// do something with the returned data in the buffer
}
}
I am not sure whether all the attributes are required in the struct
declarations. The crucial thing seems to be using the unsafe
and fixed
keywords.
Thanks to @Charlieface who helped me direct my thoughts into the correct direction!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论