【问题标题】:How convert const wchar_t* from C DLL to C# string automatically如何将 const wchar_t* 从 C DLL 自动转换为 C# 字符串
【发布时间】:2013-05-25 20:30:25
【问题描述】:

这只是一种好奇心。说不定这世上有一个人做过这样的事:

我必须导出 C 函数并通过 DllImport 从 C# 代码加载它

const wchar_t * SysGetLibInfo() {
    return dllmanager.SysGetLibInfo();
}

最好的方法是声明 IntPtr,然后使用某些函数将其转换为字符串,并且被广泛推荐。换句话说,像这样

[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SysGetLibInfo(); 
// ...
Marshal.PtrToStringUni(SysGetLibInfo());

这种方法有效。但是有没有办法自动做到这一点?让 SysGetLibInfo 返回一个字符串?我发现了一些这样的建议:

[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.LPWStr)]
private static extern string SysGetLibInfo();

但它不起作用,而且根据各种示例和少量报告,它不应该起作用。

有没有办法编写我自己的属性,比如 MarshalAs,它将 IntPtr 转换为字符串?类似的东西:

[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
[return: MyOwnMarshalPtrToStringUni]
private static extern string SysGetLibInfo();

提前感谢您提供任何信息或有用的链接、示例、书籍。同样,这只是一种好奇心。

附:不建议使用单独的函数包装 SysGetLibInfo,该函数使用 PtrToStringUni 将结果转换为字符串;)

【问题讨论】:

    标签: c# c++ dll wchar-t interopservices


    【解决方案1】:

    我认为,问题出在LPWStr

    您不能将 LPWStr 值与非托管字符串一起使用 ,除非 字符串是使用非托管 CoTaskMemAlloc 函数创建的

    这很好用。本机代码:

    // header
    extern "C" __declspec(dllexport) wchar_t* SysGetLibInfo(void);
    
    // implementation
    extern "C" __declspec(dllexport) wchar_t* SysGetLibInfo(void)
    {
        return TEXT("Hello from unmanaged world!");
    }
    

    托管代码:

        [DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.LPTStr)]
        static extern string SysGetLibInfo();
    

    如果你用这种方式改变原生函数:

    extern "C" __declspec(dllexport) wchar_t* SysGetLibInfo(void)
    {
        wchar_t* pStr = (wchar_t*)CoTaskMemAlloc(100);
    
        ZeroMemory(pStr, 100);
    
        wcscpy(pStr, TEXT("Hello from unmanaged world!"));
    
        return pStr;
    }
    

    那么[return: MarshalAs(UnmanagedType.LPWStr)] 也会起作用。

    【讨论】:

    • 谢谢!不幸的是,我不想这样做。让我们假设已经编写了 DLL,它返回 const wchar_t * :)。非常感谢!
    • 假设 DLL 已经编写好了 - 所以,在编组时使用 LPTStr。这在两种情况下都有效(有CoTaskMemAlloc 和没有它)。这里不需要自定义编组器。
    • LPTStr 不起作用,有时会调用 System.AccessViolationException。根据 MS 文档并没有什么特别之处
    【解决方案2】:

    您不能覆盖 MarshalAs,但可以改用自定义编组

    http://msdn.microsoft.com/en-us/library/w22x2hw6.aspx

      [DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
      [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshaller))]
      private static extern string SysGetLibInfo();
    

    【讨论】:

    • 谢谢,这是一个选项。没有比实现 ICustomMarshaler 接口更简单的方法了,不是吗?
    • 标准字符串类型的自定义编组器?闻起来像另一个轮子。
    • 嗯。为了允许这个“[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(MyMarshaller))]”,我需要创建实现 ICustomMarshaler 接口的类 MyMarshaller,对吗?在 MyMarshaller#MarshalNativeToManaged 我将使用 PtrToStringUni.... 或者你的意思是有一个标准类已经这样做了?
    猜你喜欢
    • 2011-02-03
    • 1970-01-01
    • 2012-09-03
    • 1970-01-01
    • 2016-11-04
    • 2015-07-04
    • 2016-04-20
    • 1970-01-01
    • 2014-04-27
    相关资源
    最近更新 更多