【问题标题】:How to return text from Native (C++) code如何从本机 (C++) 代码返回文本
【发布时间】:2011-07-15 13:56:26
【问题描述】:

我正在使用 Pinvoke 实现本机 (C++) 代码和托管 (C#) 代码之间的互操作性。我想要实现的是从本机代码中获取一些文本到我的托管代码中。为此,我尝试了很多事情,例如通过 ref 传递字符串/字符串生成器,使用 [IN] 和 [OUT],编组到 LPSTR,从函数返回字符串等,但在我的情况下没有任何作用。对于一些小代码的任何帮助将不胜感激。

【问题讨论】:

    标签: c# c++ string pinvoke


    【解决方案1】:

    我会用 BSTR 来做这件事,因为这意味着您不必为每个字符串调用原生两次,一次获取长度,然后一次获取内容。

    使用 BSTR,编组器将负责使用正确的内存管理器解除分配 BSTR,以便您可以安全地将其从 C++ 代码中传递出去。

    C++

    #include <comutil.h>
    BSTR GetSomeText()
    {
        return ::SysAllocString(L"Greetings from the native world!");
    }
    

    C#

    [DllImport(@"test.dll", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.BStr)]
    private static extern string GetSomeText();
    

    BSTR 有一个小缺点,即它携带 UTF-16 有效负载,但您的源数据很可能是 char*

    要克服这个问题,您可以像这样结束从 char*BSTR 的转换:

    BSTR ANSItoBSTR(const char* input)
    {
        BSTR result = NULL;
        int lenA = lstrlenA(input);
        int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0);
        if (lenW > 0)
        {
            result = ::SysAllocStringLen(0, lenW);
            ::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW);
        } 
        return result;
    }
    

    这是最难的一个,现在很容易添加其他包装器以从LPWSTRstd::stringstd::wstring 等转换为BSTR

    【讨论】:

    • @rturrado 非常感谢您在编辑中的更正。
    • 这个答案很有帮助,所以值得。顺便说一句,只是意识到 'may well char*' 缺少一个 'be'。
    • 您可以将-1作为第四个参数传递给MultiByteToWideChar,让函数自动计算字符串的长度;避免需要自己打电话给lstrlenA
    • 源数据类型char* 没有说明字符串的编码;如果是 UTF-8 编码的字符串,请考虑使用 CP_UTF8 而不是 CP_ACP
    • @Frerich 这是一个很好的观点。我的答案是假设 Windows 上的文本几乎总是使用本地 ANSI 代码页或 UTF-16 编码的。 UTF-8 在 Windows 中很少见。
    【解决方案2】:

    Here 是一个讨论过字符串编组的话题。

    需要用属性标记参数

    [MarshalAs(UnmanagedType.LPSTR)]
    

    【讨论】:

    • 在 Unity for Android 上为我工作。
    【解决方案3】:

    这是通过 C# 执行此操作的示例。我通过 pInvoking 通过 C# 调用 Native 函数 GetWindowTextGetWindowText 返回 handle 传递给它的窗口的标题。

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern int GetWindowTextLength(IntPtr hWnd);
    
        public static string GetText(IntPtr hWnd)
        {
            // Allocate correct string length first
            int length = GetWindowTextLength(hWnd);
            StringBuilder sb = new StringBuilder(length + 1);
            GetWindowText(hWnd, sb, sb.Capacity);
            return sb.ToString();
        }        
    
        private void button1_Click(object sender, EventArgs e)
        {
            string str = GetText(this.Handle);
        }
    

    【讨论】:

      【解决方案4】:

      如果您要返回 char *,并且不想修改 C/C++ 代码来为您的返回值分配内存(或者您无法修改该代码),那么您可以更改您的C# extern function-prototype 返回一个IntPtr,然后自己进行封送处理。

      例如,这是我为 Pocketsphinx 编写的互操作的 sn-p:

      [DllImport("sphinxbase.dll", CallingConvention = CallingConvention.Cdecl)]
      private static extern IntPtr /* char const * */ jsgf_grammar_name(IntPtr /* jsgf_t * */ jsgf);
      

      这里是 C# JsgfGrammar 类的 get-accessor。 m_pU 是一个 IntPtr,它指向原始的 jsgf_t 对象。

      public string Name
      {
          get
          {
              IntPtr pU = jsgf_grammar_name(m_pU);
              if (pU == IntPtr.Zero)
                  strName = null;
              else
                  strName = Marshal.PtrToStringAnsi(pU);
              return strName;
          }
      }
      

      为其他字符串格式(例如 Unicode)修改此示例应该很简单。

      【讨论】:

        【解决方案5】:

        为什么不构建自己的文本结构:

        struct mystr { mystr(const char *_) : _text((_ == 0) 
                                                   ? 0 
                                                   : ::strdup(_)
                                                  ) 
                                              {}
                      ~mystr() { if(this->_text != 0) delete [] this->_text; }
                       operator char * & () { return this->_text; }
        
                      private : char *_text;
                     };
        

        【讨论】:

          猜你喜欢
          • 2018-11-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-03
          • 1970-01-01
          • 2014-09-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多