Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access class in C++/CLI from C#?

I am writing a GUI tool in C# to parse and display the data output of another program written in C. In order to parse the data I need to know the data structures which are specified in a number of C header files. Thus I need to incorporate those C header files into my C# project. My questions are:

1) After some research I came to conclude that the best way is to create a new C++/CLI project in my solution, import the C header files into this new project, write some C++/CLI classes that act as thin wrappers for the data structures defined in the C header files, then reference the C++/CLI wrapper classes from the C# code. Is this the best approach, or is there a better way?

2) I ran into a reference problem. Here's my simplified code to illustrate the problem:

Original C header in C++/CLI project

#define ABC  0x12345

Wrapper class in C++/CLI project

#include "my_c_header.h"

namespace C_Wrappers {
    public ref class MyWrapper {
        public:
            property unsigned int C_ABC {
                unsigned int get() { return ABC; }
            }
    }
 }

User class in C# project

using C_Wrappers;
using System;

namespace DataViewer {
    public partial class MainForm : Form {
        private  MyWrapper myWrapper = new MyWrapper();
        public MainForm() {
            Console.WriteLine(myWrapper.C_ABC.ToString());
        }
    }
 }

In the C# project I added a reference to the C++/CLI project (using right click > Add Reference). During build I got an error in the C# project: "The type or namespace 'C_Wrappers' could not be found (are you missing a using directive or an assembly reference?)."

I thought I did everything I was supposed to. What should I do to fix this error?

like image 330
Thomas Nguyen Avatar asked Oct 03 '13 16:10

Thomas Nguyen


2 Answers

In my own solution, I had 4 projects:

  1. the C++ project and the test code
  2. the C++ DLL project which only compiles a DLL out of the first project source using dynamic links
  3. the wrapper project which is only an adapter using C++/CLI, which wraps around the raw C++
  4. the C# WPF project which was my graphical interface.

Here's my translation of the provided link above.

The C++ DLL project

Make your C++ code into a DLL lib.

  1. Inside Visual Studio, go to File > New > Project, Select Win32 project from the Visual C++ tab.
  2. Choose a name for both the project and the Solution, the solution will have all the projects inside.
  3. Inside the assistant for Win32 Application, click next, check the DLL box, then Empty project then click Finish.

Project creationProject assistant

Code to add

This is my C++ header for the dll (minus lot of stuff).

Token.h

#pragma once
#define DLLEXP   __declspec( dllexport )

DLLEXP void pop_back(std::string& str);

DLLEXP std::string testReturnString();

DLLEXP int getRandomNumber();

There's nothing to change inside the CPP.

Build the project, you should have a DLL and a LIB file to include in the C# project debug dir.

The C++/CLI wrapper

This project serves as an interface between the native code from the previous project, and managed code of the GUI.

  1. Add a new project (class library in Visual C++) (called "Wrapper" in this example) to the solution
  2. Add the path to the native project with the additional Include directories
  3. Add the native project as a reference for the new project (right click > References... > Add New Link)
  4. In Properties > Linker > Input, put the name of the native dll in the delayed loading of DLLs (Computations.dll in this example) field

The C++/CLI code

My wrapper is only a class which looks something like this (minus my own code).

Wrapper.h

#include "Token.h" // include your C++ header

#include <string>
#include <iostream>

namespace Wrapper {

    // noticed the ref?
    public ref class TokenAnalyzer
    {

    public:
        TokenAnalyzer(){
        };

        void Init();
            // use the C++/CLI type, like String^ (handles)
        System::String^ ProcessLine(int lineNbr, System::String^ line);
    };
}

Nothing special inside the CPP except that you have to include #include "stdafx.h".

It should also builds into a DLL which you will include inside the C# debug dir.

Just a useful function I found somewhere on SO but don't remember where that you might need. It convert C++/CLI String handle into a C++ standard string.

std::string MarshalString (String ^ s) {
        using namespace Runtime::InteropServices;
        const char* chars = 
            (const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();
        std::string os = chars;
        Marshal::FreeHGlobal(IntPtr((void*)chars));
        return os;
    }

The C# project

Add to the solution a new project (C # Windows Form or WPF or whatever you want!) and set it as the startup project (right-click > set as startup project).

  1. Add the C++/CLI project as a reference for the new project
  2. Add Directive using Wrapper; in source code form

Use it like:

/// Store the C++/CLI Wrapper object.</summary>
private Wrapper.TokenAnalyzer mTokenAnalyzer = new TokenAnalyzer();

Update 2016/05/06

Ashwin took the time to make a sample project and a blog post tutorial which may help further.

like image 51
Emile Bergeron Avatar answered Oct 19 '22 04:10

Emile Bergeron


For me, the answer to this similar question solved that same error in C#.

(i.e. I had a wrapper header file in my C++\CLI project, but forgot to include a .cpp file as well (even though it only has two lines in it: #include "stdafx.h" and #include "Wrapper.h"))

like image 26
Bartel Avatar answered Oct 19 '22 05:10

Bartel