VS Solution(all the credits for Tim Anderson)
Contents
Important note
This article replaces the previous one with the same title.
Indeed the previous C++/CLI wrapper implementation had a flaw that created memory corruption.
This issue has been fixed in the following implementation.
Moreover I’ve used it as an opportunity to greatly enhance the content, especially the description of the C++/CLI wrapper implementation, design rationales being now included.
Update note
The article has been updated on the 19th of June 2016 to integrate 3 fixes:
- the
YahooAPIWrapper
‘s destructor is correctly declared in the header file to avoid memory leaks, - the
YahooAPIWrapper
‘s definition/cpp file does not redefine the class and the__declspec(dllexport)
metadata has been moved to the header file to avoid compilation errors, - the
YahooAPIWrapper
‘s and native C++ program’s implementations have been updated to take into account the new fields names of Yahoo API to avoid runtime exceptions.
Introduction
When it comes to software development in a professional environment, heterogeneity is the rule not the exception: you often need to interact with systems developed with other technologies.
I’ve been recently faced with such a situation: a team that uses only native C++ needed to retrieve data using the object-oriented API of another team that develops only in .Net with C#.
This is a relatively uncommon scenario (just look at the number of articles on the subject), the standard case being new systems based on the .Net platform, developed in C# or VB.Net, needing to interact with legacy systems developed in native C++.
I’ve used the C++/CLI platform due to its unique ability to mix managed (.Net) and native code in one place and is then the ideal tool for building bridges between these two worlds using simple wrappers: the native face of the wrapper can be consumed by the legacy components and its managed face can directly use the C# API.
In this article I’ll illustrate how I’ve tackled the issue by building a simple C++/CLI wrapper, using a similar use-case: market-data retrieval from Yahoo.
(All the source code of this article is available in this ZIP archive)
The C# library
Here is a simple C# class that retrieves financial data using the Yahoo finance API.
Source code
using System.Net; // WebClient using System.Globalization; // CultureInfo public class YahooAPI { private static readonly WebClient webClient = new WebClient(); private const string UrlTemplate = "http://finance.yahoo.com/d/quotes.csv?s={0}&f={1}"; private static double ParseDouble(string value) { return double.Parse(value.Trim(), CultureInfo.InvariantCulture); } private static string[] GetDataFromYahoo(string symbol, string fields) { string request = string.Format(UrlTemplate, symbol, fields); string rawData = webClient.DownloadString(request).Trim(); return rawData.Split(','); } public double GetBid(string symbol) { return ParseDouble(GetDataFromYahoo(symbol, "b")[0]); } public double GetAsk(string symbol) { return ParseDouble(GetDataFromYahoo(symbol, "a")[0]); } public string GetCapitalization(string symbol) { return GetDataFromYahoo(symbol, "j1")[0]; } public string[] GetValues(string symbol, string fields) { return GetDataFromYahoo(symbol, fields); } }
Nothing to notice that has to do with our problematic, this is just plain-vanilla C#.
Compilation
We compile it to obtain our “YahooAPI.dll” managed DLL:
csc /target:library YahooAPI.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.
The C++/CLI wrapper
Header file
Here is the header file (that will be consumed by the C++/CLI and native C++ codes) for the wrapper:
class YahooAPIWrapperPrivate; class __declspec(dllexport) YahooAPIWrapper { private: YahooAPIWrapperPrivate* _private; public: YahooAPIWrapper(); public: ~YahooAPIWrapper(); public: double GetBid(const char* symbol); public: double GetAsk(const char* symbol); public: const char* GetCapitalization(const char* symbol); public: const char** GetValues(const char* symbol, const char* fields); };
Nothing special except:
- the “__declspec(dllexport)” metadata that asks the compiler to publicly export the whole interface of the class and to generate a “.lib“ file we’ll use to link the native C++ program with the C++/CLI DLL
- the forward declaration of the “YahooAPIWrapperPrivate” class, needed so that the compiler knows “YahooAPIWrapperPrivate” refers to some class defined somewhere; where it is and what it is is irrelevant for the compiler at this stage, it only needs to know it has to emit code that allocates a memory area whose size is the size of any pointer on this platform.
Source code
Here comes the interesting part, our C++/CLI wrapper that uses the C# library:
#using "YahooAPI.dll" #include <msclr\auto_gcroot.h> #include "YahooAPIWrapper.h" using namespace System::Runtime::InteropServices; // Marshal class YahooAPIWrapperPrivate { public: msclr::auto_gcroot<YahooAPI^> yahooAPI; }; YahooAPIWrapper::YahooAPIWrapper() { _private = new YahooAPIWrapperPrivate(); _private->yahooAPI = gcnew YahooAPI(); } double YahooAPIWrapper::GetBid(const char* symbol) { return _private->yahooAPI->GetBid(gcnew System::String(symbol)); } double YahooAPIWrapper::GetAsk(const char* symbol) { return _private->yahooAPI->GetAsk(gcnew System::String(symbol)); } const char* YahooAPIWrapper::GetCapitalization(const char* symbol) { System::String^ managedCapi = _private->yahooAPI->GetCapitalization(gcnew System::String(symbol)); return (const char*)Marshal::StringToHGlobalAnsi(managedCapi).ToPointer(); } const char** YahooAPIWrapper::GetValues(const char* symbol, const char* fields) { cli::array<System::String^>^ managedValues = _private->yahooAPI->GetValues(gcnew System::String(symbol), gcnew System::String(fields)); const char** unmanagedValues = new const char*[managedValues->Length]; for (int i = 0; i < managedValues->Length; ++i) { unmanagedValues[i] = (const char*)Marshal::StringToHGlobalAnsi(managedValues[i]).ToPointer(); } return unmanagedValues; } YahooAPIWrapper::~YahooAPIWrapper() { delete _private; }
Some explanations:
- the hats “^” represent managed references, i.e. they point to managed objects (like “System::String“) allocated on the managed heap; they are to managed objects what native pointers are to native objects allocated on the native heap
- the “gcnew” operator is used for allocating objects on the managed heap, whereas the “new” operator allocates only on the native heap
- “cli::array” is the C++/CLI representation of a managed array
- “auto_gcroot” is a wrapper around a managed reference: you can’t directly embed a managed reference inside a native type especially because the way memory is handled in the native and managed worlds is quite different; moreover, compared to “gcroot“, which too avoid explicit management like pinning, “auto_gcroot” is automatically disposed when going out of scope
- the “StringToHGlobalAnsi” method converts a managed “System::String” which is made of UTF-16 chars to an array of ANSI chars it allocates on the native heap; it returns a pointer to this array as an “IntPtr” which is a managed wrapper around a native pointer that we obtain with the “ToPointer” method as a “void*“
- moreover you may wonder why we use “const char*” instead of “std::string“: because publicly exposing STL types is brittle as their implementation could differ from vendor to vendor and even between different versions from the same vendor
Well, after all, there is quite a bunch of things to explain. 🙂
But if you use C++/CLI on a regular basis you’ll quickly become familiar with this at first cryptic stuff.
Design rationales
You may have one more question: why the gcroot field has been isolated in its own structure?
First you should know that in native C++ you must declare all the members of a type, including the private part.
This may seem strange to C# programmers, because in C# the private part is hidden; but for native C++, header files are more than a simple description of the interface of the types, they describe their memory structure too, then all the information must be available to the calling code so that it is able to correctly allocate the memory for the instances of the types, otherwise you’ll get memory corruption (believe me you don’t want to live such a situation ;)).
But in that case, why not simply add the gcroot field to the class declaration?
Because “gcroot” is pure C++/CLI stuff that has no sense for native C++ so your code won’t even compile; moreover, even if it compiled, this would be an ugly leak of the abstraction we’re trying to build.
With this design we’ve hidden all the C++/CLI stuff inside the “YahooAPIWrapperPrivate” structure so that our wrapper has an interface compatible with native C++.
Finally, I’ve later discovered that I’ve only reinvented the wheel: the PIMPL principle used in native C++ development; so it seems like one more compelling argument in favor of this design.
OK for using an additional structure, but why a pointer to the structure instead of an instance?
Because as I’ve said you must fully describe the memory layout of your types, this includes the memory layout of objects inlined into the types, so here the compiler of the native C++ code should have to know the layout of the “YahooAPIWrapperPrivate” type too, so it should know about … gcroot.
We have not this issue with pointers because they all have the same size; indeed the values of pointers are memory addresses: their size depends only on the platform (Intel 8088 (16 bits), x86/IA-32 (32 bits), IA-64 (64 bits)…); then the compiler can allocate a fixed amount of memory per pointer without having to worry about the objects pointed to.
Compilation
Here is how to compile the C++/CLI wrapper:
cl /clr /LD YahooAPIWrapper.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01
for Microsoft (R) .NET Framework version 4.00.30319.18034
Copyright (C) Microsoft Corporation. All rights reserved.
YahooAPIWrapper.cpp
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:YahooAPIWrapper.dll
/dll
/implib:YahooAPIWrapper.lib
YahooAPIWrapper.obj
Creating library YahooAPIWrapper.lib and object YahooAPIWrapper.exp
- “/clr” triggers the C++/CLI mode, by default CL acts as a native C++ compiler
- “/LD” asks CL to generate a DLL instead of an EXE
We now have our “YahooAPIWrapper.dll” DLL ready to be used.
The native C++ application
Finally here is the native C++ application that uses the C# API through the C++/CLI wrapper.
Source code
#include <iostream> #include "YahooAPIWrapper.h" int main() { const char* stock = "GOOG"; YahooAPIWrapper yahoo; double bid = yahoo.GetBid(stock); double ask = yahoo.GetAsk(stock); const char* capi = yahoo.GetCapitalization(stock); const char** bidAskCapi = yahoo.GetValues(stock, "abj1"); std::cout << "Bid: " << bid << std::endl; std::cout << "Ask: " << ask << std::endl; std::cout << "Capi: " << capi << std::endl; std::cout << "BidAskCapi[0]: " << bidAskCapi[0] << std::endl; std::cout << "BidAskCapi[1]: " << bidAskCapi[1] << std::endl; std::cout << "BidAskCapi[2]: " << bidAskCapi[2] << std::endl; }
Compilation
Compilation is straightforward:
cl test.cpp YahooAPIWrapper.lib
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
test.cpp
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\xlocale(323) : wa
rning C4530: C++ exception handler used, but unwind semantics are not enabled. S
pecify /EHsc
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:test.exe
test.obj
YahooAPIWrapper.lib
Creating library test.lib and object test.exp
As you can see, from the point of view of the native C++ application, things are transparent (be it the source code or the compilation process), there is no trace of any .Net stuff (except the name of the wrapper I’ve deliberately made explicit), just a plain old native C++ API.
Execution
And here are the results:
test.exe
Bid: 821.2
Ask: 822
Capi: 270.8B
BidAskCapi[0]: 821.20
BidAskCapi[1]: 822.00
BidAskCapi[2]: 270.8B
Returning data structures
Following a question in the comments, I’ve built a small sample to illustrate how you could return data-structures instead of simple types and arrays.
The C# library
using System; // Random namespace StockMarket { public struct Quote { public double Bid { get; set; } public double Mid { get; set; } public double Ask { get; set; } public double Capitalization { get; set; } } public class API { private Random randomGenerator = new Random(); public Quote GetQuote(string symbol) { double mid = randomGenerator.Next(95, 105); return new Quote { Bid = mid - 0.1, Mid = mid, Ask = mid + 0.1, Capitalization = mid * 10e6 }; } } }
The C++/CLI wrapper
Header file
class StockMarketAPIWrapperPrivate; struct __declspec(dllexport) Quote { double bid; double mid; double ask; double capitalization; }; class __declspec(dllexport) StockMarketAPIWrapper { private: StockMarketAPIWrapperPrivate* _private; public: StockMarketAPIWrapper(); public: ~StockMarketAPIWrapper(); public: const Quote GetQuote(const char* symbol); };
Quote
is the native equivalent of the managed StockMarket.Quote
data-structure.
Definition file
#using "StockMarketAPI.dll" #include <msclr\auto_gcroot.h> #include "StockMarketAPIWrapper.h" class StockMarketAPIWrapperPrivate { public: msclr::auto_gcroot<StockMarket::API^> API; }; StockMarketAPIWrapper::StockMarketAPIWrapper() { _private = new StockMarketAPIWrapperPrivate(); _private->API = gcnew StockMarket::API(); } const Quote StockMarketAPIWrapper::GetQuote(const char* symbol) { StockMarket::Quote managedQuote = _private->API->GetQuote(gcnew System::String(symbol)); Quote nativeQuote; nativeQuote.bid = managedQuote.Bid; nativeQuote.mid = managedQuote.Mid; nativeQuote.ask = managedQuote.Ask; nativeQuote.capitalization = managedQuote.Capitalization; return nativeQuote; } StockMarketAPIWrapper::~StockMarketAPIWrapper() { delete _private; }
The native C++ application
#include <iostream> #include "StockMarketAPIWrapper.h" int main() { const char* stock = "GOOG"; StockMarketAPIWrapper API; Quote quote = API.GetQuote(stock); std::cout << "Bid: " << quote.bid << std::endl; std::cout << "Mid: " << quote.mid << std::endl; std::cout << "Ask: " << quote.ask << std::endl; std::cout << "Capi: " << quote.capitalization << std::endl; }
Compilation
C# library
csc /target:library StockMarketAPI.cs
Compilateur Microsoft (R) Visual C# version 1.3.1.60616
Copyright (C) Microsoft Corporation. Tous droits réservés.
C++/CLI wrapper
cl /clr /LD StockMarketAPIWrapper.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1
for Microsoft (R) .NET Framework version 4.07.2633.0
Copyright (C) Microsoft Corporation. All rights reserved.
StockMarketAPIWrapper.cpp
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:StockMarketAPIWrapper.dll
/dll
/implib:StockMarketAPIWrapper.lib
StockMarketAPIWrapper.obj
Creating library StockMarketAPIWrapper.lib and object StockMarketAPIWrapper.exp
Native C++ application
cl test.cpp StockMarketAPIWrapper.lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
test.cpp
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xlocale(341): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\exception(359): warning C4577: 'noexcept' used with no exception handling mode specified; termination on exception is not guaranteed. Specify /EHsc
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:test.exe
test.obj
StockMarketAPIWrapper.lib
Creating library test.lib and object test.exp
Execution
Here are some samples of runs:
test.exe Bid: 103.9 Mid: 104 Ask: 104.1 Capi: 1.04e+09 test.exe Bid: 101.9 Mid: 102 Ask: 102.1 Capi: 1.02e+09 test.exe Bid: 99.9 Mid: 100 Ask: 100.1 Capi: 1e+09
Conclusion
As you’ve seen, using C++/CLI wrappers for native to managed interop is rather straightforward, the only difficulty being the plumbing code necessary for converting to and from managed types.
The other way around, that is managed to native interop, is almost identical except that you’ll wrap native objects and that the called side will be managed and the calling side will be native.
Though wrapping with C++/CLI is quite simple it remains a tedious and error-prone process: imagine if you need to export dozens or more managed classes!
You could of course factorize the repetitive code, like conversions, by building helpers but you’ll still need to implement the classes’ structures by-hand, so unless you have few types you should not go down this path.
Fortunately there is tools, like SWIG, that can automate the process for this kind of scenario by generating the plumbing layer; they will isolate you from a significant part of the low-level details, probably avoiding you some headaches (maybe causing others ;)), so taking the time to learn them should be worth it on the long term if you have a big project.
If you’ve used this approach in a real-life project too, I’d be really interested in hearing from you, so please let a comment with your feedback.
If you have any remark, question or suggestion feel free to let a comment as well.
Thanks for reading! 🙂
Having read your article, “Using C# from native C++ with the help of C++/CLI”,
I created a C# class:
namespace foo
{
public class Class1
{
public void Method1()
{
Console.WriteLine(“Method1”);
}
}
I built it into MyDll.dll.
I created a C++ header called Wrapper.h:
#using “MyDll.dll”
#include
using namespace System::Runtime::InteropServices;
class __declspec(dllexport) Wrapper
{
private: msclr::auto_gcroot myDll;
};
and a C++ file, Wrapper.cpp:
#include “wrapper.h”
void Wrapper::Method1()
{
}
There is also an empty (as of now) C++ file which will correspond to your test.cpp file. All three projects are contained within the same solution.
But the solution won’t build; I get these error messages, which refer to the wrapper.h file:
error C2065: ‘MyDll’ : undeclared identifier
error C2059: syntax error : ‘>’
error C2143: syntax error : missing ‘;’ before ‘}’
They refer to this line:
private: msclr::auto_gcroot myDll;
If I comment out this line, then the solution builds. What am I doing wrong?
Robert Rotstein
Hello Robert, I’ve sent you an email.
Hi,
I am having the same issue. can you please help me out?
thanks,
Dragos
Hi,
if I remember well there was an issue in the initial version of the article.
Are you using the latest version of the code?
If so can you please share your code, or a similar one that reproduce the issue?
Thanks.
Mickael
I also have the same problem when compiling the source codes. It prompts “1>StringClassWrapper.cpp(11): error C2065: ‘StringClass’ : undeclared identifier
1>StringClassWrapper.cpp(11): error C2059: syntax error : ‘>'”
I use MS VS C++ 2010 Express to develop.
How have you referenced the
StringClass
type from the wrapper source file?hey I have same as you had if you can provide me solution plz mail me
Hi,
the article has been updated twice since then and I’ve not heard of this issue again.
Could you please give more details.
eroor is
fatal error C1190: managed targeted code requires a ‘/clr’ option
and if u can provide me ur email i can mail you the project
hey I have mailed you the project in which i am facing problem
There is plenty of issues with the code:
GetUrl
instead ofGetBid
wchar_t*
seems good:msclr::auto_gcroot<chrome::ChromeUrl^>
andgcnew chrome::ChromeUrl()
Here is the fixed wrapper :
And a native C++ main that works with the fixes:
hey thanks for the reply
but I have a question Now. Yesterday i solve that problem by adding my c# project as a reference so my ques. is that .. do I need to do that(adding my c# project as a reference).
Really thanks for you solution again….
Yes I’ve too added it as a reference which seems the right solution to benefit from VS integration and automation.
So you can stick with it.
As you return string , int , double , can you please give a example to return class .
Like we have a class in c# , with 2-3 properties , I want to return this call in managed c++ .
Hi Shakir,
following your comment I’ve added a full section for this use-case.
Have a look at it, hopefully it will do the job. 🙂
For a similar issue, I selected the build check box for the projects in Configuration Manager, and the error went away.
Hello,
Thanks for the info. I am unable to add iostream header in the mananged wrapper of the c++/cli. The inclusion brings the error when built:
C:\Programs\Microsoft Visual Studio 10.0\VC\include\eh.h(78): error C2059: syntax error : ‘(‘.
How to solve this?
Hello,
what is the extension of your file?
If you’ve used “.c” you can either:
– change it for “.cpp”
– compile with the “/Tp” (note the upper “T”) option to help “cl” understand you’re working with C++: cl /Tp test.c YahooAPIWrapper.lib
Let me know if your issue is fixed…
Thanks
Hi,
Thanks for the info.
File ext is .cpp, The project builds when #include is commented,
Using /Tp did not help same error…:-(
Are you using VS to build?
If yes, please check the setting: “Properties -> C/C++ -> Advanced -> Compile As -> Compile as C++ Code”
Yes, its Compile as C++ Code and yet the same problem.
Please send me your whole project by email at postmaster@pragmateek.com so that I can have a more thorough look at the issue. Thanks
Thanks a lot, it seems to have corrected it self now
Glad your issue has been fixed. 🙂
I have tried your source code and Compiled a DLL, When I tried to call the DLl from my python code it gives me a error of Method ‘GetAsk’ not found. could you please explain how can I expose the method GetAsk to the non C++ functions?
Hi John, thanks for reading
The Yahoo wrapper is a class so its methods are not directly usable from Python.
A possible solution is to create a C interface:
And we should be able to use it like this:
But I’ve tested it and the constructor fails when allocating the private part:
So you could test it and see if you have the same behavior or if it works on your side.
But maybe you’re not using ctypes, if so could you elaborate more on your process?
Thanks
Ok I’ve tested more thoroughly and think I’ve found the issue.
This is related to .Net assemblies probing.
I have a fix and will post this ASAP.
Stay tuned! 🙂
Here is my final answer: http://pragmateek.com/if-your-plumbing-doesnt-work-youre-just-not-using-enough-pipes/
Please read it and share your feedback.
Thanks 🙂
Hi,
Thank you for the post. Can this technique be applied if I want to pop up a Window Form written in C# on .NET framework in legacy native C++ ?
Hi Tran,
yes you can use whatever part of .Net you want, even some WPF. 😉
The only thing you have to do is bridging the native code and the .Net code with the kind of plumbing described in this article.
Let me know if you have any issue…
Hi,
First, thanks for your post.
Also, do you know if it is possible to pass to a C++ function, a C# object as a parameter ? I mean in your example, lets take private static double ParseDouble(string value) function, you use a c# string as a parameter and I wanted to know if it is possible to pass a C# object like private static double ParseDouble(Object value) and interop with it in C++ dll.
Hi Romain,
yes it’s possible but is more or less complex depending on what you intend to do.
If you want to pass a structure things are relatively straightforward: http://msdn.microsoft.com/en-us/library/awbckfbz.aspx
If you want to invoke methods then you’ll have to write a C++/CLI wrapper around your C# class, it will forward the calls from the native C++ side to the managed side.
Hope this helps…
Actually I was thinking to use IntPtr and Marshal.GetNativeVariantForObject() to pass a pointer to the C++ library but I have to test. Do you think that is possible ?
Anyway thanks for help and links.
IMHO it won’t move the needle a lot, and even if you can make it works it will be far more intrusive than building a wrapper that will cleanly uncouple the two parts.
If you have a recurrent need for this kind of interop scenario you should seriously consider using SWIG to generate the plumbing.
Ok thanks for information.
And a last question, did you know how to use c# enum type in a C++ unmanaged function ?
The simplest thing you can do is to redefine the enum on the native C++ side.
This way you can transparently pass the enum values.
You only need to take care to keep the two enum definitions in sync.
But how you pass the value as a parameter ?
For example :
C# :
public enum A
{
VALUE_1 =0,
VALUE_2,
VALUE_3
}
and i have a function like that : void function_A(A eValue);
So if i understood , I have to do the same structure on the C++ side like that :
enum A
{
VALUE_1 = 0,
VALUE_2,
VALUE_3
};
But how can I call the function function_A ? I mean how i can use the gcnew directive in order to pass the enum ?
If you’re using DllImport you can declare the function this way:
Ok thanks for the tips.
Anither question concerning the way to pass the parameters between the wrapper and the managed application ; when you do _private->yahooAPI->GetBid(gcnew System::String(symbol)); I think “symbol” is passed by value the a copy is done. If I use “ref” in the c# application, do you think when I call the function by the C++ application, the parameter will be passed by reference (no copy)?
symbol
is passed by copy of reference.If you want to pass by reference of reference you can declare the
symbol
parameter asref
on the C# side.Here is a minimal example:
A.cs:
test.cpp:
Output:
Thank you for this post,
Let me try to explain what I need: I have a large project written in c++ that I would like to leave untouched. I want to replace the current, functional Win32 user interface with a new WPF module I started implementing.
I would like to encapsulate the WPF part and interact with it using SendMessage functions. I can probably write the wrapper as a Win32 window DLL to solve this. However, I wander how I can deal with the events from the WPF part. Sould I write the handlers inside the wrapper?
Thank you in advance.
Shaul Eizikovich
Hi Shaul,
First, to develop your WPF UI you should use C#, don’t be tempted by developing your WPF UI in C++/CLI even if you are fluent in C++, for at least 2 reasons:
– limited WPF integration in Visual Studio (e.g. no designer support)
– a far smaller C++/CLI developer community and believe me WPF already comes with its own set of issues 🙂
Secondly you should clearly isolate the Win32 UI layer from the “business” layer and have a self-contained business project with no dependency on the UI.
Once you have a well designed native application remains the wrapping part.
The best way of tackling this depends on the number of entry-points of the C++ business layer.
If you have few entry-points:
– not object oriented: simply build a static C# wrapper that uses DllImports
– object-oriented: write a small C++/CLI wrapper
If you have more entry-points and specifically object-oriented then you should use SWIG which will produce a C# wrapper automatically.
Once you have a wrapper the communication between the two layers should be quite obvious, you simply use the wrapper from the WPF application in a transparent manner, something like:
Hope this helps…
Thank you pragmateek for your comprehensive reply.
Thinking over and over again about the project I see it now as much simpler one:
1. As you say, WPF window will be done as pure C# project using Visual Studio. I compile it as DLL.
(Q. Can one C# DLL contain all my WPF windows or do I have to create a DLL per-window?)
2. Around the WPF windows (that is, one or more DLLs) I write a C++/CLI wrapper along the lines you suggested. The wrapper does not have to be Object Oriented, so I could write it in C# as you suggested but I’m much more fluent with C++.
3. The wrapper contains a hidden Win32 window to serve as target to application’s messages.
4. The wrapper will be compiled as DLL with two interface functions: A constructor and GetWindowHandle that will return a handle to the hidden window.
5. The wrapper will handle all events from the WPF windows.
1) The WPF part is typically a full-fledged application, like was your Win32 application, not just a DLL.
This application will contain the main entry-point and all the stuff related to the UI, including all your windows.
Of course you can isolate the entry-point, the managed Main, in another project but it’s often useless if your windows are only used from this main.
2) If your functional API is not object oriented and not too big (because you really don’t want to do all the marshalling yourself) you can indeed use DllImports, something like:
3) 4) 5) Why do you want to use window message passing between your WPF UI and your native layer?
I’m obviously missing something but the WPF UI should handle events like clicks on buttons and in response interact with the legacy code through the wrapper to compute values for example.
I don’t know what’s your exact use-case but typically here is the workflow:
– the user starts the application exe file which starts the UI immediately, starting the WPF message loop (like the Win32 message loop) on the main thread
– the WPF interface displays some inputs and some action buttons
– the user fills in the inputs and clicks an action button
– the button click event’s handler reads the input and calls a method of the wrapper and it gets some new values in return
– it displays these new values on the interface.
This assume that you have implemented a strict separation of concerns in your application: as an example a “business” method like “compute_value” should not interact with the UI, be it to read or write data.
Oh and another important thing: what you want to do is a relatively standard scenario: calling legacy native C++ from managed C# (your WPF UI).
This is demonstrated in this sample: http://code.msdn.microsoft.com/windowsdesktop/CppCLINativeDllWrapper-29c32acd
This article is about the reverse scenario, more involved, i.e. calling managed C# from native C++.
I think this is simpler to have the managed part of your application, i.e. the WPF interface, to be the “master” part, i.e. the one that will drive things.
You could do the reverse, i.e. driving your managed C#/WPF from native C++, using the technique demonstrated in this article, but this is really not the easy way. 🙂
I really appreciate your help. This is an impressive manifestation of good will!
My current project holds a large C++ executable where the UI is only one, relatively small, module. I chose to implement the CU (Control Unit) of the executable as an invisible Win32 window so that it will serve as a hub to messages from the other units, including the UI.
The UI (Currently implemented as another Win32 window) interacts with the CU through messages. I think it is a good way to separate units and to enable development of units in parallel. Anyway, this is what we’ve got.
When The application starts, it creates the modules one by one, including the UI module.
It is of course possible to reverse the order so that the UI starts the application, leaving the CU outside as a DLL or rewriting it in C++ (Compiling it with /clr is impossible since it contains and ). However, I don’t think it will be wise, especially not being a C# person.
So I tried to create a C++/CLI wrapper to my (preliminary) WPF interface and the call the wrapper constructor from a plain C++ main. This failed. I suspect that this is because I didn’t declare my main thread as STA. I probably have to do it from the wrapper, calling the WPF in a new thread. No idea as how to do it. Wish me luck.
Shaul
Ah OK I better understand your architecture.
Isolating the modules this way and communicating through messaging is indeed a good design with full uncoupling.
To run your WPF UI in another thread you can explicitly set the apartment-state with something like:
To send message from your WPF UI to your hub you can DllImport the SendMessage function.
Here is the signature to use from the C# side : http://www.pinvoke.net/default.aspx/user32/SendMessage.html
And to read messages sent by the legacy native part you can check this : http://stackoverflow.com/questions/624367/how-to-handle-wndproc-messages-in-wpf
I’m almost there. Only annoying problem with WPF binding.
My Win32 app opens a Win32 window which opens a WPF window.
Moving the WPF window around sends it coordinates to the application and the topmost window.
When I send a message to the Win32 window it processes it and calls (using your method) a method at the C# code-behind of the WPF window.
So far so good.
Here’s the problem: Since this method is not a WPF event handler it cannot effect the WPF user interface.
Here’s the entire code-behind file (My method is Set_YYY()), “EventModel” is public class EventModel : INotifyPropertyChanged
Could you give me a hint? Thank you!
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
namespace DatabindingExample
{
public partial class MainPage : Window
{
private EventModel _event;
public MainPage()
{
InitializeComponent();
// create a model object
_event = new EventModel()
{
Date = new DateTime(2011, 7, 1),
Title = “Silverlight User Group”
};
// bind the Date to the UI
this.DataContext = _event;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_event.Title = _event.Title.ToLower();
_event.Date = _event.Date.AddDays(1);
}
public void Set_YYY()
{
_event.Title = _event.Title.ToUpper();
}
}
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
If you need to dispatch the call to the UI thread you can use its dispatcher, something like:
This did not work but hey, thank you for the general direction.
Amazing how they made WPF complicated to anyone using it differently from the orthodox way.
What’s the error you get?
WPF is a framework so you have to stick with its design, otherwise yes things can quickly become complicated. 🙂
WPF is not the best tool for everything UI related, but 90% of the time it’s better than the alternatives like Winforms.
It does not compile. It says: error CS1660: Cannot convert lambda expression to type ‘System.Delegate’ because it is not a delegate type
Actually the alternative I was thinking of was Direct2D. It is pure Win32 replacement for DGI/GDI+ , it has fantastic performance but it does not support ‘controls’ natively.
You can write your own controls (I did create buttons) though.
I chose WPF because it enables you to design non-standard GUI using XAML – I hope I over come this last (hurdle) and continue with the actual development.
Ah OK, you’re not using the latest version.
This should do the trick:
The reason is that without more information the C# compiler does not know to which delegate type it should map the lambda.
In the latest version of .Net there is an overload of
Invoke
that takes anAction
so you don’t need to cast yourself, the compiler will use the correct overload.Believe me WPF is the way to go, “there is no alternative” 😉
Dear pragmateek,
The last solution compile but did not affect the UI. I stepped into it with the debugger and it did seem to do everything it should only that the textbox was not affected.
HOWEVER, I did solve the problem in a different way that has one great advantaged: It works.
You might be interested to add it to this fantastic page.
I followed this “Walkthrough” by MS: http://msdn.microsoft.com/en-us/library/ms744829(v=vs.100).aspx#communicating_with_the_page
Search for this interesting paragraph:
The simplest solution to this issue is to implement a managed class that contains a set of static fields to hold references to any managed objects that you need access to. The sample uses the WPFPageHost class to hold a reference to the WPF content, plus the initial values of a number of its properties that might be changed later by the user. This is defined in the header.
And it is simple. You create a STATIC class in the wrapper and save there a reference to your page and controls.
You can later use them as simple pointers and write data directly to the controls!
Hope you like it and THANK YOU for your help!
Glad you make it work! 🙂
Not sure it’s a very orthodox solution but I guess it’s a pragmatic choice and you can’t be blamed for that. 😉
Hi,
thank you for all the information in the post.
I have implemented your example to use it with VS2010 and it is working just fine.
But I have a question concerning deconstructors in this example and it would be great if you can help me with that. I am wondering why the deconstructor is never called. Also, I do get a “Detected memory leaks!” information in the output when using “_CrtDumpMemoryLeaks();” with the YahooTest.cpp which referes to _private. For example:
yahoo {_private=0x009f5320 } YahooAPIWrapper
_private 0x009f5320 YahooAPIWrapperPrivate *
Detected memory leaks!
Dumping objects ->
{144} normal block at 0x009F5320, 4 bytes long.
This happens even with a very short test program:
#include “stdafx.h”
#include “YahooAPIWrapper.h”
#define _CRTDBG_MAP_ALLOC
#include
#include
int main()
{
YahooAPIWrapper yahoo;
_CrtDumpMemoryLeaks();
}
I solved the memory leakage problem by declaring the destructor in the YahooAPIWrapper.h as follows:
public: ~YahooAPIWrapper();
With the CRT Debug Library it depends where you put the _CrtDumpMemoryLeaks(). I verified it now by using the Visual Leak Detector (http://vld.codeplex.com/) which shows me the complete callstack if memory allocation has led to a leak.
With declaring the constructor in the wrapper header file I get: “No memory leaks detected.”
So thank you again for your excellent overview of how to write a wrapper. 🙂
Hi Martin,
good catch: the header is not up to date!
The version I have on my computer correctly includes the destructor declaration. :/
I’ll fix that.
Moreover note that any “destructor” (aka finalizer) you define in the C# part should be called too:
Thanks for the catch. 🙂
Fixed! 🙂
I’ve fixed it in the archived code too.
Thanks for the post. This helped me out a lot on a project I am working on.
Thanks Steve for your comment.
Glad this article was useful to you.
Could you please share more (non confidential) information about your use case?
Thanks. 🙂
The “.lib” file generated by Visual Studio for C++/CLI wrapper cannot be linked statically in XE4’s C++ project (perhaps different compilers). It gives some kind of COFF linking errors.
If we need to load this Wrapper DLL dynamically in C++ code in XE4 then how can we expose the class and its member functions including the functions that create and destroy the object for this Class exposed via wrapper C++/CLI dll?
Can you provide a sample interface header for such a scenario when we need to expose and use this wrapper for C++ code in XE4.
Thanks,
Santosh
Hum, first it surprises me that they don’t support this scenario.
You should really contact their technical staff to be sure.
And if it really is not possible then you can use a C interface which should create no issue as there is no specific mangling rules.
You can have a look at this article: http://pragmateek.com/if-your-plumbing-doesnt-work-youre-just-not-using-enough-pipes/
Note that you don’t need the C++/CLI wrapper anymore if you use a C interface.
Don’t put the C interface above the C++/CLI interface, what I’ve demonstrated in the “If your plumbing doesn’t work you’re just not using enough pipes”, because in your case you just don’t need it.
Basically you will create a new mangling rule but one that you control to be independent of the compilers.
If you have no overloads it will be quite simple; if you have some overloads then you’ll have to decide how you distinguish them: by appending an index (DoSomething_1, DoSomething_2, …), by appending the parameters’ types (DoSomethingWith_int, DoSomethingWith_double, …) or in any other readable manner.
Hi Pragmateek,
My scenario is almost similar to above Post,except following
My App (XE4 C++ code) -Loads- C++/CLI Wrapper code (Visual Studio 2012) talks to .NET C# DLL (Visual Studio 2012)
Approach mentioned by you looks simple and easy to follow
but I’m struggling to expose the Class or its objects to my Native XE4 C++ App.
I’m trying to Load my C++/CLI DLL in my XE4 C++ code using LoadLibrary() and then I needed access to Class and its public
functions which I believe should be made available as we have declared it as
“class __declspec(dllexport) WrapperClass”
but how do I get to the object of this class in my Native XE4 C++ code.
I saw you mentioned something like follows which I declared in my Wrapper DLL somewhat like following:
—————————————————————–
__declspec(dllexport) Wrapper *CreateObject()
{
return new Wrapper();
}
__declspec(dllexport) void DestroyObject(Wrapper *instance)
{
delete instance;
}
—————————————————————–
I’m particularly struggling with exposing of C++/CLI DLL Class, its methods and its object to my XE4 C++ DLL to be used in XE4 code. Can you please help with some sample code?
Would appreciate your kind help.
Thanks
Hi Santosh,
I don’t know XE4 but the C++/CLI library can be used just like you
would with a pure native C++ library.
And this is a two steps process:
– at compile time you write your code basing it on the C++ library
headers, you trust them to match existing members of the library
– at link time the linker will try to find the symbols you’ve used in
the library.
To know which symbols are exported you can run “dumpbin /exports
CppCliWrapper.dll” and you should see mangled class’ members names.
There is nothing specific to C++/CLI: the CppCliWrapper.dll library
has the same interface than any native C++ library.
The only possible issue I imagine is different mangling rules between
the CL compiler and the XE4 C++ code.
Indeed each compiler has often its own mangling rules.
So your issue is how to use ANY native C++ library from XE4 code, and
to this I have no answer but I’m sure the community forums or users
support of the vendor have the answer. 🙂
But I’m quite optimistic as chances are good they have anticipated
this kind of common needs and allowed interoperability with CL
generated C++ code.
Hope this helps, and if you have more specific questions do not hesitate to ask.
Please tell me when your issue is fixed.
Thanks.
Hi Pragmateek,
Thanks for your reply. I could move forward by Fixing all the bits and pieces where I was initially struggling with my current scenario:
My App (XE4 C++ code) -Loads- C++/CLI Wrapper code (Visual Studio 2012) talks to .NET C# DLL (Visual Studio 2012)
One specific thing now that I need some help with is how to Pass a parameter by Reference across Native C++ Code to C++/CLI Wrapper code (Visual Studio 2012).
Following is an example of one of my Exposed function via Warpper DLL.
DLLEXPORT int ProcessSingleRequest(void* wrapper, const char *request, char *response)
{
return reinterpret_cast(wrapper)->ProcessSingleRequest(request, response);
}
In my Wrapper class implementation of ProcessSingleRequest() I could manage to get “response” changed to the desired value that I want to return but when it returns from above exported “C” interface its reset to NULL or undefined value. What am I missing here?
My code is based on your article shared at “http://pragmateek.com/if-your-plumbing-doesnt-work-youre-just-not-using-enough-pipes/”
Hope to hear from you soon…
Hi Santosh,
could you provide a minimal sample that shows the issue?
But I think you should use a char** instead of a char*, because you’ll have to update the value of the pointer.
Thanks
I’m very new to C++/CLI and I use Visual Studio (2008) rather occasionally.
But I have a job to do, so I thought your article was a godsend.
However:
If I use Visual Studio instead of command-line compiling, the wizard automatically provides namespaces when it creates a project. I’m rather surprised to see there are none in your example. I hope adding some is not going to be a problem.
I must have a completely wrong idea about what C++/CLI is about but
I’m hugely surprised to see the class keyword appear in the C++/CLI wrapper CPP file. I thought that was left to the header file in C++!
In any case I’m getting a compiler error when I try to compile a project named YahooAPIWrapper (which is defined as a library).
\YahooAPIWrapper.cpp(17) : error C2011: ‘YahooAPIWrapper’ : ‘class’ type redefinition
Hi Henri,
– Namespaces are only a tool to organize types so using one should not change anything.
I’ve ommited namespaces to keep things as simple as possible.
– Concerning the class, AFAIK in native C++ too you can both declare and define a class in the same CPP file.
Nevertheless it’s indeed cleaner to declare the class in the header file and define its members in the CPP file.
As for your error it’s probably due to a mix between this two approaches, so sticking with the best-practice you mention, i.e. declaring in the header, and including it wherever you use YahooAPIWrapper, and defining in the CPP should fix the issue.
Moreover you may need to guard against recursive inclusion of the header with a “#pragma once”.
Hope this helps. 🙂
I’ve fixed this in the article and in the code archive.
Your article is the clearest I’ve seen on this subject, so thanks for that! I’ve compiled your source as three projects in one solution in VS 2010. It all compiled without error. When I debug, it blows up with:
Unhandled exception at 0x767ec41f in yahoo_look.exe: 0xE0434352: 0xe0434352.
on the line:
YahooAPIWrapper yahoo;
That’s in the main console .EXE. Clearly, something is compiled incorrectly, but I don’t know what. Where can I begin looking? Thanks for your help!
Hi Donald,
this is strange, this line will simply allocate the wrapper and call its constructor which does not do crazy things.
So try to debug the YahooAPIWrapper constructor to identify which instruction is responsible for this error.
Thanks.
I can always email anyone the project(s) if they want to take a look.
again many thanks
I added a reference to YahooAPI from YahooAPIWrapper. The compiler says the reference is already there, but now it works! Great!!! Thanks for a very useful project.
Is there any easy way to debug into the C# routine from unmanaged, or do you just use message boxes or file probes?
Glad it works. 🙂
Visual Studio should be smart enough to allow debugging both managed and unmanaged code.
I don’t remember if there was a special configuration or if you needed to attach the debugger afterward or use another trick.
This documentation may help you: http://msdn.microsoft.com/en-us/library/kbaht4dh.aspx (the setting makes sense and this is simpler than in my memory :)).
Donald,
When you say “I added a reference to YahooAPI from YahooAPIWrapper.” what exactly did you do. I’ve built my own 3 components, all compile and execute but like you when I execute
YuniqueAPIWrapper yunique;
I crash never getting to any break point, if I trace with assembly it gets buried to deep for me to follow. I should add that my test app is an MFC Dialog type app as that is what the final app will be.
many thanks
I just added a console test app and it does the same thing. For configurations my test apps and the wrapper are defined as WIN32 and the C# is x86. I added a reference in the C# app to the wrapper DLL which makes no sense to me and it had no effect.
Any ideas?? anyone??
Seems like the CLR can’t load, so probably an issue with bitness.
I fear Win32 is misleading and can also generate x64 code, which is probably the case if you are on a x64 platform.
To check this you can use the dumpbin tool:
And look for “machine (x86)” or “machine (x64)”.
OK, I got it, the C# dll was in the wrong folder so the system could not find it. These multi project solutions are sometime a PITA to setup.
Thanks again for your code sample it’s been a great help.
Interop is always tricky be it direct or through COM.
Glad you’ve fixed it.
Hi Donald,
I am getting the same exception at the same line. The app is compiled as win32 console application. Can you please tell me where have you added the reference, what change did you made exactly?
Hi Shikha,
have you checked that the DLL is in the correct folder when running?
Tony had the same exception because of this issue.
Hello,
Its working now, after adding managed lib as reference to the wrapper.
Thanks
Indeed adding it as a reference automatically copy it in the bin folder.
Glad it works now.
The sample is working fine when the app is compiled as a console app. When I compile the app as a .DLL and call it from another routine, it blows up on this line, with an Invalid Access error deep in Windows:
#include
#include “YahooAPIWrapper.h”
int main()
{
const char* stock = “GOOG”;
YahooAPIWrapper yahoo; // <— blows up here.
double bid = yahoo.GetBid(stock);
double ask = yahoo.GetAsk(stock);
There's no material difference in the code, and the YahooAPIWrapper.cpp and YahooAPI.cs are not changed at all. All code compiled with no errors, and the .DLL loads and runs up to the line that blows up. When I put it back to console, it again works fine. It's too simple to be anything very serious, but I sure can't figure this out. Thanks for your help!
Hum, strange indeed.
Is the YahooAPIWrapper constructor called?
If yes can you identify which line exactly is crashing the app inside it?
And what happens if you instead allocate the wrapper on the heap:
YahooAPIWrapper* yahoo = new YahooAPIWrapper();
?I don’t know if the constructor is called. I tried to set breakpoints in that part of the code, but couldn’t break there. Yes, I tried allocating on the heap, but the result was the same.
If you can’t break you can always use some good old print statements to be sure. 🙂
Could you please send me a minimal project that illustrates the issue, I’ll check it.
Thanks.
Well, it’s working now. I created two projects, one console, one dll. They both worked! I copied the .dlls to my actual app, and the blowup was the same as before. In my app, the .exe calls through 3 levels of .dlls to get to your test code. When I compiled ONLY the .exe in release mode, it worked (no blowup). I haven’t pinpointed the difference between debug and release for the .exe, but it works now! Don’t know how that could possibly relate to .dlls way downstream… Progress, maybe. Thanks for your help!
Seems like a memory issue indeed, but why does it appear when creating the class…
I really don’t like this kind of situation where you lose control, and things are kind of magic.
At least it forces us to be humble because we know we can’t control and understand everything. 🙂
I only hope the issue is not in my implementation though it’s pretty basic…
First off, thank you for writing this. Excellent stuff!
I have the need to call some vb.net dlls from an unmanaged C++ app. Though I see the *fun* in writing the wrapper class by hand, I do have to deal with deadlines 🙂 You mentioned that swig automates this plumbing step. Can you eloborate on this? As far as I can tell, swig only works with C/C++ header files. How do you get it to plumb managed vb or c# code?
Hi and thanks for your comment.
Concerning SWIG you’re right it is not a good fit for your use-case.
Do you really need to export all the types of the VB.Net library?
You should enumerate the “services” you will need from the library and only expose entry-points for them, which should considerably reduce the development effort.
Note that you could develop these high-level services with C# which interoperates seamlessly with VB.Net.
C# is a better long-term choice than VB.Net and easier to understand if you are a C++ developer.
If you really need to use the entire object model then your best bet is probably COM interop which is supported by Visual C++.
Could you tell us more about the role of this VB.Net library, and which functions you’d like to leverage from the native C++ side?
Because depending on them there is plenty of more specific solutions.
Thanks.
Hey, thanks for the quick reply!
Short history: We have 2 vendor apps. One is .Net based and the support/development team chose to use vb.net at the time (~2006, they were mostly vb6 guys anyway). That has been the defacto language since (I’ll leave the politics out as to why they won’t move to C#). The other app is C++ based with far fewer developers. I would like to take advantage of all the helper classes (oracle wrappers, application interface wrappers, custom sql queueing, etc) that have been developed using vb.net instead of reimplementing them in C++. Exposing them via COM is the normal mode, but we have several processes that require a lot interactive interactions (sql queries, etc) and the COM interop layer slows that process down a bit. I’d like to compare the performance between COM and C++/CLI.
Ah, I just re-read your section regarding swig. You were talking about managed to unmanaged plumbing, doh!
While I’m tinkering with creating this wrapper class, how do I handle dll’s with multiple classes defined? For example, I have a project called MyAccess and it’s build time artifact is called MyAccess.dll. Within that project, there are 3 classes: MyConnection, MyReader and MyAttribute. I’ve tried to follow your example, but I’m getting compile errors.
#using "MyAccess.dll"
#include
using namespace System::Runtime::InteropServices; // Marshal
class MyAccessWrapperPrivate
{
public: msclr::auto_gcroot myConnection;
};
class __declspec(dllexport) MyAccessWrapper
{
private: MyAccessWrapperPrivate* _private;
public: MyAccessWrapper()
{
_private = new MyAccessWrapperPrivate();
_private->myConnection = gcnew MyConnection();
}
}
Errors:
C:\devel\>cl /clr /LD MyConnectionWrapper.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 16.00.40219.01
for Microsoft (R) .NET Framework version 4.00.30319.1001
Copyright (C) Microsoft Corporation. All rights reserved.
MyConnectionWrapper.cpp
MyConnectionWrapper.cpp(12) : error C2065: 'MyConnection' : undeclared identifier
MyConnectionWrapper.cpp(12) : error C2059: syntax error : '>'
MyConnectionWrapper.cpp(13) : error C2143: syntax error : missing ';' before '}'
MyConnectionWrapper.cpp(16) : error C2143: syntax error : missing ';' before '{'
MyConnectionWrapper.cpp(20) : error C2143: syntax error : missing ';' before '{'
MyConnectionWrapper.cpp(23) : error C2143: syntax error : missing ';' before '}'
MyConnectionWrapper.cpp(24) : error C2143: syntax error : missing ';' before '}'
MyConnectionWrapper.cpp(25) : error C2143: syntax error : missing ';' before '}'
MyConnectionWrapper.cpp(25) : fatal error C1004: unexpected end-of-file found
Hopefully the code tag worked.
Thanks!
Your use-case is indeed interesting.
As for the COM performance issue, if you are I/O-bound, like interacting with a database or using the FS then the overhead should be negligible.
COM is not a good fit if you have a lot of short interactions.
e.g. to take an extreme example:
As for the issue you have with the wrapper I think that classes in your assembly are in namespaces so you must import them in the current scope:
You should then be able to access the
MyConnection
class.It’s more iterative and makes *a lot* of calls.
Regarding namespaces, I don’t see any explicit namespaces declared on the vb side. If I use the following code:
#using "MyAccess.dll"
#include
using namespace System::Runtime::InteropServices; // Marshal
using namespace MyAccess;
class MyAccessWrapperPrivate
{
public: msclr::auto_gcroot myConnection;
};
class __declspec(dllexport) MyAccessWrapper
{
private: MyAccessWrapperPrivate* _private;
public: MyAccessWrapper()
{
_private = new MyAccessWrapperPrivate();
_private->myConnection = gcnew MyConnection();
}
}
I get this from the compiler:
MyConnectionWrapper.cpp
MyConnectionWrapper.cpp(20) : error C3767: 'MyAccess::MyConnection::MyConnection': candidate function(s) not accessible
If there is no namespace as I’ve did then indeed you dont need any
using namespace
.Is the
MyConnection
class public?By default classes without any qualifier are internals, i.e. they can only be accessed from the same assembly.
Thanks for this great article! It is very very helpful. I am trying to write a windbg extension(c++ dll) which needs to take advantage of some Thirdparty C# program. I learned the way you did, got a demo work. But also run into some problem. I tried to change the return type as string instead of const char*, got the crash in the windbg UI, maybe it is because the STL compatible issue as you mentioned?
“moreover you may wonder why we use “const char*” instead of “std::string“: because publicly exposing STL types is brittle as their implementation could differ from vendor to vendor and even between different versions from the same vendor”.
I also plan to return some STL containers like vector, map. Do you have any workarounds? Do we have to stick to the primitive data types? Or we can send the container ref as an argument. I am just thinking about it, haven’t tested it yet.
Hi Stanley,
thanks for your kind feedback.
And yes I can confirm that exposing STL types is definitely a dangerous practice that can cause a number of unexpected behaviors; but not sure the issue you have is one of them.
AFAIK there is no plug-and-play workaround but if you want to control your public API you can create some alternatives for the types you intend to use, that are safe to transfer across the API boundaries.
For
string
s usingchar *
is a standard practice, as well as using arrays instead ofvector
s.For the
map
you could create a class with two arrays: one with the keys, another with the values, or represent it as an array of key-value pairs.Either use a known convention if you can find one or the simplest implementation for your special case, so that on the calling side converting between them and standard C++ types is easy.
Thank you for the advice. I tried to pass by reference, and this time I found it retrieved nothing instead of crashing. So I guess it might be some memory issue. But both ways work correctly in an exe I wrote for test. I am still shooting in the dark. I guess maybe my extension is based on COM that’s why it happens. I don’t know the internal mechanism. My assumption is based on that’s the only difference I know between the extension dll and the my test exe.
If you throw COM into the equation you’re really asking for troubles. 😉
More seriously if you are well versed in COM you may try to expose what you want from the .Net/C# stuff via COM, as it is a “standard” platform with, AFAIK, well defined types.
Makes sense. Actually i gave up both. As I found the C# exe needs the app.config at runtime to do the basicbinding. It probably would take another amount of time to get around it. I switched to python. Now the feature is done. Though I am not able to make interop work. It is a good experience. Thanks again!
Thanks for your feedback Stanley.
Python is definitely a magic tool.
I’m always amazed of this kind of “success stories” : can’t do it with C++/Java/C#… done it with Python.
I hope one day I’ll have a closer look at this strange creature. 🙂
Hi pragmateek,
when I’ll compile:
#using “YahooAPI.dll”
// or #using “..\CS\bin\Debug\YahooAPI.dll”
// or #using “C:\Projekte\CPP CS Wrapper\CS\bin\Debug\YahooAPI.dll”
#include “stdafx.h”
#include
using namespace System::Runtime::InteropServices; // Marshal
class YahooAPIWrapperPrivate
{
public: msclr::auto_gcroot yahooAPI;
};
the compiler says: “error C2065: ‘YahooAPI’: undeclared identifier”.
But syntax highlightning shows that YahooAPI is known and the object catalogue shows the dll with the class YahooAPI also.
Thanx Volker
Hi Volker,
I guess the YahooAPI managed project correctly generates the YahooAPI.dll assembly…
Have you tried to put this assembly in the same folder as your C++/CLI wrapper source file?
If it’s still broken in VS then try to compile using the command line, this is the safest way to check if there is a “true” issue or if this is just some VS glitch.
Hi pragmateek,
thank you for your great web-site and helping.
I’ve found my mistake: it’s necessary to #include “stdafx.h” before #using “YahooAPI.dll”.
Incidentally #1: in your ZIP, there is no destructor of ~YahooAPIWrapper.
Incidentally #2: I’ve done a small improvement in YahooAPIWrapper.h:
#ifdef DLL
#define DLLEXP __declspec(dllexport)
#else
#define DLLEXP
#endif
class DLLEXP YahooAPIWrapper
{…}
So I can include YahooAPIWrapper.h in YahooAPIWrapper.cpp (where I set the define DLL) and in test.cpp. So I’ve only ONE definition of the class YahooAPIWrapper in YahooAPIWrapper.h and not a second definition in YahooAPIWrapper.cpp.
ciao
Volker
Hi Volker,
glad you’ve fixed your issue. 🙂
Thanks for your feedback and the catches.
The destructor issue was fixed some months ago, as you can see in the article but for some reason the ZIP was not up-to-date. :/
This is now fixed.
As for the other improvement, yes this is not best of breed C++ 😉
I’ll fix it when I have more time to thoroughly test it.
All the best. 🙂
Works perfect from C++. Can I use this wrapper from Delphi? If so, then how? Just calling external library gives an error “entry point not found”. Dllexportviewer shows that export functions have quite strange names.
Thanks!
Hi, glad it works for you.
The strange names are the result of mangling which encodes the parameters metadata like types.
For use from Delphi I think a direct binary communication might be complex, so you’d better try with COM interop if this is an object oriented API.
If you have simple entry points (a la C, i.e. not OO) then you could try the DllExport extension: http://stackoverflow.com/a/6175291/145757.
Hi Pragmateek,
already have tried to use DllExport extension, but it produces many of floating point operation exceptions (possible solution is just turn off floating point control registry). Perhaps technique described here http://pragmateek.com/if-your-plumbing-doesnt-work-youre-just-not-using-enough-pipes/ can be helpful?
Thank you for feedback!
The article you point to was more of a proof of concept, you should avoid using so many layers, this would be a nightmare to maintain.
I think COM is your best bet because on the .Net side the COM interop layer is well implemented and using .Net components through COM, e.g. from VBA (check my other articles), is (almost) a breeze.
To be sure this is the best choice you should ask Delphi experts but I’d be surprised if there was other serious alternatives…
Can you please give an example of how to passing arrays as argument?
I have an VC++ MFC program, and I want to call a function in c# dll like:
int LoadCodes(out int numData, out long[] Bcols, out float[] Codes, out string[] Names, out string[] ShortNames). Can you give me some advice please? Thanks!
Hi Jin,
here is a full sample for your use-case:
– C# code to call:
– C++/CLI wrapper code:
- native C++ program code:
Hope this helps...
This is a wonderful article and have been trying to add it to my attempt at wrapping the TFS API. I am still struggling a bit to manage even the connection. Hopefully I can get some suggestion or tips from you please.
The idea is to use TFS API in a C++ (QT5) project.
Thanks for the feedback Vivian.
I don’t know QT but as it is native C++ there should be no specific stuff.
The C++/CLI wrapper is a pure native C++ component that should integrate seamlessly in your QT project.
Which issue(s) do you have?
New to all of this but I’m trying to setup a project via VS2013. Using the CLR Library project setup and attempting to define the header file I’m a bit perplexed on how to define the header. Would it be possible for someone to show the header file (YahooAPIWrapper.h) setup and I’ll try to go from there with the cpp.
many thanks
Here is the header I’ve used:
OK, I got that to work
#pragma once
using namespace System;
class YuniqueAPIWrapperPrivate;
class YuniqueAPIWrapper
{
private: YuniqueAPIWrapper* _private;
public: YuniqueAPIWrapper();
public: ~YuniqueAPIWrapper();
public: bool Connect();
};
but when I compile I get an error (class type redefinition) on this line:
class __declspec(dllexport) YuniqueAPIWrapper
#include “stdafx.h”
#include “YuniqueAPIWrapper.h”
#using “YuniqueAPI.dll”
#include
using namespace System::Runtime::InteropServices; // Marshal
class YuniqueAPIWrapperPrivate
{
public: msclr::auto_gcroot yuniqueAPI;
};
class __declspec(dllexport) YuniqueAPIWrapper
{
what am I doing wrong?
Many thanks
The issue is that you are defining the class both in the header and the implementation files.
So drop the
class YuniqueAPIWrapper
redefinition and instead use:Never mine, I got it all to work, I hope.
Glad it works now. 🙂
Thanks for sharing your article.
http://blogs.microsoft.co.il/sasha/2008/02/16/net-to-c-bridge/ presents similar ideas and includes a picture.
Thanks for the reference Ryan.
But I must be missing something as I don’t understand why there is an additional indirection layer…
There’s effectively the same number of indirection layers:
Your .NET class YahooAPI corresponds with the green “C# Class” box (or class Calculator in his example)
Your class YahooAPIWrapperPrivate corresponds with the red “IL Bridge” box (or class ILBridge_CppCliWrapper_Calculator in his example)
Your class YahooAPIWrapper corresponds with the red “Native class” box (or class NativeExport_CppCliWrapper_Calculator in his example).
The main difference is his ILBridge_CppCliWrapper_Calculator has the gcroot member
private, not public, and so he has only simple wrapper functions in the NativeExport class rather than directly accessing the gcroot member from that class and having the actual code there too. I think this is a general question about how best to do PIMPL (e.g. http://www.gotw.ca/gotw/024.htm)
Maybe an advantage of Sasha’s way is that you can ensure the NativeExport class is truly native only because you compile it with #pragma unmanaged?
Using CLI to go the other direction (from C++ to .NET) doesn’t require the extra layer of indirection because C# doesn’t require #including a class declaration header file in the same way as in native C++ to use a class.
It was helpful to have yours as a second example, with the discussion about auto_gcroot and design Q&As. Thanks.
Thanks for the clarification Ryan.
I got this exception running the final test.exe:
Unhandled Exception: System.FormatException: Input string was not in a correct format.
at System.Number.ParseDouble(String value, NumberStyles options, NumberFormatInfo numfmt)
at YahooInCSharp.YahooAPI.ParseDouble(String value)
at YahooInCSharp.YahooAPI.GetBid(String symbol)
at YahooAPIWrapper.GetBid(YahooAPIWrapper* , SByte* symbol)
I got the same exception when attempting to call GetBid from a C# Main
Could you please include the code that is causing the error?
I’ve updated the article and the code archive to take this Yahoo API update into account.
Sorry, I meant GREEN “IL Bridge” box!
Hi Pragmateek,
I’ve been reading your blog with great interest.
I eventually need to solve a problem of a very similar nature, but am struggling with the C++ syntax. (It’s been several years, unfortunately)
As a quick synopsis, I have a C# .dll that implements a client-server object network which itself satisfies a very stringent set of rules. basically,
every object can be described as a set of (optionally read only) properties or methods
return types are either primitives (e.g. string, boolean, int, float) or objects, or arrays of primitives or objects.
parameters are either primitives or objects or arrays of primitives or objects.
thrown exceptions are also objects from within the model.
and finally, callers can create new objects from the model, or reuse existing ones.
with this in mind, here’s a fairly small (3 classes) C# example describing the model.
Duplicate.cs
namespace ObjectNetwork
{
public class Duplicate : System.Exception
{
private String m_message;
internal Duplicate(String m)
{
m_message = m;
}
}
}
Stock.cs
namespace ObjectNetwork
{
public class Stock
{
private String m_name;
private double m_value;
public Stock(String name)
{
m_name = name;
}
public String Name
{
get
{
return m_name;
}
}
public double Price
{
get
{
return m_value;
}
set
{
m_value = value;
}
}
}
}
Basket.cs
namespace ObjectNetwork
{
public class Basket
{
private ArrayList m_allStocks = new ArrayList();
public Basket()
{
Stock a = new Stock(“A”);
a.Price = 10;
Stock b = new Stock(“B”);
b.Price = 15;
m_allStocks.Add(a);
m_allStocks.Add(b);
}
public void AddStock(Stock s)
{
Stock[] ss = AllStocks;
for (int i = 0; i < ss.Length; i++)
if (ss[i].Name.Equals(s.Name))
throw new Duplicate(s.Name + " is an existing stock in the basket");
m_allStocks.Add(s);
}
public Stock[] AllStocks
{
get
{
return (Stock[])m_allStocks.ToArray();
}
}
}
}
Can you give me an idea of what the generated C++/CLI interfaces and implementations will look like?
Appreciate the help,
Thanks
Anil
Hi Anil,
my C++ skills are rusty but here are some ideas for the native C++ side API:
–
String
->char*
(avoidstd::string
which is not safe across libraries boundaries)–
Stock
->Stock&
orStock*
:s
is a .Net reference, and equivalents in C++ are references and pointers–
Stock[]
->Stock**
: in C++ we often use contiguous set of objects as arraysOn the private implementation side:
–
ArrayList
(you’d better use the generic typeList<Stock>
) ->std::vector<Stock*>
My real problem is that the ‘wrapper’ managed and un-managed h and .cpp files i created to emulate this object network aren’t compiling.
I’m running into issue attempting to pass managed references around from one object to another.
Ah well.
Thanks
Anil
Here is a full sample with your object model.
First there is a bug in your C# code I’ve fixed:
using System.Linq;
...
public Stock[] AllStocks
{
get
{
return m_allStocks.Cast<Stock>().ToArray();
}
}
Here is the C++/CLI wrapper:
#using "ObjectNetwork.dll"
#include <msclr\auto_gcroot.h>
#include "Wrapper.h"
using namespace System::Runtime::InteropServices; // Marshal
using namespace ObjectNetwork;
class StockWrapperPrivate
{
public: msclr::auto_gcroot<Stock^> stock;
};
StockWrapper::StockWrapper(const char* name)
{
_private = new StockWrapperPrivate();
_private->stock = gcnew Stock(gcnew System::String(name));
}
const char* StockWrapper::GetName()
{
return (const char*)Marshal::StringToHGlobalAnsi(_private->stock->Name).ToPointer();
}
double StockWrapper::GetPrice()
{
return _private->stock->Price;
}
void StockWrapper::SetPrice(double price)
{
_private->stock->Price = price;
}
StockWrapper::~StockWrapper()
{
delete _private;
}
class BasketWrapperPrivate
{
public: msclr::auto_gcroot<Basket^> basket;
};
BasketWrapper::BasketWrapper()
{
_private = new BasketWrapperPrivate();
_private->basket = gcnew Basket();
}
void BasketWrapper::AddStock(StockWrapper& s)
{
Stock^ stock = gcnew Stock(gcnew System::String(s.GetName()));
stock->Price = s.GetPrice();
_private->basket->AddStock(stock);
}
StockWrapper** BasketWrapper::GetAllStocks()^ stocks = _private->basket->AllStocks;
{
cli::array
StockWrapper** unmanaged = new StockWrapper*[stocks->Length];
for (int i = 0; i < stocks->Length; ++i)
{
StockWrapper* wrapper = new StockWrapper((const char*)Marshal::StringToHGlobalAnsi(stocks[i]->Name).ToPointer());
wrapper->SetPrice(stocks[i]->Price);
unmanaged[i] = wrapper;
}
return unmanaged;
}
BasketWrapper::~BasketWrapper()
{
delete _private;
}
And the header file:
class StockWrapperPrivate;
class BasketWrapperPrivate;
class __declspec(dllexport) StockWrapper
{
private: StockWrapperPrivate* _private;
public: StockWrapper(const char*);
public: ~StockWrapper();
public: const char* GetName();
public: double GetPrice();
public: void SetPrice(double);
};
class __declspec(dllexport) BasketWrapper
{
private: BasketWrapperPrivate* _private;
public: BasketWrapper();
public: ~BasketWrapper();
public: const char* GetName();
public: void AddStock(StockWrapper&);
public: StockWrapper** GetAllStocks();
};
And a native C++ program:
#include <iostream>
#include "Wrapper.h"
int main()
{
const char* name = "GOOG";
StockWrapper Google(name);
std::cout << "Name: " << Google.GetName() << std::endl;
BasketWrapper basket;
basket.AddStock(Google);
StockWrapper** stocks = basket.GetAllStocks();
std::cout << "First stock name: " << stocks[0]->GetName() << std::endl;
std::cout << "Second stock name: " << stocks[1]->GetName() << std::endl;
std::cout << "Third stock name: " << stocks[2]->GetName() << std::endl;
}
Result:
Name: GOOG
First stock name: A
Second stock name: B
Third stock name: GOOG
Hope this helps.
Thanks this has been very helpful. I do have a question as far as memory management goes. In the method GetValues who or what is responsible for free’ing the memory allocated by unmanagedValues? If the CLR’s are different from the dll and the calling exe does that pose an issue? In my case the exe is MFC C++ 6 while the dll is in C#/.NET 4/CLR 10.
Many Thanks!
Thanks for the feedback Tim.
AFAIK in a mixed context there is two heaps:
– the unmanaged one used by
new
in native C++,– the managed one used by
gcnew
in managed C++ and bynew
in C#.But there is a single process and (most often) a single CLR, so there is no issue of this kind.
The only issue you can have is differing bitnesses: e.g. 64 for the native process and 32 for the managed DLL.
If you need to manage memory you should expose an additional
FreeValues
method that would take care of freeing the memory it allocated on the native heap.Pingback: Call C# Functionality from a C++ Native app without CLR [closed] - DexPage
Hi Pragmateek,
many thanks for sharing this post with us.
It helps me lot on a project where I have to include a communication library written in C# by my colleagues to my native C++ application.
Regards
Jörg
Hi Jörg,
I’m glad this article has been helpful to you. 🙂
Regards.
I am totally new in this kind of topic :
This is my header file ::
#pragma once
using namespace System;
class YahooAPIWrapperPrivate;
class __declspec(dllexport) YahooAPIWrapper
{
private: YahooAPIWrapperPrivate* _private;
public: YahooAPIWrapper();
public: ~YahooAPIWrapper();
public: double GetBid(const char* symbol);
public: double GetAsk(const char* symbol);
};
And this is YahooAPIWrapper.cpp::
#include “Stdafx.h”
#using “E:\\YahooAPI.dll”
#include “YahooAPIWrapper.h”
#include
using namespace System::Runtime::InteropServices; // Marshal
class YahooAPIWrapperPrivate
{
//public: msclr::auto_gcroot yahooAPI;
//public: msclr::auto_gcroot YahooAPI;
public: msclr::auto_gcroot yahooAPI;
};
//class __declspec(dllexport) YahooAPIWrapper
//{
//
YahooAPIWrapper::YahooAPIWrapper()
{
_private = new YahooAPIWrapperPrivate();
_private->yahooAPI = gcnew YahooAPI();
}
double YahooAPIWrapper::GetBid(const char* symbol)
{
return _private->yahooAPI->GetBid(gcnew System::String(symbol));
}
double YahooAPIWrapper::GetAsk(const char* symbol)
{
return _private->yahooAPI->GetAsk(gcnew System::String(symbol));
}
YahooAPIWrapper:: ~YahooAPIWrapper()
{
delete _private;
}
Now I got errors :::
1. GetAsk is not a member of ‘msclr::auto_gcroot’
2. GetBid is not a member of ‘msclr::auto_gcroot’
3. syntax error: ‘>’
4. syntax error: identifier ‘YahooAPI’
5. ‘YahooAPIWrapperPrivate’ : no appropriate default constructor available
6. binary ‘->’ : no operator found which takes a left-hand operand of type ‘msclr::auto_gcroot’ (or there is no acceptable conversion)
7. ‘YahooAPI’ : illegal use of namespace identifier in expression
8. ‘msclr::auto_gcroot’ : too few template arguments
9. IntelliSense: namespace “YahooAPI” is not a type name
Can you tell me what I am doing wrong here? And can you suggest me some articles which help to improve my concepts like this?
Thanks, this is very useful cause After two days I found this. Really this is good article.
auto_gcroot
should be typed with the .Net/C# type:public: msclr::auto_gcroot<YahooAPI^> yahooAPI
.Fix that one and it should work far better. 🙂
Hi. Firstly let me thank you for the very informative article, but also for taking the time to respond to each and every comment – much appreciated!
I’m attempting to re-use your examples to wrap up my own C# DLL using VS2015, but even though I currently only have the bare framework of the C++ wrapper I am having compile problems due to the C++ reporting that my C# class is undefined.
– It seems I can’t add a C# project reference to the C++ project
– I have tried the full path in the #using statement for the DLL
– I have added the C# dll output folder to as an additional include directory and and additional #using directory.
It’s as though the class is not exposed in the C# based DLL, yet the only major difference to your own example I have noted is that my C# class is defined within a namespace (reading previous comments you have indicated this should not matter).
I am about to re-use your example code as a VS2015 solution (rather than individual files to be compiled at the command line), but I have already noted a runtime problem with your test application when build on my machine…..
Unhandled Exception: System.FormatException: Input string was not in a correct f
ormat.
at System.Number.ParseDouble(String value, NumberStyles options, NumberFormatInfo numfmt)
at System.Double.Parse(String s, IFormatProvider provider)
at YahooAPI.ParseDouble(String value)
at YahooAPI.GetBid(String symbol)
at YahooAPIWrapper.GetBid(YahooAPIWrapper* , SByte* symbol)
My build output is the same as yours (though compiler version 19 identified rather than 10), with the exception one additional warning.
E:\Devtools\Microsoft VS2015\VC\INCLUDE\exception(358): warning C4577: ‘noexcept’ used with no exception handling mode specified; termination on exception is not guaranteed. Specify /EHsc
Even applying the suggest /EHsc switch it still gives the same runtime exception.
Do you know why you example is not working when built using the VS2015 command line compiler?
Do you have any insights into why my C# class may not be visible to the C++ code?
Paul Parkins
It seems I can add a reference to the C# project after all. VS was not allowing me as my C# was targetted for .Net 4.5.2 and the C++ defaulted to 3.5.
Using the information in the link below I determine you can only change your C++/CLR libraries targetted version by manually editing the .vcxproj file. http://stackoverflow.com/questions/2921702/change-c-cli-project-to-another-framework-than-4-0-with-vs2010
So now I have managed to add a reference – though my C# is still reported as undefined 🙁 Still at least I got past one blocker, lol
I had to add an entry to my C++ such as this in order to be able to use my C# class
using namespace mynamespace;
Hi Paul,
so you’ve managed to compile using VS 2015?
As for your runtime error I will try a guess: maybe the data returned by Yahoo are wrong: depending on the time bid/ask can be empty I imagine.
Debugging should tell you more…
Indeed the Yahoo API seems to have changed the codes used to identify the fields.
I’ve updated the article and code archive accordingly.
I got the same FormatException. It was caused by the values returned by the Yahoo API (mainly ‘N/A’). Maybe the API has changed.
I did the following changes to the YahooAPI-class based on information from this page: http://www.jarloo.com/yahoo_finance/
YahooAPI.cs
Changed UrlTemplate to this:
private const string UrlTemplate = “http://download.finance.yahoo.com/d/quotes.csv?s={0}&f={1}”;
Changed the field identifiers used by GetBid() and GetAsk():
public double GetBid(string symbol)
{
return ParseDouble(GetDataFromYahoo(symbol, “b”)[0]);
}
public double GetAsk(string symbol)
{
return ParseDouble(GetDataFromYahoo(symbol, “a”)[0]);
}
In the file Test.cpp I changed the call to yahoo.GetValues() to this:
const char** bidAskCapi = yahoo.GetValues(stock, “abj1”);
In production code you would of course use double.TryParse instead of double.Parse for better error handling.
The C4530 is not related to the FormatException, I just ignored it, no problem.
I use VS2012 Express and .NET 4.0 compilers.
Thanks Nils for sharing these fixes.
Hopefully this will avoid other developers some headaches. 🙂
Fixed in the new article and code archive.
I think, you are aware about “send to” -> “mail recipient”. If not look at this image :: http://www.guidingtech.com/assets/postimages/2012/10/send-to-mail.png
I am calling dll file when user clicks on “send to” -> “mail recipient”. I sat dll path in registry and file is called perfectly. But the issue is, I want to make dll using c# code, and in c# I cannot specify entry point like c++ code – BOOL APIENTRY DllMain. So how do I call function of dll which created in C#.
Is there any way in c# to specify an entry point?
So I found that I can not specify an entry point using C# code, so I came with another solution. And the solution is, using c++ dll entry point, I will call function of c# dll. Do you have some suggestion about this?
Sorry to respond so late but I have not received any notification for your comment. :/
You can indeed use the technique shown in this article: develop your extension in C#, wrap it in a C++/CLI DLL.
I think you don’t need a native C++ DLL as the C++/CLI one should be able to expose a DllMain entry-point.
G;Day Paul,
Just came across your article and it was a great help in interfacing some crusty old c application to some useful C# functionality contained in various c# dll’s etc.. Thank you for publishing it. =)
However, in your example I am concerned with the two methods;
1. const char* GetCapitalization(const char* symbol)
2. const char** GetValues(const char* symbol, const char* fields)
They both use Marshal::StringToHGlobalAnsi(…) to allocate unmanaged memory on the heap with the contents of the managed string. If you read the documentation on it, it is pretty clear that this heaped allocation should be explicitly freed. when your done with it i.e.
“StringToHGlobalAnsi is useful for custom marshaling or when mixing managed and unmanaged code. Because this method allocates the unmanaged memory required for a string, always free the memory by calling FreeHGlobal.”
Due to the fact that my application is passing around quite large string buffers from managed to unmanaged, these could be a source of potential large memory leaks
Do you have any ideas on how to get around this? I attempted to save this heaped memory in a private member of of the wrapper class (not private) and then call FreeHGlobal when the dtor is called but that crashed the app.
In my case, the data is not changed so I toyed with the idea of using a pin_ptr to a pass the internal ptr back but this is no good as the pin_ptr goes out of scope when you leave the managed methods scope.
Anyway, as you can tell I am a little out of my depth on this, any suggestions on this would be much appreciated.
Cheers.
Hi,
glad this article has been useful to you. 🙂
You’re right, memory allocated by
StringToHGlobalAnsi
should be freed.I’ve overlooked it here as this is not an issue with such a small PoC.
As for the correct way to handle it I would have proceeded exactly like you did.
AFAIK the memory is allocated on the native heap so you should not be concerned with GC mechanics and pinned memory.
I’d be curious to see some code that crashes when
FreeHGlobal
is called.Regards.
G;Day Pragmateek,
Just a quick followup, I was able to finally get it to work by using my initial idea of persisting the IntPtr returned by StringToHGlobalASCII in a private member of my wrapper and freeing it when it is deconstructed. Not sure what was the problem the first time I tired it out.
Anyway. thanks again.
Nice if it works now.
Too bad we haven’t had the opportunity to understand what was wrong. 🙂
Regards.
Helllo pragmateek,
thank you a lot for your article since it was very helpful for me.
I have a question regarding passing a value by reference.
For example if i have a c# function like:
public bool DoSomething(int input, out double output);
Could you explain to me how my wrapper function should look like so that i can pass a reference or whatever it needs to be so that the function can set the variable ‘output’?
Best regards
Daniel
I guess the easiest way is:
public bool DoSomething(int input, double &output);
If yes, sorry for asking…
Hi Daniel,
thanks for your feedback.
I have a sample for such a use-case above with the
LoadCodes
method.I’ve used a pointer but as in C++ references are pointers in disguise you probably can use a reference too.
Let me know if it works as expected.
Thank you for you quick response.
Yeah both ways are possible. I just made the mistake that, when i worked with pointers i wanted to pass the pointer to the c# function…
Hey there, thanks so much for the article. First one that’s ever gotten me close to using my VB.net DLL in native C++. However…. I’m getting an error. My vb.net DLL is just an adding function ( so no need to pay attention to it… but VB.net should work as well since C# and VB work on .NET)
Here’s my Header (trying.h)
class AddWrapperPrivate;
class __declspec(dllexport) AddingWrapper
{
private: AddWrapperPrivate* _private;
public: AddingWrapper();
public: ~AddingWrapper();
public: int Adding(double* x, double* y);
};
here’s my CLR .cpp
using namespace System::Runtime::InteropServices;
class AddWrapperPrivate
{
public: msclr::auto_gcroot add;
};
class __declspec(dllexport) AddingWrapper {
private: AddWrapperPrivate* _private;
public: AddingWrapper()
{
_private = new AddWrapperPrivate();
_private->add = gcnew Add();
};
public: int Adding(double* x, double* y) {
return _private->add->Adding(*x, *y);
};
public: ~AddingWrapper()
{
delete _private;
};
};
And here’s my console app:
#include “stdafx.h”
#include
#include
#include “C:\Users\mleclai3\Documents\Visual Studio 2015\Projects\Trying\Trying\Trying.h”
int main()
{
double* x =0 ;
double* i =0;
AddingWrapper add;
std::cout <> *x;
std::cout <> *i;
int result =add.Adding(x,i);
std::cout << "result is:" <> *x;
return 0;
}
3 things happen:
If I don’t compile my console app in Release x86 I get this error : unresolved external symbol “public: __thiscall AddingWrapper::AddingWrapper(void)” (??0AddingWrapper@@QAE@XZ) FOR ALL Trying.cpp functions
If I compile it in Release x86 Then I’ll get an error that says my *x variable points to a null object (???)
Finally, If i write my Trying.h the way you did your header I get an error c2011 ‘AddingWrapper’ : ‘class’ type redefinition
You think you could give me some help figuring it out?
After compiling it with the library… It just throws me another error ( god help me please) :
An unhandled exception of type ‘System.AccessViolationException’ occurred in AddingWrapper.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
More precisely:
Unhandled Exception: System.AccessViolationException : attempted to read or write protected memory. This is often an indication that other memory is corrupt. at gcroot (Add ^).. P$AAVAdd@@(gcroot(Add^)) at AddingWrapper.Adding(AddingWrapper, double* x, double* y)
Did your code ever do that???
Hi Marc,
let’s tackle your issues one by one:
1) The build chain bitness must be consistent: if any of the dependencies is in x86 (32-bit) your calling code must be in x86 too.
By default you’re probably using the x86 build chain with CL x86.
The linker will not consider dependencies with a different bitness as itself, hence won’t take symbols contained in them into account, hence your error.
2) In the
main
x
andi
are pointers (variables containing memory addresses) so you must initialize them with valid addresses (owned by the process).But you don’t need them and you should do something like that instead:
3) Indeed in the
.cpp
you should not define class members as I did (not a best practice I confess).Instead you should have no
class AddingWrapper { ... }
declaration but only members definitions:4) The runtime error is the direct consequence of point 2):
when you write
double* x = 0
you say "x is pointing to the first memory address" which is an invalid address for your process;so when you do
*x
you're trying to get the value stored at this invalid address and the OS logically answers you can't do that with theAccessViolationException
(segmentation fault).Hope this helps.
Tell me if you manage to get it to work or if you have any other issue.
Great discussion but has anybody got a complete VS2010 solution with the working example that compiles?
Hi Steve,
what issue have you with your VS 2010 solution?
You should have 3 projects: 1 for the .Net API, 1 for the C++/CLI wrapper, and 1 for the native application.
I created a C# class library for the YahooAPI. Then I added another project containing a C++ header and source file for YahooWrapper supporting CLR. The last project consists of the test.cpp. I have a problem in the following class in the YahooWrapper:
class YahooAPIWrapperPrivate
{
public: msclr::auto_gcrootyahooAPI;
}
I am getting the error “class msclr:: auto_gcroot<>, wrap a resource to enforce strict ownership d ensure proper clean up. “YahooAPI” is not a type name.”
I would appreciate any help.
Thanks
The class is defined as follows as I made a mistake in the original question:
class YahooAPIWrapperPrivate
{
public: msclr::auto_gcrootyahooAPI;
};
Hello Marija,
is the C# project correctly referenced from the C++/CLI one?
I have this header file
// ClassLibrary4.h
using namespace System;
using namespace Microsoft::Kinect;
using namespace std;
using namespace System::Runtime::InteropServices;
//using namespace Distance_Test;
//#using
class clrPrivate;
class __declspec(dllexport) clr
{
private: clrPrivate* _private;
public: clr();
public: ~clr();
public: String^ calculate( Double Robot_Points_Values[],array human_point_cloud , unsigned char bodyindexdata[]);
// TODO: Add your methods for this class here.
};
and for .cpp this:
using namespace Distance_Test;
using namespace std;
using namespace System::Runtime::InteropServices; // Maratos
using namespace Microsoft::Kinect;
using namespace System;
class clrPrivate
{
public: msclr::auto_gcrootDistance_Calc_Library_Test21;
};
clr::clr()
{
_private = new clrPrivate();
_private->Distance_Calc_Library_Test21 = gcnew MINIMUM_DISTANCE();
}
String^ clr::calculate(Double Robot_Points_Values[], array human_point_cloud, unsigned char bodyindexdata[])
{
//String^ output = _private->Distance_Calc_Library_Test21->calculate(gcnew Double(Robot_Points_Values), (gcnew CameraSpacePoint human_point_cloud), gcnew(unsigned char bodyindexdata));
//return _private->Distance_Calc_Library_Test21->calculate->value_return(Robot_Points_Values,human_point_cloud,bodyindexdata);
String^ output = _private->Distance_Calc_Library_Test21->calculate.value_return( Robot_Points_Values,human_point_cloud,bodyindexdata);
}
//return output;
//return _private->Distance_Calc_Library_Test21->calculate->(gcnew String^( robot_position, human_point_cloud, bodyindexdata));
clr::~clr()
{
delete _private;
}
but i have these 2 errors :
1)Error C3395 ‘clr::calculate’: __declspec(dllexport) cannot be applied to a function with the __clrcall calling convention Line 19 of my ClassLibrary4.h
2)Error C2228 left of ‘.value_return’ must have class/struct/union ClassLibrary4.cpp Line 32 of my ClassLibrary4.cpp
Could you please give me some help?
ALSO for the .cpp file
#using “Library_Test2.dll”
#include
#include “ClassLibrary4.h”
#include
1) The wrapper must have a native interface, not a managed one, so you can’t use the managed/CLR types like
String
.2) How is
value_return
defined?Thank you for updating this article with new memory control. Lots of examples out there with managed calling native, not so many with native calling managed. I originally used the “ManWrap” stuff from ages ago and everything started breaking on win 10 and server 2012. I got that to work then XP broke. I will be updating my “ManWrap” based stuff to this. A much more straight forward approach. Hopefully that will fix the mystery problem I was having when code gated from native to CLI. I will let you know how it works out.
You also need to _declspec(dllxport) _declspec(dllimport) on the header file using the usual tricks.
I hope it will be helpful to you too.
Good luck for that and keep us informed. 🙂
I have a vs2010 project of this if you would like a copy to post here. Send me an email if you would like it.
As discussed I’ve just published it.
Just upgraded it to VS 2015.
Thanks Tim. 🙂
Hi, thank you for your article, your sample is working well. Instead I have a question if you can help me please. How can I notify from my c# dll the c++/cli module that an event is triggered?
Thank you in advance.
Hi,
here is a sample I did for another similar request:
– the C# API which raises events:
– the C++/CLI wrapper:
– the C++/CLI wrapper’s header file:
– the native C++ program:
Compilation steps:
– C# API: csc /target:library StockAPI.cs
– C++/CLI wrapper: cl /clr /LD StockAPIWrapper.cpp
– native C++ program : cl main.cpp StockAPIWrapper.lib
Hi,
Thank you for the article. Just working on the similar problem.
I am wonder about necessity __declspec(dllexport) .as far as code used as static library.
Also to proper apply pimpl idoom it looks better to use smart-pointers.
I have changed a while the code by replacing ptr to unique_ptr and make
YahooAPIWrapper class a proxy. The class functions just repeat in YahooAPIWrapperPrivate and all marshaling done inside.
So at the end it works exactly the same. Here the changes if you interesting in:
What do you think?
Hi Alex,
thanks for your feedback and for sharing your enhancements.
I’ve added some code markup so all code should be visible now.
Do not hesitate to fix it.
As for the
__declspec(dllexport)
you may be right indeed, but I’m far from being a C++ expert. 🙂sry it’s eat some symbols in the code. IN YahooAPIWrapper.h
need include memory.h and use unique_ptr instead of ptr to avoid delete in destructor.
Hi,
Just wanted to say thanks for this article!
Information on this subject is scarce on the net, and I think your article is the best I’ve found.
Cheers.
Hi Eran,
glad this article has been helpful to you. 🙂
Hi Sir,
thanks for your article, It really helped me to understand this concept. I am here trying to use the existing C# API in native C++. While doing so, as you mentioned i am trying to use SWIG to generate the C++\CLI wrapper using my C# API assembly. But i couldnt see the help for the same, instead i am seeing the reverse to use C++ in C#. Can you please let know where should i find help to generate C++\CLI wrapper using my C# Code?
Hi,
thanks for the feedback. 🙂
As for SWIG indeed it won’t be a good fit for this use-case.
I fear you’ll have to write the wrappers yourself as described in this article or use some other intermediate layer that might ease the interfacing, like COM.
Indeed COM as some tools to automatically generate metadata components (type libraries TLBs) that would act like a pivot between the two worlds (.Net and C++).
The idea would be to generate the TLB from the C# code with TLBEXP and to import it on the native C++ side.
Of course COM comes with its own set of issues and craftsmanship so if you have never worked with it it’s probably not worth the trouble to learn it.
So if you have a few classes sticking with the manual C++/CLI wrapper creation is the easier and safer path.
I have a unusual issue here… I have a “c# .dll” that communicates with a SOAP Web Service, and I’m working in an old project developed in Gupta CTD 3.0… In that enviroment I can call .dll’s, but not written in C#, I’ve tried with one made in c++ and it worked… So, my question is if I can use this shown method to make a c++ .dll that communicates through a c++/cli wrapper to my c# .dll… I don’t know if I was clear enough. Thank you for the post!
Yes your use-case is exactly covered by this article, so you can directly use the technique presented here.
Hopefully it won’t be too difficult, especially if your API is quite simple.
Hello, thanks for the nice tutorial.
I am getting an error in my native c++ code when I define the object of YahooAPIWrapper class. I am using vs2015.
First I created C# class library in order to generate the C# dll. I copied the source code exactly as mentioned in the tutorial. Then for the C++/CLI part, I created a new C++ CLR Class library and copied the source code into YahooAPIWrapper.cpp and .h. This generates a dll and lib file. Now, I create a C++ project and include the header file in main.cpp. For including the library, I mentioned the location in “Additional Library directories” in Linker->General and also mention the lib file in Linker->Input.
Now when I run this code, I get the following error:
Unhandled exception at 0x739E08C2 (KernelBase.dll) in CallYahooWrapper.exe: 0xE0434352 (parameters: 0x80070002, 0x00000000, 0x00000000, 0x00000000, 0x72260000).
This occurs at the line – YahooAPIWrapper yahoo;
Could you please help me with the error.
Thanks
I also tried your VS project. I get the same error. Your project is able to declare the object yahoo, but the error occurs when yahoo.GetBid(…) is called.
Hello,
thanks for the feedback.
Maybe this is due to the Yahoo API being down.
Please check with a basic code not requesting data from it.
Thanks for your reply.
I tried the following and it worked successfully.
I removed all the methods given in your code and made a simple code which shows a message box and it worked.
Glad all is running fine now.
I don’t know what they’ve changed and why but indeed it has broken my samples. 🙁
Hello Pragmateek,
I have used this wrapper for various projects. I am able to pass single variables such as int or string using the wrapper between c# and c++. I am currently stuck on passing byte array.
I have a c# dll for which i have made a c++cli wrapper. With this I can call the c# functions in native c++. The c# code has an image as a byte array and bitmap. I want to pass this image to native c++ code for further usage. I have checked various links on SO and it seems that there is a solution using Lockbits but I do not understand it. Is it possible for you to make a tutorial for the same or give some good sources.
Thanks
Hello Ankit,
depending on your use-case you could try to let the Bitmap operations on the C# side, and only command them from C++ through more simple method calls.
I’ve never used the Bitmap API but the MSDN sample seems similar to your use-case: Bitmap.LockBits
Except that in your case you would pass a pointer to the copied array back to C++ instead of manipulating it on the C# side.
I would just like to say, as a fellow developer/blogger and C++ wrangler, the energy and commitment you’ve put into helping each and every poster here is, without doubt, inspirational and admirable. You deserve a medal!
Thanks for the kind words Giles.
Keep it up! 🙂
Hello. There is a function:
public void Evaluate(ref float Res, ref List inputData)
{
List input0Combined = new List();
input0Combined.AddRange(inputData);
List result = manager.RunModel(modelName, int.MaxValue, evaluateInputNames,
new List { new Tensor(input0Combined, new List { 1, 1, 48 }) }, evaluateOutputNames);
List tmp = new List();
result[0].CopyTo(tmp);
Res = 0;
Res = tmp[0];
}
And there is a dll by your example:
#using “CntkAPI.dll”
#include “stdafx.h”
#include
#include “DLLCriD.h”
using namespace System::Runtime::InteropServices; // Marshal
class LoadAPIModel
{
public: msclr::auto_gcrootcntkNET;
};
ClassNet::ClassNet()
{
pmodel = new LoadAPIModel();
(*pmodel).cntkNET = gcnew CntkAPI::CntkNET();
}
void ClassNet::Evaluate(float& Res, std::list& inputData)
{
pmodel->cntkNET->Evaluate(Res, … ); // Here is the problem
}
ClassNet::~ClassNet()
{
delete pmodel;
pmodel = nullptr;
}
How to transfer a list to a function?
I would appreciate any hint.
Hello,
I don’t think you can pass directly a
List
which won’t be understood on the native C++ side.But you can pass an array as demonstrated in this comment.
Loaded projects into VS 2015 (it upgraded the Wrapper), built OK but run/debug failed with
Unhandled exception at 0x75e21812 in Test.exe: 0xE0434352: 0xe0434352.
Trying to find a solution like this – calling C# code from native C++ – and nothing works
Hi Yuri,
Please try the command-line build to ensure the issue is specific to VS.
GThanks for this tutorial. It really helped me.
Thanks pragmateek. This is nice and helpful tutorial.
I have an application which is developed in C++ we can say it A . I have made a wrapper B.dll which is calling C# C.dll. C.dll has the functionality which is running a thread. By referring above example I am able to call methods present in C.dll and successfully passed parameters but thread isnt going up.
C.dll
public void CallFromWrapper(Structure_variable V1)
{
// I am able to access the parameters present in V1 strucure coming from the Wrapper B.dll application.
Thread newThread = new Thread(() => dummyCalltoCSharpAPI(V1));
newThread.SetApartmentState(ApartmentState.STA);
newThread.Start();
}
public void dummyCalltoCSharpAPI(Structure_variable V1)
{
Application.Run(new C(V1)); //Here C is the constructor
}
But it is not starting the “C” constructor after following all the steps. Inside “C” constructor there is UI present.
Hi Abhi,
is it a Winforms or WPF application?
I wonder if the issue might be that the main thread is terminating so the process is killed.
Try to run the message-loop on the main thread, i.e. do not call
Application.Run
in a dedicated thread but directly inCallFromWrapper
.Hey Pragmateek team.Thank you for your quick response.
It is Winform application.
I have called directly “Application.Run” in “CallFromWrapper” It is working fine now. 🙂
I’m glad all is working as you wish.
Keep it up! 🙂
I have a query again, in wrapper B.dll which is calling C# C.dll. Where C.dll returning a structure array. I have did following thing and it is not calling the method from C.dll.
B.dll
class BCLIAPIWrapperPrivate
{
public: msclr::auto_gcroot API;
};
BCLIAPIWrapper::BCLIAPIWrapper()
{
_private = new BCLIAPIWrapperPrivate();
_private->API = gcnew C_NAMESPACE::C_ClassName();
}
native_structure* BCLIAPIWrapper :: CallFromWrapper(Structure_variable V1)
{
C_NAMESPACE::Managed_Structure_variable managedObj1;
// copied all structure data in managedObj1 here from V1
cli::array^
managedStructObj2 = _private->API->CallFromWrapper(managedObj1);
//Copied all data from managedStructObj2 to NativeStructObj manually
return NativeStructObj;
}
A.dll
BCLIAPIWrapper BAPIWrapperObj;
A_NATIVE_STRUCTURE *NativeRecData;
// Code breaking here
NativeRecData = BAPIWrapperObj.CallFromWrapper(Structure_variable V1);
Thanks in advance. 🙂
Hi Abhi,
What is breaking exactly, have you an error?
Have you a complete code I could compile and run?
Hi,
I’m updating a C# server from .NET 4.0 to 4.5 or higher. Without changing any code, I get a “System.AccessViolationException: ‘Attempted to read or write protected memory. This is often an indication that other memory is corrupt.’
I’ve searched everywhere, in different forums, blogs, but nobody is able to solve my problem. Please read the following in stackexchange: https://stackoverflow.com/questions/56085130/acess-violation-protected-memory-when-editing-a-passed-intptr-from-c-sharp-to-na
Upon finding this, I think you may be able to solve my problem because it has to do with DLLImport and passing IntPtr. Some protocol has changed to 4.5 that I need to update for this memory corruption error to go away.
Please help.
Just to be clear, it works 100% on .NET 4.0 just not on 4.5 or higher or .NET core 3.0.
The wrapper I’m using is https://github.com/IceYGO/ygosharp/blob/master/YGOSharp.OCGWrapper/Api.cs
and I’m compiling this:
https://github.com/Fluorohydride/ygopro-core/blob/master/ocgapi.cpp
everything runs fine until set_player_info which is the first to use a IntPtr (pointer)
and gives the error
and i’m guessing the reason it gives no error before that is called is because there’s no pointer being used until then
Hi – Thanks for a great tutorial! I got excited about your solution as it could be a “solution” to the proof of concept search I’ve been on to write some libraries for a 3rd party app that requires a Win32 DLL. Ideally I like to use your scheme to write the bulk of the code in a C# library and use the native library as a “pass-through”.
To that end, the native console I’m using (based on your scheme) to test the interaction between the two libraries builds and works without any errors.
However, when I connect the APIWrapper DLL to my 3rd party app I’m getting the error “External exception EO434352”.
The error occurs at the point where I try to instantiate the APIWrapper class in the ‘GetRevision’ function (see comments in below code).
Any insight would be greatly appreciated!
// APIWrapper.h
#pragma once
#define DLL_EXP extern “C” __declspec(dllexport) // please note this is new – an addition to your scheme
DLL_EXP void GetRevision(char* data_in, char *data_out);
DLL_EXP void Foo(char* data_in, char *data_out);
class APIWrapperPrivate;
class __declspec(dllexport) APIWrapper
{
private: APIWrapperPrivate* _private;
public:
APIWrapper();
~APIWrapper();
const char* GetRevisionFunc(const char* rev);
};
// APIWrapper.cpp
// – snip –
DLL_EXP void GetRevision(char* data_in, char *data_out)
{
MessageBox(NULL, TEXT(“MsgBox1”), TEXT(“Test”), MB_OK); // msgbox works!
APIWrapper wrp; // <libraryAPI->GetRevisionUtil(gcnew System::String(rev));
return (const char*)Marshal::StringToHGlobalAnsi(managedCapi).ToPointer();
}
Hi Jon,
thanks for your feedback. 🙂
If you only need to expose native entry-points I advise you to use a simpler way with DllExport.
The C++/CLI wrapper approach is worth the trouble if you need to expose an object-oriented API.
Hi, Your last sentence gave me a bit a of a pause: “The C++/CLI wrapper approach is worth the trouble if you need to expose an object-oriented API.” … because that is what I thought I WAS attempting. 🙂
Please note that your scheme seemed very well suited to what I’ve been trying to achieve – until I ran up against the error that I describe above.
I guess the question is – does it seem possible to resolve the error I’m seeing?
Hi Pragmateek, it seems I should clarify my situation a bit.
I’ve got some equipment for which I’ve written some VB6 functions over the years. And of course with Win10 it’s becoming increasing difficult to support these VB6 apps. So my hope has been to upgrade these functions to .Net. This would be easy enough except that I never really liked the way these apps interfaced with the 3rd party app our equipment uses. Ideally these functions would connect using a DLL.
Over the years, at various intervals, I’ve taken a run at this task – trying to understand how to write the DLL code to make the connection. Well this year I finally figured out that piece (It should be obvious that I’m not a C++ programmer). Once I solved the first part, I then started trying to understand of it were possible to simply use the Win32 DLL to send & receive string values from a C# library – where I’m a bit more comfortable.
And then I found your project! And it seemed like your scheme could get me very close.
Now you mentioned using ‘DllExport, but from what I can see it’s more C++ intensive, and it doesn’t meet my main requirement – which is a Win32 DLL.
I should note that the guidance provided by our 3rd party equipment vendor is that it expects the following structure:
#define DLL_EXP extern “C” __declspec(dllexport)
DLL_EXP void _MY_FUNC(char* data_in, char *data_out);
It’s seems I’m pretty close to a solution with using your scheme – if I can just figure out how to instantiate the wrapper class without throwing the error.
Any suggestions you could provide would be greatly appreciated.
Hi Jon,
what I mean is that the API between the equipment and your code is not object-oriented, the equipment won’t instantiate some classes of yours, but will call some entry-points (~static methods) through a C interface.
So you could follow this “tutorial” from master Hans Passant: C# Unmanaged Exports 🙂
This is the safest and fastest path.
Using tools like “.Net DllExport” I’ve mentioned or “Unmanaged Exports” should be easier but I’ve tried to make a PoC and unfortunately both are a complete PITA. 🙁
Hi Pragmateek, Just to follow-up – your link to Master Hans tutorial was very helpful in resolving/simplifying the calls from the un-managed library to the managed.
And for the sake of any who might be following my tale of woe, I was able to resolve my “External exception EO434352” error by using WinDbg (long story short – it ended up being a simple ‘file not found’! 🙂 ). I’ve chronicled all of this in my lengthy post at SO: https://stackoverflow.com/questions/60961498/external-exception-e0434352-on-calling-a-c-cli-wrapper/61354563#61354563
Thanks again for all your help 🙂
Hi Jon,
Thanks for the follow-up.
I’m glad you’ve fixed your issue.
It’s why I prefer the Visual Studio C# debugging experience. 🙂
Oops! Somehow the above code lost a few lines once it was posted. Here is the pertinent “wrapper” code again:
// APIWrapper.cpp
// – snip –
DLL_EXP void GetRevision(char* data_in, char *data_out)
{
MessageBox(NULL, TEXT(“MsgBox1”), TEXT(“Test”), MB_OK); // msgbox works here!
APIWrapper wrp; // <libraryAPI->GetRevisionUtil(gcnew System::String(rev));
return (const char*)Marshal::StringToHGlobalAnsi(managedCapi).ToPointer();
}
I’m trying to make it work by copying and pasting the code with no changes to just get it up and running. I get compile errors on the c++ exe that will run everything. The errors are LINK2019 errors unresolved external symbol on the YahooApiWrapper methods. I’m not a C++ guy so any help would be appreciated
Hi Daniel,
Sorry for the late answer but I was OOO.
Are you using a CLI to compile?
If so please share the commands you use.
Hi, great to see an article like this which is still getting answers from the author!
I’m trying to use this method on a .NET 5 C# dll as we want to use EntityFrameworkCore as a DB layer for a legacy native C++ application. However when my application comes to load the dlls I get the following;
System.IO.FileNotFoundException
HResult=0x80070002
Message=Could not load file or assembly ‘MyDll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’. The system cannot find the file specified.
Source=
StackTrace:
I tried to create a MyDll.runtimeconfig.json (an article I saw suggested that this would be required and wasn’t currently being generated for the c# dll) but it hasn’t made a difference. I placed it in the folder where my native C++ .exe and managed C++ apiwrapper are – and in the build folder.
I checked I hadn’t strayed too far from your article (My file names & methods are different, but I followed the method) by building the same project but using .NET Framework 4.7.2, cut & paste my classes/code from the .NET 5 project and it works like a charm.
I appreciate .NET 5 is still a work in progress – should I be using a different method to try this or is there any known workaround for this?
Hi Phil,
I don’t know .NET 5, but as a first step I would trace the process with Process Monitor (aka ProcMon) in order to know in which places the runtime is looking for the assembly.
Pingback: The Advantage of a CLI Wrapper
Unhandled exception at 0x756AB662 (KernelBase.dll) in Test.exe: 0xE0434352 (parameters: 0x80131509, 0x00000000, 0x00000000, 0x00000000, 0x6F0C0000).
Got this issue while running this project.
Sorry for the late answer, hopefully you’ve managed to fix the issue in the meantime.
Otherwise, are you using the exact same code?
I find the same exception, it appears that yahoo has terminated the service
https://stackoverflow.com/questions/49688080/need-in-understanding-exception-at-runtime-in-c-cli-project-execution
I thought I could change the url to point somewhere else like nasdaq but same exception.
Any ideas appreciated.
Hi Mike,
Indeed, you’d better off writing a mock/stub of this Yahoo service.
How about going the other way from the charp code to the vc++ clr managed project to the native project.
How would the vc++ clr managed code provide a call backfunction to the c# project that the c# project would call, and how would the vc++ clr project get a callback function pointer from the native c++ project to call
You can use C++/CLI to wrap it the other way and call native C++ from C#, and tools like SWIG could help.
For the callback maybe have a look at How to: Marshal Callbacks and Delegates By Using C++ Interop
Hi,
Very nice work! I followed online instructions using different names, not Yahoo. But, structure is the same as your codes.
I got working from C++ ->C++ CLR -> c#. I have one question. My c# part is socket comm, and receiving data, what is best way to pass received sock data to unmanaged C++ through C++ CLR?
Hello Jian,
My pleasure.
You should be able to simply return a native pointer to the first element of the managed array, ensuring it is pinned so that it won’t be moved by the GC, to avoid copying the data to a native array: https://learn.microsoft.com/en-us/cpp/dotnet/how-to-marshal-arrays-using-cpp-interop?view=msvc-170
Hi,
Could I use he C# API which raises events for data transfer?
Hi,
Not events strictly speaking, but callbacks might be an option: the native code passes a native function to C# which calls it when it has something to notify about.
Hi,
Nice post.
Thank you for sharing!
Pingback: The Advantage of a CLI Wrapper