如何在 c 中封送此 c 结构并调用其方法#

How to marshal this c struct and call its method in c#

提问人:Plaje 提问时间:7/21/2023 最后编辑:Plaje 更新时间:7/21/2023 访问量:109

问:

我在其他地方看到过类似的答案,但这些解决方案都不起作用,而且通常它们的问题略有不同。我还没有看到任何其他问题的解决方案在这里有所帮助。

我有一个dll,其中包含一个包含uint数组的结构类型的方法参数,作为指针传递给该方法。以下是旧 c 标头的内容:

typedef struct HandleArrayType
{
    uint32_t HandleArray[max_number_100];
}HandleArrayType;
typedef HandleArrayType *HandleArrayPtrType;

typedef bool (*type_connectToMultiple)(char *CommaSeparatedString, HandleArrayPtrType ArrayPointer, uint32_t numberOfOpensAttempted) __attribute__((cdecl));

我们尝试了几种方法来编组这个问题,这是我们尝试的最后一个建议,它给了我们内存访问冲突:

[StructLayout(LayoutKind.Sequential)]
public struct HandleArrayType
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
    public uint[] HandleArray;
}

[DllImport("file", CallingConvention = CallingConvention.Cdecl)]
internal static extern bool ConnectToMultiple(string[] nameArray, ref HandleArrayType myStruct, uint numAttempted);

我想我之前也尝试过几次这样的东西,它确实连接了,但是我们得到了一个句柄而不是两个句柄,并且句柄编号相差甚远(表明应该的结构编组不正确):

[DllImport("file", CallingConvention = CallingConvention.Cdecl)]
    internal static extern bool ConnectToMultiple(IntPtr commaSepHandlesString, uint[] handles , ref uint numAttempted);

(然后,当我们调用它时,我们将封送组为句柄字符串的 IntPtr) 如果有人知道正确编组的方法,请发布解决方案,我们现在正在使用一种糟糕的解决方法,这将使我们的过程非常缓慢(并且速度是一个问题)。

编辑:我们能够通过安装最新版本的dll来更快地解决,因此这对我们来说不再是一个大问题。但是 connectToMultiple 方法仍然被破坏。仍然很好奇它是否能发挥作用

C# C pinvoke 封送 dllimport

评论

1赞 Ben Voigt 7/21/2023
关键字很有帮助...learn.microsoft.com/en-us/dotnet/csharp/language-reference/......fixed
1赞 Hans Passant 7/21/2023
第一个代码段是已发布的 C 代码的正确声明。“numAttempted”是一个名称,表明它返回一个值。这使得 C 声明错误,应uint32_t *numberOfOpensAttempted。出于某种不可猜测的原因,你似乎在第二次尝试中做对了。
1赞 Selvin 7/21/2023
string[]? 而是......应标记为非托管 LPStr...char *string
1赞 Simon Mourier 7/21/2023
一个明确的答案需要更多的 C 语言和关于它如何工作的语义(+ 字符集、打包、谁分配等)。无论如何,这样的事情应该可以工作(和/或像汉斯所说的那样将最后一个更改为)static extern bool ConnectToMultiple(string nameArray, ref HandleArrayType myStruct, uint numAttempted);ref
0赞 Plaje 7/21/2023
@Selvin我已经尝试了将字符串传递给该方法的多种变体,在大多数情况下,实际上它们都有效。带有 marshalas 的 IntPtr 有效,LPstr 有效,并且仅传递字符串本身也有效。我不知道为什么。除了返回的句柄数组之外,一切似乎几乎在任何地方都可以工作,它总是返回一些我很确定不是真正句柄的大数字

答:

-1赞 jdweng 7/21/2023 #1

该结构需要位于非托管空间中。

       [StructLayout(LayoutKind.Sequential)]
        public struct HandleArrayType
        {
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
            public IntPtr[] HandleArray;
        }
        [DllImport("file", CallingConvention = CallingConvention.Cdecl)]
        internal static extern bool ConnectToMultiple(string nameArray, IntPtr myStruct, uint numAttempted);

        static void Main(string[] args)
        {
            HandleArrayType handleArrayType = new HandleArrayType();
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(handleArrayType));
            string nameArray = "abc";
            uint numAttempted = 100;
            bool results = ConnectToMultiple(nameArray, ptr, numAttempted);
        }

评论

1赞 Ben Voigt 7/21/2023
为什么要更改数组中的元素类型?C 代码在任何平台上都绝对是 32 位整数。不,该结构不需要位于非托管内存中。
0赞 jdweng 7/21/2023
@BenVoigt:INTPTR 是一个 32 位整数。这并没有真正的区别。我怀疑这真的是一个指针。
0赞 Plaje 7/21/2023
这导致在调用 connectomultiple 方法时发生访问冲突
0赞 Plaje 7/21/2023
更正:我将 uint numAttempt 更改为 ref uint numAttempted,并且它还报告它已连接(返回 true,numattempt 更改为 2)。但是,我不知道我应该如何引用返回的句柄,因为它们隐藏在 intptr 后面。我会环顾四周,看看是否能找到一种方法来获取这些值
1赞 Ben Voigt 7/21/2023
@jdweng:不,IntPtr 是一个指针大小的整数,如 .它不是到处都是 32 位的。uintptr_t
1赞 Charlieface 7/21/2023 #2

看来 C 声明是错误的,并且是一个指针。此外,您需要指定字符串的字符集,通常为 .numAttemptedLPStr

此外,目前尚不清楚是否应该是 or 或 。myStruct[In, Out] ref[Out] outin

[StructLayout(LayoutKind.Sequential)]
public struct HandleArrayType
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
    public int[] HandleArray;
}

[DllImport("file", CallingConvention = CallingConvention.Cdecl)]
internal static extern bool ConnectToMultiple(
    [MarshalAs(UnmanagedType.LPStr)] string nameArray,
    [In, Out] ref HandleArrayType myStruct,
    [Out] out uint numAttempted);

static void Main(string[] args)
{
    var handleArrayType = new HandleArrayType();
    bool results = ConnectToMultiple("abc", ref handleArrayType, out var numAttempted);
}

评论

0赞 Plaje 7/21/2023
谢谢,我实际上并没有使用 handlearray 定义,我只是使用常规数组。出于某种原因,它确实连接并返回了“句柄”值,但是当以后使用句柄时,我可以看到它们不正确。这是在我意识到输入是一个结构而不是实际数组之前。我很快就会尝试一下,看看会发生什么
0赞 Plaje 7/21/2023
同样为了清楚起见,IntPtr 确实有效,因为 IntPtr 字符串中的逗号数决定了调用中“numattempted”返回的数字。唯一不起作用的是返回的句柄
1赞 Charlieface 7/21/2023
是的,这就是我说的,当你这样做时它不起作用的原因是因为你没有指定字符集,所以它被修改为 Unicode 而不是 ANSIstring nameArray
0赞 Plaje 7/21/2023
我试了一下,类似于我在 OP 中的最后一次尝试,它似乎确实连接了,但返回的句柄是“2069910436”(应该有两个句柄)。我想当我做我的时,句柄是一个较小的数字,例如 14000000/15000000 或其他什么,但句柄在这两种情况下都不起作用
1赞 Charlieface 7/21/2023
就像其他人说的,可能是包装关闭了,或者可能被定义为其他东西,很难知道。uint32_t