How to return text from Native (C++) code

前端 未结 5 1207
無奈伤痛
無奈伤痛 2020-11-27 14:50

I am using Pinvoke for Interoperability between Native(C++) code and Managed(C#) code. What i want to achieve is get some text from native code into my managed code. For thi

相关标签:
5条回答
  • 2020-11-27 15:12

    Here is a topic were string marshaling has been discussed.

    It's need to mark parameter with attribute

    [MarshalAs(UnmanagedType.LPSTR)]
    
    0 讨论(0)
  • 2020-11-27 15:22

    I'd do it with a BSTR since it means you don't have to call into native twice per string, once to get the length and then once to get the contents.

    With a BSTR the marshaller will take care of deallocating the BSTR with the right memory manager so you can safely pass it out of your C++ code.

    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();
    

    There is one minor drawback of the BSTR, namely that it carries a UTF-16 payload but your source data may well be char*.

    To overcome this you can wrap up the conversion from char* to BSTR like this:

    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;
    }
    

    That's the hardest one out of the way, and now it's easy to add other wrappers to convert to BSTR from LPWSTR, std::string, std::wstring etc.

    0 讨论(0)
  • 2020-11-27 15:23

    why not building your own text struct :

    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;
                 };
    
    0 讨论(0)
  • 2020-11-27 15:24

    Here is an example of doing this through C#. I am calling Native function GetWindowText through C# by pInvoking. GetWindowText returns the caption of the window whose handle is passed to it.

        [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);
        }
    
    0 讨论(0)
  • 2020-11-27 15:25

    If you're returning a char *, and don't want to have to modify the C/C++ code to allocate memory for your return value (or you can't modify that code), then you can change your C# extern function-prototype to return an IntPtr, and do the marshaling yourself.

    For instance, here's a snippet of the interop I wrote for Pocketsphinx:

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

    And here's the get-accessor for the C# JsgfGrammar class. m_pU is an IntPtr that points to the raw jsgf_t object.

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

    Modifying this example for other string formats (e.g. Unicode) should be trivial.

    0 讨论(0)
提交回复
热议问题