15

I am trying to call my C++ library from my C# application (via C++/CLI). I followed the example from this question (for my specific application). The setup of my application is:

  • Project1: C++ Project (I compile this to a DLL)
  • Project2: C++ Project (my CLR wrapper; just the header file per the example above; references Project1)
  • Project3: C# Project (references Project2)

Unfortunately, when I actually go to access the CLR wrapper object in my C# application, I receive the following error:

The type or namespace name 'YourClass' could not be found (are you missing a using directive or an assembly reference?)

Do I have the project setup incorrectly, or is there something else I should be looking into? (Unfortunately, I cannot post the code for proprietary reasons, but it is a very simple bit of code and easily follows the above example.)

Update:

So I did exactly what Chris said to do (see answer below), but I am still receiving a message from my C# application that "The type or namespace name 'MyProgram' could not be found (are you missing a using directive or an assembly reference?). Here is a (mock-up) of my code.

  • Project1 - This is my C++ application. It compiles/works. I have used it elsewhere. (I get a DLL out of this build.)
  • Project2 - Here is my code for my wrapper.

MyWrapper.h

#pragma once

#include "myorigapp.h"

using namespace System;

namespace MyProgram
{
    public ref class MyWrapper
    {
    private:
        myorigapp* NativePtr;

    public:
        MyWrapper() 
        {
            NativePtr = new myorigapp();
        }

        ~MyWrapper() 
        { 
            delete NativePtr;
            NativePtr = NULL;
        }

        void dostuff()
        { 
            NativePtr->dostuff(); 
        }
    }
}
  • Project3 - This is my C# application.

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using MyProgram;

namespace Testing
{
    class Program
    {
        static void Main(string[] args)
        {
            MyWrapper p = new MyWrapper();
            p.dostuff();
        }
    }
}

