How to call c++ functions from c# program?

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • gilit golit
    New Member
    • Feb 2011
    • 27

    How to call c++ functions from c# program?

    I have developed a software in C++.
    Now I am trying to add GUI for it .
    using windows forms.
    I understand that it is not recommended to write it in C++
    So I have started to write it in C#.
    This is a managed code.
    Then I have to call the functions in my software to perform the processing and computing .
    I understand that I have to wrap the C++ native code with a CLI managed code.
    How do I do it ? and How do I transfer the parameters ? I need clear full instructions with a code sample.

    1. How do I define the prototypes of the wrapping functions ? (strings declarations like System::String )
    2. How do I convert System::String from the wrapper class functions to the native C++ functions ?

    3. What is Invoke ? PInvoke ?

    4. Is there any C# method for System::String to convert it to characters array string and via versa


    Thanks.
  • Jason Mortmer
    New Member
    • Feb 2011
    • 23

    #2
    If you've built your functionality into a DLL, ensure that you have compiled it as a COM/ActiveX object. That way you can add a reference to it within your C# project and all of the classes will automatically be wrapped in C# code. Alternatively, you can use the System.Runtime. InteropServices namespace within C# to load functions from your DLL using 'DLLImport' and wrapping the functions manually. If you choose to do it this way, you need to look into Marshalling unmanaged types.

    Comment

    • gilit golit
      New Member
      • Feb 2011
      • 27

      #3
      1. can I get more details and code ?
      I still don't know how to do it at all.

      I can change my software , or write some new functions,
      if you tell me - no problem.

      2. In both ways my software becomes the dll ?

      3. do I have to declare in the C# extern f (..) for my functions ?
      4. Is it true, that c# can call only class methods , and not "normal functions ?
      5. How do I pass strings between them ?

      Comment

      • Jason Mortmer
        New Member
        • Feb 2011
        • 23

        #4
        I cant just paste code, you get nowhere from copying and pasting. Its better if you find out from MSDN about InteropServices .

        They are declared as prototypes using the DLLImport attribute:

        [DLLImport("Some DLL.DLL")]
        public static extern void RandomFunction( int Parameter);

        This function doesn't HAVE to be in a class. Your perception of a "normal" function would be a plain 'C' function which is just a static function. If a static function is within a class, the class name just acts like a namespace to contain the function.

        Comment

        • gilit golit
          New Member
          • Feb 2011
          • 27

          #5
          ok
          I have tried compiling the "c" code as dll

          Then added it as a reference to the c# program
          the c test code is :

          #pragma once
          ///////////#include <stdio.h >
          using namespace System;

          //extern "C" __declspec( dllexport ) bool __stdcall say_helloHello () {
          extern "C"
          { __declspec( dllexport ) void say_helloHello () {

          ////fprintf(stdout, " SAY HELLOHELLO\n" );
          int i = 1;
          //return true;
          }
          }
          (in the comments there are other versions that I have tried.).


          then in the C#
          // C# main
          using System.Runtime. InteropServices ;

          [DllImport("lib1 test1.dll", EntryPoint = "say_hello" ) ]
          //static extern bool say_helloHello () ; another option that I tried
          public static extern void say_helloHello () ;

          namespace CustomerDetails
          {
          partial class CustomerForm {
          ....
          }; }


          It always complained about "void" marked in Italic . no matter what I wrote there or if I deleted it at all, It said :
          Expected class, delagate, enum or struct

          help !

          Comment

          • Jason Mortmer
            New Member
            • Feb 2011
            • 23

            #6
            You are getting that error because you are declaring the prototype OUTSIDE the namespace. C# works in a different way to C/C++, everything needs to be within a namespace in order for the compiler to know where to put the function, and what it has access to.


            My working example:

            (Inside Visual Studio 2010: Empty Windows DLL Project)
            TestDLL.cpp
            Code:
            #include <stdio.h>
            
            // Exported DLL function.
            __declspec(dllexport) int DLLFunction(int Number1, int Number2)
            {
            	// Add the 2 numbers and return it.
            	return Number1 + Number2;
            }

            Export.def
            Code:
            LIBRARY TestDLL
            EXPORTS
                 DLLFunction

            (Inside Visual Studio 2010: C# Console Project)
            Code:
            using System;
            using System.Runtime.InteropServices;
            
            namespace CallTestDLL
            {
                class Program
                {
                    // ************COMMENT SHOWING A DIFFERENT WAY TO DECLARE THE PROTOTYPE*********************
                    //[DllImport("TestDLL.dll", CallingConvention = CallingConvention.Cdecl)]
                    //[return: MarshalAs(UnmanagedType.I4)]
                    //public static extern int DLLFunction([MarshalAs(UnmanagedType.I4)] int Number1, 
                    //                                     [MarshalAs(UnmanagedType.I4)] int Number2);
                    // *****************************************************************************************
            
                    // Description of DLL import:
                    // "TestDLL.dll" is the file being loaded.
                    // The calling convention is cdecl as the function was exported in this way.
                    // "public static extern" means that this function does not belong to a class (static) and that it is
                    // defined elsewhere (in the DLL).
                    // The comment above shows what the function would look like if you wanted to Marshal the unmanaged
                    // types yourself. However, int is done automatically so there is no need to add the Marshal attributes.
                    [DllImport("TestDLL.dll", CallingConvention = CallingConvention.Cdecl)]
                    public static extern int DLLFunction(int Number1,
                                                         int Number2);
                    
                    static void Main(string[] args)
                    {
                        // Simple call the function as any other.
                        Console.WriteLine(DLLFunction(2, 4));
                        
                        Console.ReadLine();
                    }
                }
            }
            I hope you understand what you need to do now in order to load your C++ functions.

            Comment

            • gilit golit
              New Member
              • Feb 2011
              • 27

              #7
              I did copy and paste for your code .


              I opened new project -> c++ -> CLR -> empty project

              In the configuration it is set to clr
              Common Language Runtime Support (/clr)
              I copied the command line to here, so you can see the
              project properties setting :


              /Zi /clr /nologo /W3 /WX- /Od /Oy- /D "WIN32" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /EHa /GS /fp:precise /Zc:wchar_t /Zc:forScope /Fp"Debug\TestDL L.pch" /Fa"Debug\" /Fo"Debug\" /Fd"Debug\vc100. pdb" /analyze- /errorReport:que ue


              But it did not generate any dll file.
              It generated TestDLL.exp and TestDLL.exe
              The compilation succeeded , but it gave the following warning :
              1>------ Build started: Project: TestDLL, Configuration: Debug Win32 ------
              1>Build started 08/02/2011 9:52:12 AM.
              1>InitializeBui ldStatus:
              1> Creating "Debug\TestDLL. unsuccessfulbui ld" because "AlwaysCrea te" was specified.
              1>GenerateTarge tFrameworkMonik erAttribute:
              1>Skipping target "GenerateTarget FrameworkMonike rAttribute" because all output files are up-to-date with respect to the input files.
              1>ClCompile:
              1> All outputs are up-to-date.
              1> TestDLL.cpp
              1> All outputs are up-to-date.
              1>Link:
              1> Creating library c:\documents and settings\ilana\ my documents\visua l studio 2010\Projects\T estDLL\Debug\Te stDLL.lib and object c:\documents and settings\ilana\ my documents\visua l studio 2010\Projects\T estDLL\Debug\Te stDLL.exp
              1>TestDLL.exp : warning LNK4070: /OUT:TestDLL.dll directive in .EXP differs from output filename 'c:\documents and settings\ilana\ my documents\visua l studio 2010\Projects\T estDLL\Debug\Te stDLL.exe'; ignoring directive
              1> TestDLL.vcxproj -> c:\documents and settings\ilana\ my documents\visua l studio 2010\Projects\T estDLL\Debug\Te stDLL.exe
              1>FinalizeBuild Status:
              1> Deleting file "Debug\TestDLL. unsuccessfulbui ld".
              1> Touching "Debug\TestDLL. lastbuildstate" .
              1>
              1>Build succeeded.
              1>
              1>Time Elapsed 00:00:01.37
              ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped =========

              what did I do wrong ?



              I have also tried :
              No Common Language Runtime Support
              and got
              1>------ Build started: Project: TestDLL, Configuration: Debug Win32 ------
              1>Build started 08/02/2011 10:09:41 AM.
              1>InitializeBui ldStatus:
              1> Creating "Debug\TestDLL. unsuccessfulbui ld" because "AlwaysCrea te" was specified.
              1>ClCompile:
              1> All outputs are up-to-date.
              1> TestDLL.cpp
              1>ManifestResou rceCompile:
              1> All outputs are up-to-date.
              1>Link:
              1> Creating library c:\documents and settings\ilana\ my documents\visua l studio 2010\Projects\T estDLL\Debug\Te stDLL.lib and object c:\documents and settings\ilana\ my documents\visua l studio 2010\Projects\T estDLL\Debug\Te stDLL.exp
              1>TestDLL.exp : warning LNK4070: /OUT:TestDLL.dll directive in .EXP differs from output filename 'c:\documents and settings\ilana\ my documents\visua l studio 2010\Projects\T estDLL\Debug\TestDLL.exe'; ignoring directive
              1>Manifest:
              1> All outputs are up-to-date.
              1>LinkEmbedMani fest:
              1> All outputs are up-to-date.
              1> TestDLL.vcxproj -> c:\documents and settings\ilana\ my documents\visua l studio 2010\Projects\T estDLL\Debug\Te stDLL.exe
              1>FinalizeBuild Status:
              1> Deleting file "Debug\TestDLL. unsuccessfulbui ld".
              1> Touching "Debug\TestDLL. lastbuildstate" .
              1>
              1>Build succeeded.
              1>
              1>Time Elapsed 00:00:01.20
              ========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ========
              Last edited by gilit golit; Feb 8 '11, 08:12 AM. Reason: fix

              Comment

              • gilit golit
                New Member
                • Feb 2011
                • 27

                #8
                Hi
                Now, I have found out that I can set the Configuration Type:

                Project properties -> Configuration properties -> General ->
                Project Defaults: -> Configuration Type :
                and I have set it to : Dynamic Library (.dll)


                It did work !

                Thanks ! ! !
                You gave me a full detailed answer !


                (next I will have to use the C# project as windows form appliaction .
                I hope it will work ...... )
                P.s. Why did I have to open th C++ project as an empty project,
                and not class library project ?
                Last edited by gilit golit; Feb 8 '11, 09:09 AM. Reason: fix

                Comment

                • gilit golit
                  New Member
                  • Feb 2011
                  • 27

                  #9
                  Now, I have tried the same with two functions : it did not work !
                  I just added another function, and declared it in the Exp.def file
                  as follows :
                  TestDLL.cpp
                  __declspec(dlle xport) int DLLFunction(int Number1, int Number2)
                  {
                  return Number1 + Number2;
                  }

                  __declspec(dlle xport) int DLLFunction2(in t Number1, int Number2)
                  {
                  // just return a constant number.
                  return 200 ;
                  }

                  TestDLL.cpp
                  LIBRARY TestDLL
                  EXPORTS
                  DLLFunction
                  DLLFunction2
                  ;

                  Everything was compiled ,
                  but failed immideatly at runtime ,
                  saying Unhandled exception : could not load CallTestDLL.pro gram because the method DLLFunction2 has no implementation <No RVA>


                  help !

                  Comment

                  • gilit golit
                    New Member
                    • Feb 2011
                    • 27

                    #10
                    ok

                    1. I have found out , that for every function in the c# calling project , I have to do the DllImport . as follows:



                    [DllImport("Test DLL.dll", CallingConventi on = CallingConventi on.Cdecl)]
                    public static extern int DLLFunction(int Number1,
                    int Number2);
                    [DllImport("Test DLL.dll", CallingConventi on = CallingConventi on.Cdecl)]
                    public static extern int DLLFunction2(in t Number1,
                    int Number2);
                    [DllImport("Test DLL.dll", CallingConventi on = CallingConventi on.StdCall)]
                    public static extern int say_hello(strin g name1);
                    static void Main(string[] args)


                    right ?


                    2.
                    Also , If I am not wrong , To define the function in the Exp.def file or to define it as "extern "C" { ... } is equivalent.

                    Right ?


                    3.
                    Still I want to know how to pass a string or a character array in both directions , and to be able to parse it in the c dll and the conversions .


                    4. when should we use "CallingConvent ion.StdCall" and when "CallingConvent ion.Cdecl" ?


                    Regards

                    Comment

                    • Jason Mortmer
                      New Member
                      • Feb 2011
                      • 23

                      #11
                      There is actually no need for the export.def file but it requires you to add a few more preprocessor directives that allow the DLL to be imported into another assembly.

                      Yes, every function has to have the DLLImport attribute attatched to it, this is the nature of attributes in C#.

                      My DLL compiled with the default cdecl calling convention and thats why i had to specify that in the dllimport attribute. The default attribute for DLLImport calling convention is stdcall, thats why i had to explicitly say which one the function uses. You could simply apply the __stdcall calling convention to your DLL functions and that way you wouldnt have to specify it in the DLLImport.

                      Passing a string would require Marshalling. The commented DLLImport in my code above shows how to marshal unmanaged types using attributes: '[MarshalAs(Unman agedType.I4) int Parameter' will convert a 4-byte unmanaged integer to a managed 'int' within C#. With strings, you want '[MarshalAs(Unman agedType.LPStr)] string Parameter' if it is ASCII, LPWStr if the string is Unicode.

                      Comment

                      Working...