Passing array of structs from C# to C++可以翻译为”从C#传递结构体数组到C++”。

huangapple go评论65阅读模式
英文:

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.ByValTStrSizeConst。确保在结构体上设置 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 and SizeConst for inline WCHAR arrays. make sure to set CharSet = 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声明中是否需要所有属性,关键似乎是使用了unsafefixed关键字。

感谢@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!

huangapple
  • 本文由 发表于 2023年5月17日 18:34:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76271161.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定