【问题标题】:How to convert C function signature to C# and call that function in the native DLL?如何将 C 函数签名转换为 C# 并在本机 DLL 中调用该函数?
【发布时间】:2013-12-15 21:32:09
【问题描述】:
int __stdcall getProps( /* [ in ] */ void * context,
                        /* [ in ] */ const UINT32 index,
                        /* [ in ] */ WCHAR * const pwszFilterPath,
                        /* [ in, out ]*/ UINT32 * const pFilterPathSizeInCch,
                        /* [ in ]*/ WCHAR * const pwszFilterName,
                        /* [ in, out ] */ UINT32 * const pFilterNameSizeInCch );

对于上面签名的 C 函数,这是正确的 C# 签名吗:

[DllImport("myLib.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
        public static extern int getProps( IntPtr context,
                                           uint index,
                                           [MarshalAs(UnmanagedType.LPWStr)]
                                           out StringBuilder pwszFilterPath,
                                           ref uint pFilterPathSizeInCch,
                                           [MarshalAs(UnmanagedType.LPWStr)]
                                           out StringBuilder pwszFilterName,
                                           ref uint pFilterNameSizeInCch );

这里出了点问题,我不知道是什么(我不懂 C#)。有时当我让它工作时,我会在 StringBuilder 变量中收到 ANSI 字符串,它们应该包含 UNICODE 字符。

这个想法是调用这个函数两次。首先将 pwszFilterPath 和 pwszFilterName 设置为 NULL,以便检索所需的大小,然后为这两个缓冲区分配内存,然后在第二次调用时检索值。我知道如何在 C/C++ 中做到这一点,但在 C# 中不知道。

【问题讨论】:

    标签: c# c++ pinvoke marshalling


    【解决方案1】:

    您必须从 StringBuilder 参数中删除 out 关键字。一个普通的StringBuilder 参数被编组为一个指向字符数组的指针。如果您希望本机代码接收 NULL,请传递 C# null

    [DllImport("myLib.dll", CharSet = CharSet.Unicode)]
    public static extern int getProps(
        IntPtr context,
        uint index,
        StringBuilder pwszFilterPath,
        ref uint pFilterPathSizeInCch,
        StringBuilder pwszFilterName,
        ref uint pFilterNameSizeInCch
    );
    

    调用模式如下:

    1. null 传递给StringBuilder 参数以找出缓冲区大小。
    2. 使用接收容量的构造函数重载分配StringBuilder 实例。
    3. 通过StringBuilder 实例再次调用该函数。

    大概是这样的:

    uint filterPathLength = 0;
    uint filterNameLength
    int retval = getProps(
        context,
        index,
        null,
        ref filterPathLength,
        null,
        ref filterNameLength
    );
    // check retval
    
    StringBuilder filterPath = new StringBuilder(filterPathLength);
    StringBuilder filterName = new StringBuilder(filterNameLength);
    int retval = getProps(
        context,
        index,
        filterPath,
        ref filterPathLength,
        filterName,
        ref filterNameLength
    );
    // check retval
    

    并且您需要确保您获得了适合空终止符的长度约定。我无法判断您的函数是否返回包含空终止符的长度。


    正如@Ben 指出的那样,您的注释与文本描述不符。

    /* [ in ] */ WCHAR * const pwszFilterPath
    

    [ in ] 应该暗示数据正在流入函数。如果是这样,类型应该是const WCHAR*。但事实并非如此。这是一个输出参数。所以代码应该是:

    /* [ out ] */ WCHAR * const pwszFilterPath
    

    【讨论】:

    • 更不用说,因为这些参数一开始是[in],所以它们应该是String而不是StringBuilder
    • @BenVoigt [In] 注释与文本中的描述不匹配。
    • 谢谢。我还有一个问题......我会试着看看我是否能通过你最后的 2 个答案来解决问题。如果没有,我将创建另一个问题。
    猜你喜欢
    • 2015-07-28
    • 1970-01-01
    • 1970-01-01
    • 2020-04-12
    • 2010-11-16
    • 2018-04-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多