Website of Markus Griesser


Connecting C++ and C# - Part 1



So, what is the main problem discussed here? My current problem is, that I have an old but very large C++ application which I want to extend with .NET code and UI for improving the development process. On the one hand side, it is somehow easy to import C++ functions in C#, as you can see here:

      namespace Connex{
        public class OldImports{
          [DllImport("pathtotheolddll.dll")]
          public extern static void OldFunction();
        }
        
        public class TestMe{
          public void CallOldFunction(){
            // Call the imported function
            OldImports.OldFunction();
          }
        }
      }
    

Well, this is not calling C# in C++ but the other way. Because it is the easier part and therefore also needed in many cases, I will start with this. Remeber, that this method only works, if OldFunction is exported by the dll with its "clear" name OldFunction (using the .def file) and not masked like it would be if you use extern __declspec(dllexport). If so, you have to use the Entrypoint parameter in [DllImport].

But what, if the function takes some arguments? No problem if we are talking about simple types like integers or doubles. See code:

      namespace Connex{
        public class OldImports{
          [DllImport("pathtotheolddll.dll")]
          public extern static int OldFunctionWithArgs(int nValue, 
            ref double dValue);
        }
        
        public class TestMe{
          public void CallOldFunction(){
            // Call the imported function
            int nValue = 4;
            double dValue = 10.3;
            int nRet = OldImports.OldFunction(nValue, ref dValue);
            MessageBox.Show(dValue.ToString());
          }
        }
      }
    

Now the OldFunction takes two arguments, one int and one double. The integer is passed by value and the double by reference. The functions is also returning an integer. In C++ the function would have the following look:

    extern __declspec(dllexport) int OldFunction(int nValue, double &dValue){
      dValue *= nValue;
      return 1;
    }
    

Nothing difficult up to now. But what if we want to pass a struct or a string to the C++ function? For passing a struct you have much more to do. The first thing is to declare the struct in your .NET language. Here in C#:

    namespace Connex{
      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
      public struct MFCStruct {
        public int nI;
        public double dL;
      }
    }
    

The StructLayout attribute is important, because it tells the compiler that this struct is passed to unmanaged code. If you are aware of the different datatypes in C# and C++ you can simply use LayoutKind.Sequential and let the compiler do the work. But take care, that int in C++ and int in C# must not be of same length (depends on how the C++ project was compiled). In such cases you may want to use LayoutKind.Explicit. For detailed information have a look at MSDN. The CharSet attribute tells how the unmanaged code accepts strings. If your old application is build using UNICODE, than you have to use CharSet.Unicode. When using ANSI the prefered value would be CharSet.ANSI.
And now the function call:

    namespace Connex{
      public class OldImports{
          [DllImport("pathtotheolddll.dll")]
          public extern static int OldFunctionWithArgs(MFCStruct t);
        }
      
      public class TestMe{
          public void CallOldFunction(){
            MFCStruct t = new MFCStruct();
            int nRet = OldImports.OldFunction(t);
          }
        }
    }
    

In this example the struct is passed by value. If you want to pass it by reference you can use the same way as with the double I described before.
Now a string that is passed by reference, manipulated by the old C++ function and of course printed out by the C# function.

    namespace Connex{
      public class OldImports{
          [DllImport("pathtotheolddll.dll")]
          public extern static int OldFunctionWithArgs(
            [MarshalAs(UnmanagedType.BStr)]ref string s);
        }
      
      public class TestMe{
          public void CallOldFunction(){
            string s = "Hello World";
            int nRet = OldImports.OldFunction(ref s);
            MessageBox.Show(s);
          }
        }
    }
    

What is this MarshalAs attribute about? The marshaller is part of the .NET framework and handles conversion between managed and unmanaged code. Depending on how the string is accepted on the unmanaged side (the C++ function) you will have to tell the marshaller what to do. For detailed description have again a look at MSDN. In my case, the C++ function originally was defined with a CString (MFC) reference, which I currently could not get to work. So I changed the parameter to BSTR &. The MFC seems to be a big problem when connecting to C#...