How to pass a string argument from C++ to a managed C# DLL

后端 未结 1 1224
旧巷少年郎
旧巷少年郎 2021-01-22 23:22

I\'m writing a C++ program that needs to call a DLL written in C#. I followed these instructions for creating my C# DLL and linking in to it from my C++.

https://suppor

相关标签:
1条回答
  • 2021-01-22 23:44

    I couldn't find a concise answer to this question, so I'm putting my solution here in the hope that it helps someone in the future.

    TL;DR: You need to use a BSTR to pass strings back and forth between C# and C++.

    Here is how I did it.

    C# Code

    Here is a sample of my C# code. A few things to note:

    • Any function you want to be able to call from the C++ Must be delcared in the interface section.
    • Notice the way I declare the stringToPrint argument both in the interface and the function definition. Prefacing string with [MarshalAs(UnmanagedType.BStr)] is crucial.
    • Once in the function you can use the string argument as if it were a normal string. You don't need to convert from BSTR in C# the way you do in C++. More on that below.

    .CS file

    //Reference where I got all this:
    //https://support.microsoft.com/en-us/kb/828736
    
    // Class1.cs
    // A simple managed DLL that contains a method to add two numbers.
    using System;
    using System.Runtime.InteropServices;
    
    
    namespace ManagedDLL
    {
        // Interface declaration.
        public interface ICalculator
        {
            //Test functions
            int Add(int Number1, int Number2);
            int ReturnAge();
            string StringTest();
            void PrintAString([MarshalAs(UnmanagedType.BStr)] string stringToPrint);
        };
    
        // Interface implementation.
        public class ManagedClass : ICalculator
        {
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            //Test functions
            public int Add(int Number1, int Number2)
            {
                return Number1 + Number2;
            }
    
            public int ReturnAge()
            {
                return 35;
            }
    
            public string StringTest()
            {
                return "Can you hear me now?";
            }
    
            public void PrintAString([MarshalAs(UnmanagedType.BStr)] string stringToPrint)
            {
                Console.WriteLine("Trying to print a BSTR in C#");
    
                Console.WriteLine(stringToPrint);
                Console.WriteLine("Done printing");
            }
        }
    }
    

    C++ Code

    A few things to notice in the C++:

    • The #import call in the header. This is where you tell the C++ how to find your C# library. This is mentioned in the tutorial I link to in the question.
    • If you have a return value in one of your C# functions, it won't come through to the C++ as a return. Instead you'll need to include a pointer to a C++ variable as a parameter when you make the call. See the AddTest() function for an example of this.
    • Strings must be passed to the C# as BSTR type variables. It's fairly easy to convert a std::string to a BSTR, I have functions for doing the conversion in either direction.

    Header File

    //csLink.h
    
    #include <windows.h>
    #include <iostream>
    #include <string>
    
    #import "path/to/C#/dll.tlb" raw_interfaces_only
    
    using namespace std;
    
    namespace Sample{
    
        class CSLink {
        public:
            CSLink();
            //Test functions
            int AddTest(int i1, int i2);
            int AgeTest();
            string StringTestCall();
            void stringArgTest(string s);
    
        private:
            ICalculatorPtr pCalc;
            long lResult;
    
            string convertBSTR(BSTR *s);
            BSTR convertBSTR(string s);
    
        };
    }
    

    Source file

    //csLink.cpp
    
    #include "stdafx.h"
    #include "csLink.h"
    
    using namespace std;
    
    namespace Sample{
        //Constructor
        CSLink::CSLink(){
            cout << "You have created a CS Link" << endl;
    
            //https://support.microsoft.com/en-us/kb/828736
            HRESULT hr = CoInitialize(NULL);
    
            pCalc = ICalculatorPtr(__uuidof(ManagedClass));
    
            lResult = 0;
        }
    
        //Test functions
        int CSLink::AddTest(int i1, int i2){
            cout << "you are adding " << i1 << " and " << i2 << endl;
    
            pCalc->Add(i1, i2, &lResult);
    
            cout << "The result should have been " << i1 + i2 << " and it was " << lResult << endl;
    
            return 0;
        }
    
        int CSLink::AgeTest(){
            cout << "Trying to get my age" << endl;
    
            pCalc->ReturnAge(&lResult);
    
            return lResult;
        }
    
        string CSLink::StringTestCall(){
            BSTR s;
            pCalc->StringTest(&s);
    
            return convertBSTR(&s);
        }
    
        void CSLink::stringArgTest(string s)
        {
            //References I used figuring this all out:
            //http://stackoverflow.com/questions/28061637/how-to-pass-string-parameters-between-c-and-c
            //https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshalasattribute(v=vs.110).aspx
            //http://forums.codeguru.com/showthread.php?193852-How-to-convert-string-to-wstring
            //http://stackoverflow.com/questions/6284524/bstr-to-stdstring-stdwstring-and-vice-versa
    
            BSTR bSTR = convertBSTR(s);
    
            cout << "~~~~~~~~~~~~~~~~~~~~~~~" << endl;
            cout << "Testing conversion: " << convertBSTR(&bSTR) << "|end test" << endl;
            pCalc->PrintAString(bSTR);
            cout << "~~~~~~~~~~~~~~~~~~~~~~~" << endl;
        }
    
        //Utility functions
        string CSLink::convertBSTR(BSTR *s){
            if (*s == nullptr){
                return "NULL STRING";
            }
            else{
                wstring ws(*s, SysStringLen(*s));
                string ss(ws.begin(), ws.end());
                return ss;
            }
        }
    
        BSTR CSLink::convertBSTR(string s){
            wstring wStr = wstring(s.length(), L' ');
            copy(s.begin(), s.end(), wStr.begin());
    
            return SysAllocStringLen(wStr.data(), wStr.size());
        }
    }
    

    That's about it. Comment with any questions, I'll do my best to answer.

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