Search code examples
c#c++combstrmanaged-code

passing BSTR string as a perimeter between managed and unmanaged code (COM interop)


While working on com interop ,i followed the tutorial on this link.The code runs fine as i have done some modification depending on my requirement but the problem comes while dealing with the string.I am using BSTR string here as a perimeter here. Here is the function in c# that i am calling from c++

  public  string ShowDialog([MarshalAs(UnmanagedType.BStr)] string stringToPrint)
    {
      //  Console.WriteLine(" Enter TOTP input:");
       // stringToPrint = Console.ReadLine();

        if (stringToPrint == "111111")
        {

            MessageBox.Show("true");



        }
        else
        {
            MessageBox.Show("false");

        }

        return stringToPrint;
    }

here is my C++ main function section of the code where the calls are being made

CoInitialize(NULL);

MyInterop::IMyDotNetInterfacePtr pDotNetCOMPtr;

HRESULT hRes = pDotNetCOMPtr.CreateInstance(MyInterop::CLSID_MyDotNetClass);
if (hRes == S_OK)
{

    BSTR lResult ;

    cout << "enter TOTP input" << endl;

    _bstr_t bstrStatus = SysAllocString(L"111111");

    pDotNetCOMPtr->ShowDialog(bstrStatus,&lResult);

    SysFreeString(bstrStatus);


}

CoUninitialize();

system("pause");

here is the output enter image description here

The issues that i am facing are as follows:

  • BSTR string is not being returned on the console after it is passed from c++ code although i am using a returning function in c#
  • Is it possible to insert input dynamically on the console as i am using SysAllocString("") here which makes it somewhat hard coded.

Solution

  • When you're using Visual Studio and the #import directive, the generated code uses _bstr_t which is a smart wrapper class over BSTR (the raw Windows type).

    So, you don't have to use SysAllocString nor SysFreeString, you can just use _bstr_t very naturally. For example, in your case, if your C# method signature is like this:

    public string ShowDialog(string stringToPrint) // you don't need the MarshalAs here, the TLB will build it as a BSTR
    

    then you can use a C++ code like this:

    ... other imports or includes
    // import mscorlib so we have a full generated cool code (I recommend not to use raw_interfaces_only)
    // we rename 'or' to something that doesn't pose problems. Your mileage can vary depending on your building context...
    #import "C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb" rename("or","whatever")
    #import "C:\myPath\MyClassLibrary1.tlb" // adapt to your path
    
    int main()
    {
      CoInitialize(NULL);
      {
        MyClassLibrary1::_Class1Ptr ptr;
        HRESULT hr = ptr.CreateInstance(__uuidof(MyClassLibrary1::Class1)); // should return S_OK/0
    
        _bstr_t input = L"111111";
        _bstr_t res = ptr->ShowDialog(input); // assign the return string
        wprintf(L"res:%s\n", res.GetBSTR()); // output (unicode) result to console
      }
      CoUninitialize();
    }
    

    You could also directly write this:

    _bstr_t res = ptr->ShowDialog(L"111111");
    

    // or this (with automatic ansi to unicode conversion)

    _bstr_t res = ptr->ShowDialog("111111");
    

    All _bstr_t are automatically allocated and freed.