Project3 references Project2 which references Project1. Everything builds without errors (except the error I described above in the C# code on the using MyProgram line).

Community
  • 1
  • 1
JasCav
  • 34,458
  • 20
  • 113
  • 170

3 Answers3

9

Just including the header from a pure C++ application isn't good enough. You need to wrap your unmanaged objects with managed ones in Project2 (i.e. public ref class YourClassDotNet)

#include "YourHeader.h"

namespace MyManagedWrapper
{
    public ref class YourClassDotNet
    {
    private:
        YourClass* ptr;

    public:
        YourClassDotNet()
        {
            ptr = new YourClass();
        }

        ~YourClassDotNet()
        {
            this->!YourClassDotNet();
        }

        !YourClassDotNet()
        {
            delete ptr;
            ptr = NULL;
        }

        void SomeMethod()
        {
            ptr->SomeMethod();
        }
    }
}
Chris Eberle
  • 47,994
  • 12
  • 82
  • 119
  • 2
    +1. Don't forget to mention that those methods of `MyManagedWrapper` will typically have to make some type conversion from .NET data types to native C++ data types and vice versa. – Doc Brown Apr 13 '11 at 21:56
  • @Doc: [at your service](http://stackoverflow.com/questions/5655936/how-to-use-c-cli-within-c-application/5656223#5656223), I guess? – sehe Apr 13 '11 at 22:05
  • Manually ensuring the native object is properly freed gets complicated fast. I suggest using a smart pointer instead of duplicating that logic in every wrapper class. [Here's one I wrote: scoped_ptr for C++/CLI (ensure managed object properly frees owned native object)](http://codereview.stackexchange.com/q/1695/2150) – Ben Voigt Apr 13 '11 at 22:23
  • @Doc Brown Yeah it gets pretty gnarly there – Chris Eberle Apr 13 '11 at 23:15
  • @Chris - Thank you for your answer. However, I am already doing this. Project1 and Project2 do build correctly. So, I may be failing to do "using" or an assembly reference correctly. Can you provide insight into this? From what I can tell (in your example) I would have a reference to Project2 from Project3, and I would have "using MyManagedWrapper" at the top of the C# class that wants to use the managed object. Is this correct? – JasCav Apr 13 '11 at 23:30
  • If you've already done this, all you need to do is add a reference to Project2 from your C# project, add `using MyManagedWrapper` to your C# source, and then simply call the managed C++ classes like normal. Also it's important that the .dll created by BOTH projects (native C++ and managed C++) are accessible at runtime for your C# project. – Chris Eberle Apr 13 '11 at 23:33
  • @Chris - I tried what you said...still having difficulty. I updated my question with more information. Thank you again for your help. – JasCav Apr 14 '11 at 15:47
  • @JasCav in the code you pasted, your class isn't public. That might have something to do with it. – Chris Eberle Apr 14 '11 at 15:50
  • @Chris - Whoops. Updated that. Still no luck. Question - what should the output of the wrapper be? Just a DLL? (It looks like I am getting Program1.dll in my output folder of Program2.dll...plus a lot of other log files and other random stuff. I'm wondering if it is building correctly despite not getting an compilation errors.) – JasCav Apr 14 '11 at 15:56
  • @JasCav You should have two DLLs. Program2 needs to pull in Program1.lib (which will link it to Program1.dll). The C# app doesn't need the .lib, because it will load Program2.dll at runtime. However since this is a compile-time error, I'd say that's not your issue. – Chris Eberle Apr 14 '11 at 16:12
  • Here's everything in my C# project to make it call the C++ project: {126ABE8D-016D-47CF-8B39-F0FAFFF637A6} managedcpp – Chris Eberle Apr 14 '11 at 16:13
  • @Chris - I appreciate all the help. Unfortunately, it looks like the Wrapper DLL is not building. I'm not receiving any compilation or linker errors, so I have no clue what is going on. I'm about ready to give up. – JasCav Apr 14 '11 at 21:16
  • I came across this answer and haven't seen an exclamation mark before a function before (!YourClassDotNet()). Can you please tell me what it is so I can google it? I tried but all I'm getting is posts about the null propogating calls (?.) – user819640 May 10 '18 at 09:00
4

Okay, well, I now feel dumb.

It turns out that the problem I was having (which I solved a couple weeks ago - just got around to updating this answer) was that I had included the header file (see Chris' answer for that), but I hadn't actually included the CPP file (which is empty other than including the header file).

Once I did this, the DLL compiled correctly and I could call the C++ functions (using C++/CLI) from my C# code.

JasCav
  • 34,458
  • 20
  • 113
  • 170
  • I tried your example and also generated a dll but I can't use it in the c# program. When I see the dll in the object browser it is empty. I don't think I quite understood your proposed solution. – dwbrito Sep 20 '12 at 10:43
  • 3
    I'm having the same problem right now, but can't understand your solution. Maybe you can explain it? Thank you – VladL Jan 22 '13 at 15:11
  • Header file needs to say translator "there's somewhere an implementation of the functionality you use"; and if there's dll from Project1 and header for it - I won't have to care in any CPP (maybe they are not in free access). Do I miss something, or author, or c# + c++ is more complex? Does the next make sense - wrap C++ header with C++/CLI to use in C# & no implementations files of that C++ part, only built library for your architecture? If yes, what CPP you are talking about? – amordo Jun 12 '22 at 19:38
  • @VladL OP means you need some CPP file in Project2 to make it 'builtable', they had only header file initially. 10 mins after my wonder I got what about answer is. OP doesn't mean CPP from dll. It's a cpp file just to make Proj2 work:) – amordo Jun 12 '22 at 19:48
3

Chris showed you the way to create a managed class that uses unmanaged code inside. There is a lot of that that you can do in C# using unsafe (it's just that hardly anyone does).

However, the reverse is also possible: using .NET types directly from a native type/function.

The thing to watch out for is that any managed pointer has to be marked as such. For this purpose, C++/CLI defines a special type of smartpointer gcroot<T> (mimicking boost::shared_pointer or std::auto_ptr in a way). So to store a managed string inside your C++ class, use the following:

#include <string>
#include <vcclr.h>
using namespace System;

class CppClass {
public:
   gcroot<String^> str;   // can use str as if it were String^
   CppClass(const std::string& text) : str(gcnew String(text.c_str())) {}
};

int main() {
   CppClass c("hello");
   c.str = gcnew String("bye");
   Console::WriteLine( c.str );   // no cast required
}

Note that (if it hasn't been fixed these days) you'll run into a bit of friction with the mismatch between managed null and C/C++ NULL. You can't easily type, as you would expect:

gcroot<Object^> the_thing;
...
if (the_thing != nullptr)
 ...
}

Instead you'd have to use the native style (the smart wrapper gcroot handles this)

gcroot< Object^ > the_thing;
if ( the_thing != NULL ) {} // or equivalently...
if ( the_thing ) {}

// not too sure anymore, but I thought the following is also possible:
if ( the_thing != gcroot<Object>(nullptr) ) {}

Note: I don't have access to a windows machine anywhere near these days, so I've quoted from memory

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633