Want to preface this by pointing out I am new to C++/CLI
We have one solution with an unmanaged C++ application project(We'll call "Application") and a C# .net remoting project which builds to a DLL(We'll call "Remoting"), and a CLI project for interfacing between the two(We'll call "Bridge").
Everything seems to be working, we have an IMyEventHandler interface in Bridge which successfully receives events from Remoting and can call methods in Application.
#ifndef EVENTS_HANDLER_INTERFACE_H_INCLUDED
#include "EventsHandlerInterface.h"
#endif
#define DLLEXPORT __declspec(dllexport)
#ifdef __cplusplus
extern "C"
{
#endif
DLLEXPORT bool RegisterAppWithBridge(IMyEventsHandler * aHandler);
DLLEXPORT void PostEventToServer(AppToServerEvent eventType);
DLLEXPORT void PollEventsFromServer();
#ifdef __cplusplus
}
#endif
In Bridge implementation we have a method for handling an event and depending on which event type it is, we will call a different method for handling that exact type:
void Bridge::OnReceiveServerEvent(IMyEvent^ aEvent)
{
// Determine event type
...
Handle_SpecificEventType();
}
This all is working fine so far. Once we call the handler for a known type of event, we can directly cast to it from the generic interface type. And this is where we start to see the issue. All these event types are defined in another DLL generated from C#. Simple events that have just ints or strings work just fine, but we have this SpecificEventType which contains a list of other types(We'll call "AnotherType") all defined in another DLL. All required DLL's have been added as references, and I am able to gcnew a AnotherType without it complaining.
However, once I try to get AnotherType element out of the list, we see the build error: "C2526 'System::Collections::Generic::List::GetEnumerator' C linkage function cannot return C++ class"
void Bridge::Handle_SpecificEventType(IMyEvent ^evt)
{
SpecificEventType ^e = (SpecificEventType ^)evt;
// We can pull the list itself, but accessing elements gives error
System::Collections::Generic:List<AnotherType ^> ^lst = e->ThatList;
// These all cause error
array<AnotherType ^> ^arr = lst->ToArray();
AnotherType ^singleElement = lst[0];
for each(AnotherType ^loopElement in lst){}
}
To clarify why we're doing this, we are trying to take managed events defined in a C# DLL and sent through .net remoting from a newer C# server, and "translate" them for an older unmanaged C++ application. So the end goal is to create a copy of the C# type "SpecificEventType" and translate it to unmanaged "SpecificEventType_Unmanaged" and just make a call to the application with that data:
// Declared in Bridge.h and gets assigned from DLLEXPORT RegisterGameWithBridge method.
IMyEventsHandler *iApplicationEventHandler;
// Bridge.cpp
void Bridge::Handle_SpecificEventType(IMyEvent ^evt)
{
... Convert SpecificEventType to SpecificEventType_Unmanaged
iApplicationEventHandler->Handle_SpecificEvent(eventUnmanaged);
}
This messaging all seems to be working and setup correctly - but it really doesn't want to give us the elements from the generic list - preventing us from pulling the data and building an unmanaged version of the event to send down to the application.
I hope I have explained this well, again I am new to CLI and haven't had to touch C++ for some years now - so let me know if any additional details are needed.
Thanks in advance for any assistance.
Turns out the issue was because all the methods in Bridge implementation were still inside of an extern "C" block. So much time lost - such a simple issue.
Related
My iOS application can use an optional external 3rd party library.
I thought of using this answer (Weak Linking - check if a class exists and use that class) and detect if the class exists before executing code specific to this library.
However, I found out that this external library is not written as Objective-C classes, but rather as C STRUTS and functions.
Is there a similar technique that would allow me to check if a C Strut or function exists? Or some better alternative to see if this library is present at runtime?
structs are compile-time artifacts. They tell the compiler how to lay out a region of memory. Once that is done, structs become unnecessary. Unlike Objective-C classes which have metadata, structs have no runtime presence. That is why it is not possible to detect them at runtime.
You can check if a dynamic library is present by calling dlopen, and passing its path:
void *hdl = dlopen(path_to_dl, RTLD_LAZY | RTLD_LOCAL);
if (hdl == NULL) {
// The library failed to load
char *err = dlerror(); // Get the error message
} else {
dlclose(hdl);
}
If dlopen returns NULL, the library cannot be loaded. You can get additional info by calling dlerror. You need to call dlclose after you are done.
AFAIK a classical C function has to exist. It is statically bound during the linking process and it is not, like Objective-C mehtods, dynamically bound on runtime.
So when the code compiles AND links without errors or warnings, then you should be fine.
The same for structs.
I'm writing a CLR wrapper for an unmanaged C++ library.
There are two files I'm including from the unmanaged lib:
//MyCLIWrapper.h
#include "C:\PATH\TO\UNMANAGED\Header.h"
#include "C:\PATH\TO\UNMANAGED\Body.cpp"
Then I'm writing CLI implementations for the unmanaged library functions:
//MyCLIWrapper.h
// includes ...
void MyCLIWrapper::ManagedFunction()
{
UnmanagedFunction(); // this function is called successfuly
}
However, if my Unmanaged function contains calls to other functions that are defined in other unmanaged header files. This causes a compiler linkage error.
If I add includes to the unmanaged headers that define these functions, my errors get resolved. However, there is a lot of functions, and a lot of includes required.
Is there a different way to approach this?
EDIT:
P.S.
My managed code is in a separate Visual Studio project (output - DLL), and the compile settings are set to /CLR. Unmanaged code is in a separate Win32 project (output - DLL).
Also, after more research I concluded that theoretically I could set my Win32 unmanaged project to CLR and just add my managed classes and headers in there as an entry point, and then it would all compile into a single DLL file. That would probably solve (?) the linkage errors. However, I would prefer to preserve the loose coupling as well as the additional series of problems that can raise from setting my unmanaged project to CLR.
EDIT #2:
The unmanaged class that I'm referencing (body.cpp, header.h) contains includes to the required files that define the functions that are causing the problems. However, my managed code doesn't pick up on the includes that are in the unmanaged body.cpp and header.h.
Linker errors are a different kettle of fish from compiler errors. You forgot to document the exact linker errors you see, but a very common mishap when you compile code with /clr in effect is that the default calling convention for non-C++ member function changes. The default is __clrcall, a convention that's optimized for managed code. While functions compiled without /clr defaults to __cdecl. Which changes the way the function name is mangled. You see this back in the linker error message, is shows that it is looking for a __clrcall function and can't find it.
You'll need to either explicitly declare your functions in the .h file with __cdecl. Or tell the compiler that these functions are not managed code. Which is the best way to tackle it:
#pragma managed(push, off)
#include "unmanagedHeader.h"
#pragma managed(pop)
Solution was fairly simple:
I added both unmanaged and managed projects to a single solution in Visual Studio.
Set the unmanaged project's "Configuration Type" to "Static Library" (.lib).
Right click on the managed project -> References -> Add Reference -> Projects -> -> Add Reference.
Then in my managed class, I include the header.h (only) just like I did in my question.
Compiled successfully!
Thank you
I have an unmanaged c++ DLL that calls c# code through a managed c++ wrapper. The unmanaged c++ DLL is a plug-in for some application (outside my control). When this application calls the unmanaged c++ DLL everything works fine until the managed c++ code tries to use the c# code. Then it crashes.
I have written a test application that does the same thing as the application, that is, it calls the unmanaged c++ DLL. This works fine.
The code is as simple as it could be:
unmanaged c++:
extern "C" __declspec(dllexport) void UnmanagedMethodCalledUponByApplication()
{
new Bridge();
}
managed c++:
Bridge::Brigde()
{
gcnew Managed(); // This line crashes
}
c#:
public class Managed
{
}
I have tried to add a try-catch (...) block around the problematic line but it doesn't catch the error.
If I replace the gcnew Managed(); line with MessageBox::Show("Alive!"); it works fine. So my guess is that something is wrong with my c# project settings.
I have tried to compile it with different platforms (Any CPU and x86). I have tried to change target framework. I have tried to call a static method in Managed instead of using gcnew. Still crashing.
Any ideas what might be the problem?
Update:
After advise in comments and answer, I attached the debugger. Now I see that I get a System.IO.FileNotFoundException saying that the managed DLL (or one of its dependencies) can't be found.
Here's a guess: The DLLs are placed together, but they are not located in the current directory. The unmanaged c++ DLL is loaded correctly since the main application specifies the path to it. The managed c++ is actually a lib, so that code works fine as well. But when the managed c++ tries to load the c# DLL it looks for it in the wrong directory.
Update:
The way to fix this is to load the c# DLL dynamically, using reflection.
extern "C" __declspec(dllexport)
Yes, that's a cheap and easy way to get the compiler to generate the required stub that loads and initializes the CLR so it can execute managed code. Problem is, it doesn't do anything reasonable to deal with exceptions thrown by managed code. And managed code likes throwing exceptions, they are a terrific trouble-shooting tool. That stops being terrific when there's no way for you to retrieve the exception information.
The best you could possibly do from native code is use the __try/__except keywords to catch the managed exception. Exception code is 0xe0434f4d. But that still doesn't give you access to the information you need, the exception message and the holy stack trace.
You can debug it. Project + Properties, Debugging, change the Debugger Type to "Mixed". Then Debug + Exceptions, tick the Thrown checkbox for CLR Exceptions. The debugger stops when the exception is thrown so you can see what's wrong.
Getting decent diagnostics after you shipped your code requires a better interop mechanism. Like using COM interop or hosting the CLR yourself.
I have added a class to my program and tested it. I was really surprised that there was any real errors. Here is the code:
#pragma once
#include "Iingredient.h"
#include <string>
#include <vector>
using namespace std;
ref class Recipe{
private:
string partsName;
vector<Iingredient> ing;
public:
Recipe(){}
};
And here are the errors:
Error 23 error C4368: cannot define 'partsName' as a member of managed
'Recipe': mixed types are not
supported c:\users\user\documents\visual studio
2010\projects\smestras2_l1\Recipe.h 10 1 file2_L1
Error 24 error C4368: cannot define 'ing' as a member of managed
'Recipe': mixed types are not
supported c:\users\user\documents\visual studio
2010\projects\smestras2_l1\Recipe.h 11 1 file2_L1
I googled a bit and found out that its about managed and unmanaged code.
How to fix this? Is it related with manged and unmanaged code or not? if so how?
I agree with others: you shouldn't use C++/CLI in most circumstances, you should use C# (or another "normal" managed language) for that (assuming you want to write a .Net application). C++/CLI is useful mostly in special circumstances, like interoperating between managed and unmanaged code.
If you're sure you want use C++/CLI, you can't put native classes into managed ones. But you can put pointers to native classes there:
ref class Recipe{
private:
string* partsName;
vector<Iingredient>* ing;
};
The code above works. But you have to keep in mind that those are normal native C++ pointers and that means you have to manually delete them. To do that property, you should read about how destructors and finalizers work in C++/CLI.
When defining ref class Recipe, you made it managed. But std::string and std::vector are umanaged types. You are trying to declare native variables in managed class, but it is not allowed.
Seems, you are novice in C++. Just, don't use C++/CLI. Consider C#, if you target .Net or unmanaged C++.
You cannot use unmanaged types in a managed class, ref keyword, because the heap and the managed heap are two different types of memory. To solve this specific problem you can use a managed string type (System::String^). The carrot tells the compiler that the type is a handle to a managed class.
Another way of going about this problem is to use pointers, that way the pointer will be on the managed heap, and the actual memory for that object will be in the standard unmanaged heap, which is where Recipe is located.
To construct your Recipe class you would have to do
Recipe^ recipe = gcnew Recipe();
With no idea of c++-cli, I can try and guess that the problem is that you are defining a reference type and trying to use C++ types inside (a std::string field) rather than whatever the equivalent managed type is (String?).
The reason why this can be problematic is that it mixes resource management approaches. Reference types are intended to be used out of a garbage collector, and unless you implement a destructor or IDisposable will just be ignored once proven that the last reference is lost. On the other hand, to be able to manage the memory in the internal field the std::string destructor must be called.
I want to make a C++/CLI wrapper of some C++ class.
The problem is that I want the ref class to be initiated with a reference of the c++ object:
A (A& a);
This works fine as long as it is in the same aseembly and used in the same project.
But when I try to reference that project from a different project, and initiate a ref-class with a reference to an object from there, it won't recognize the types properly, and doesn't recognize the correct constructor.
Any ideas of how to solve this ?
I don't think you will be able to use a reference to the C++ object in this instance, but you can pass a pointer to the C++ object across assembly boundaries by storing it in an IntPtr and then retrieving it using static_cast.
class nativeA; // Pre declaration
A (IntPtr a)
{
nativeA * nativePtr = static_cast<nativeA*>(a.ToPointer());
// Do something with nativeA
}