Consuming native C++ (compiled with /CLR) in C#: why does it pretend to work? - interop

I have a body of 'normal' C++ code which I'm trying to make usable by a C# client. I have successfully compiled this with /CLR. I now know that this isn't enough: I have to introduce managed wrapper classes ("ref") to make the code callable from managed code. This question is about what happened before I introduced the ref classes.
I found that the native C++ classes were visible from the C# project, and that I could write
MyNativeClass mnc = new MyNativeClass();
... although any attempt to call a method on the instance was rejected by the compiler. I found that when I ran the C# code, the MyNativeClass constructor was never called - indeed the attempt to instantiate mnc seemed to produce no code at all, so completed without error.
How was C# interpreting the native types in my project? Why did the compiler apparently allow me to instantiate an instance? Why were methods treated differently to the types themselves?

You made two mistakes to make this code work. First is that you declared the C++ class public, like this:
public class MyNativeClass {};
This is not syntax that makes sense in C++ but it is allowed by the C++/CLI compiler. Omitting public would have produced an error message in your C# code, CS0122: "Foo is inaccessible due to its protection level".
Second mistake is that you compiled your native C++ class with /clr in effect. Which works fine, any C++03 compliant code can be compiled to IL and gets just-in-time compiled to machine code by the jitter, just like managed code. And executes fine as well. It is however not efficient to do so, you lost the advantage of having the compile-time optimizer available to produce the best possible code. It still gets optimized, but now by the jitter which doesn't have the same luxury of time available to do as good a job as the C++ code optimizer can do. You avoid this by moving the C++ code in a separate source code file so you can compile it without /clr in effect. Or by using #pragma managed in your source code.
Anyhoo, what you ended up with was an assembly that indeed contains a declaration for MyNativeClass that any managed compiler can see. Something you can see for yourself by running ildasm.exe on the assembly. You'll see that it gets embedded as a value type type. Just a blob of bytes with no constructor and no methods. Which is a decent match for a C++ object from a managed point of view, just bytes that can be stored anywhere a value type can be stored. Declaring a variable of that type in C# works but doesn't do anything useful, it just creates the blob with all bytes set to 0. The only possible use of this declaration is that you can declare a typed pointer to the class and pass it around, that's all.

Related

Is there any class count limit in MFC project compiled with /CLR

With the risk to fall into too specific question...
Given a C++ MFC (mixed, not shaked) project compiled with /CLR, I have 200 classes already defined.
When I add a new empty class to this project, an error raises when I compile and execute in debug mode.
An unhandled exception of type 'System.IO.FileLoadException' occurred
in Unknown Module.
Additional information: Could not load file or assembly 'ProjectA,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its
dependencies. Could not find or load a type. (Exception from HRESULT:
0x80131522)
ProjectA is the name of the MFC project itself. There is no reference to any ProjectA assembly on project configuration, and there is no reference to another custom assembly.
This project only have references to some .NET Framework assemblies, in order to allow that some of custom defined classes in the project can use CLR classes.
Then, the question is...
Do you know whether there is any limitation of class number on a MFC C++ project?
EDIT:
As I say in comments, in release mode the compilation succeed without errors.
Also, I clean, build, clean, close Visual Studio, reboot computer... and the problem still appears.
If I keep in 200 classes, there is no error. When I go to 201, the error appears.
Currently I'm trying to reproduce in a new default MFC project, adding classes till arrive to 200, to confirm that there is a real limitation.
EDIT 2: ERROR FIXED
Great. #MSX and #frymode show me how avoid the error with his comments.
In the Visual Studio development environment (source / source):
Open the project's Property Pages dialog box.
Click the C/C++ folder.
Click the Code Generation property page.
Modify the Enable String Pooling (/GF) property.
Thank you guys!
The /GF hack is a known workaround for this problem. It is however not a correct one, you are putting a band-aid on a heavily bleeding wound. Pretty important that you heal the problem instead of putting a patch on it, this heavily affects the way your program runs at runtime as well.
The problem maker is the <Module> class, an internal class that the C++/CLI compiler generates. You can see it with ildasm.exe or a good decompiler. This class is required as a home for declarations in your program that are not class members, valid in native C++ but not supported by the CLR. Which demands that every variable or function declaration is a member of a class. The C++/CLI compiler solves it by moving such a declaration into the <Module> class.
There is however a limit on the number of members in a class, they are represented in the metadata of the .NET assembly with a metadata token. An index into other tables. The upper byte identifies the table number, the lower bytes are the index in the table.
You exceeded that limit. This is bad.
A problem with the /clr compile option is that it works too well. It is capable of compiling any C++03 compliant native C++ code to MSIL. Such code will be just-in-time compiled to machine code by the jitter, just like normal managed code. It is however not verifiable code and it doesn't act like managed code at all, you can blow up your program with pointer fumbles just as easily. And above all, it is not code that's optimized with the native C++ back-end. Only the jitter optimizer has a shot at improving that code, it cannot do nearly the quality job that the native C++ optimizer can do, given that it runs with a hard upper limit on how much time it can spend on doing that job.
Have a look-see with a decompiler to see what ended up in that <Module> class. That's going to be overwheliming at first, you know you've got a big one right now. You fix this problem by spending more time segregating the code into the managed parts and the native parts. Where the native code should be compiled without /clr in effect. It is a per-source file setting, you can even switch back-and-forth in a single source code file with #pragma managed. The simplest way to segregate is by keeping your native code in its own library or DLL.
This link shows that there's no limit to the number of types you can have in a namespace (not a project). Considering, that a namespace can be split across different assemblies, you could, at least in theory, have an unlimited number of types. However, this post affirms that the maximum number of classes in a .DLL
is 16777215. Probably, you'll run out of memory before you reach that number of classes :)
Just for information: there seems to be a limit to the number of fields per class, though.
P.S.:
Here's the solution to your problem taken from this link
Open the project's Property Pages dialog box.
Click the C/C++ folder.
Click the Code Generation property page.
Modify the Enable String Pooling property.

Calling a function in a managed DLL from native Visual C++ code through COM

Apologies in advance for the following verbose question ; I am a COM noob.
Scenario: I need to call a managed DLL built with C# from native Visual C++ code. In my native VC++ code, I do the following after registering "SomeDLL.dll" and generating the "SomeDLL.tlb" file with RegAsm.exe.
Import the TLB file with #import "SomeDLL.tlb"
Use the class MyClass defined in the DLL with CComPtr<MyClass>.
Everything's great! It compiles, and I can run the code etc. It hits the fan when I try to run this application on a different machine (i.e. not the one I compiled it on). I copy all the required DLLs, and I register the same DLL with RegAsm.exe but it doesn't work.
It specifically fails when it tries to initialize the COM library with CoInitialize(0) and returns the S_FALSE error which means
The COM library is already initialized on this thread.
I can confidently state that I have not called this function anywhere else in my code.
Any suggestions?
Hard to help you find that code from here, you're a lot closer. Maybe a DLL that gets injected.
Getting S_FALSE is not an error, getting RPC_E_CHANGED_MODE would be quite bad. Be sure to use the FAILED macro:
HRESULT hr = CoInitialize(0);
if (FAILED(hr)) {
CallNineOneOne(hr);
exit(hr);
}
Maybe you called OleInitialize or another function which calls ComInitialize behind the scenes.
Anyway, it does not matter to call CoInitialize several times per thread if you match each of them with a call to CoUninitialize

How do I compile boost using __cdecl calling convention?

I have a project compiled using __cdecl calling convention (msvc2010) and I compiled boost using the same compiler using the default settings.
The project linked with boost but I at runtime I got an assert message like this:
File: ...\boost\boost\program_options\detail\parsers.hpp
Line: 79
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
There are the following questions:
what calling convention does boost build with by default on Windows (msvc2010)
how to I compile boost with __cdecl calling convention
why boost wasn't able to prevent linking with code with different calling conventions? I understood that boost has really smart library auto-inclusion code.
Update #1
It looks that boost does compile and link with proper calling convention, still at runtime I get the above problem. I did a sample application using the same code and it works but in my application it fails. The only difference could be from project configuration or includes/stdafx.h
Just use
bjam ... **cxxflags=/Zp4**
while building boost libraries.
As far as I know there's not way to make C++ use cdecl calling conventions (see MSDN Calling Convention). The C++ method calling is just different from C. The only opportunity that you have to use one of the C calling conventions is for functions, which include class static functions in C++. If you know that's the case you can try forcing the option when building by adding the option during the build:
bjam cxxflags=/Gd ...
(see BBv2 Builtin features)
Or to make it "permanent" set up a user-config.jam with your compiler and add it to the build options for all BBv2 msvc builds (see BBv2 Configuration and related docs). As for you other questions:
Boost uses the default calling convention MSVC uses, except for cases where it overrides it at the code level. I don't know where those are as they are library specific. So you'd have to search the code for the "__*" code decorators.
See above for partial answer.
Detection; there are two reasons: There is a limit to how many different options we can reasonably detect for for building as it's an exponential growth of different possible variations so we limit it to the most important cases. And in the case of calling convention, it's not actually possible since it's something that can be changed on a per function basis.
I found the cause of the problem inside one of the shared property files: <StructMemberAlignment>4Bytes</StructMemberAlignment>
If I remove it the code will work. Still, I'm not sure why this is happening and how could I solve it without removing the above code (that was required by another library).
I added another question regarding boost and structure member alignment.

How to link with static libraries when building an R package

I'm creating a package that is going to be used by R (the statistical program), I'm not an expert using this application but I have managed to create a very simple package, using the following logic, I have some classes in C++, as the code has to be compiled using the R compiler and it only allows C code, I have a wrapper C code that call the C++ methods, and later I have an R script that call the methods exposed by the C code, so basically is a communication like R <-> C<->C++.
The full tutorial that I used to create this package is found here, I add it as a reference.
Now my problem is that I need to add some functionality to the package that I already created, what I need to do is to add code for late binding to a COM object which is another product that I created and that is registered using regasm tool.
This is the c++ code that I'm using to try to late bind to the COM object, I'm trying to use IDispatch to do so:
{
...
CLSID clsid;
HRESULT hr = CLSIDFromProgID((WCHAR*)"My Com object ProgId", &clsid);
if(FAILED(hr))
return;
...
}
I didn't paste the whole code because only with these lines the compiler is giving me troubles already, the command I use to compile is
R CMD SHLIB Cclass.cc C++class.cc
Where "Cclass.cc" has the C code that call the c++ methods and "C++class.cc" is actually the C++ code.
When I compile these classes the compiler says "undefined reference to `CLSIDFromProgID#8'collect2: ld returned 1 exit status"
I"m sure I have added all the header files that I need, that's why I believe my problem is that I'm not including ole32.lib and oleaut32.lib which are static libraries.
So, my question is, how can I include this libraries in order to be able to use the methods for late binding, like CLSIDFromProgID(...) or QueryInterface(...). Also if anyone believes that my problem is not linking this libraries, but something else, it would be great if can point me to which my problem may be.
Also have in mind that I need to link with those statics libraries in a way that they can be compiled without problem by the R compiler, which if I'm not wrong is a merely c compiler.
I've not tried doing this with C/C++ but rather with Fortran. I had a similar problem in that some standard IO libraries weren't being included in the library I was created. In the end I just included them all and compiled using the Fortran compiler. I didn't use any of the R compiler utilities, just compiled as if I were compiling a static Fortran library normally for use with anything else. This worked fine.
A debug path might be to compile as a static library using gcc (or whatever you're using) then try to include and call that static library from another C program, then if that works try with R.
Hope this is helpful, writing these R packages is pretty hard unless you're using vanilla C or Fortran as far as I can tell.

Strange VB6 build problems (related to nlog)

This I think is related to my use of the nlog C++ API (and my question on the nlog forum is here); the purpose of my asking this question here is to get a wider audience to my problem and perhaps to also get some more general ideas behind the VB6 IDE's failure to build in my particular scenario.
Briefly, the problem that I am having is that I am having trouble building VB6 components which reference unmanaged C++ components which have calls to nlog's C\C++ API (which is defined in NLogC.DLL). The build problems are not occurring during compile time, they are occurring when the binary is being built which suggests to me that it's some kind of linker type problem? Don't know enough about how VB6 binaries are produced to tell. The VB6 binary is produced, but it is corrupted and crashes shortly after it is invoked.
Has anyone had any similar experiences with VB6 (doesn't have to be related to nlog or C++)?
edit: Thanks for all the responses to this rather obscure problem. Still no headway unfortunately; my findings since I posted this:
'Tweaking' the compile options doesn't appear to help in this problem.
Adding a reference to the nlog-enabled C++ component from a 'blank' VB6 project doesn't crash it or cause weird build problems. So it isn't a 'native' VB6 issue, possibly an issue with the interaction between nlog and the various components and 3rd party libraries used by other referenced components?
As for C++ calling conventions: the nlog-enabled C++ component is - as far as I can see - compliant to these conventions and indeed works fine when referenced by VB6 as long as it is not making any nlog API calls. Not sure if the nlogc.DLL itself is VB6 compliant but I would have thought that that is immaterial since the API calls are being made from the C++ component; VB6 shouldn't know or care about what the C++ component is referencing (that's as far as my understanding on this goes...)
edit2: I should also note that the error message obtained during build is: "Errors during load. Please refer to "xxx" for details". When I bring up the log file, all that there is in there is: "Cannot load control xxx". Interestingly, all references to that particular control disappears from that particular project resulting in compile errors if I were to try to build again.
Got around the problem by using NLog's COM interface (NLog.ComInterop.DLL) from my unmanaged C++ code. Not as easy to do as the C\C++ API but at least it doesn't crash my VB6 components.
I would try tweaking some of the Compile options found in the Project, Properties menu, Compile panel to see if they yield any additional hints as to what is going wrong.
For example if you compile the executable to p-code rather than native code does it still crash on startup.
What error message do you get when you run your compiled binary?
I doubt the compiler/linker is the problem: project references in a VB6 project are not linked into the final executable. A project reference in VB6 is actually a reference to a COM type library (which may or may not be embedded in a .dll or other binary file type). Project references primarily serve two purposes:
The IDE extracts type information from the referenced type libraries which it then displays in the Object Browser (and in the Intellisense drop-down)
At compile-time, the compiler extracts the type information stored in the referenced libraries, including the CLSID of each class that you instantiate, and embeds this data into the executable. This allows your executable to create instances of classes contained in the libraries that you referenced.
Note that the compiled binary doesn't link to any code in the referenced libraries, and it doesn't even contain the filenames of the referenced libraries. The final executable only contains the CLSID's and other type information that it needs to instantiate COM objects at run-time.
It is much more likely that the issue is with NLog, or with how you are calling it from your code, rather than something gone awry in the VB6 compile process.
If you think it might be a linker problem, this should crash it the same way:
create a new standard project (of any kind)
add a new module and copy the "declare"-statements into it
compile
If it doesn't crash it is something else.
It would help an exact description of the error or a screenshot of what going on.
One thing to check is wherever NLogC.DLL or the C++ DLL you built have the correct calling convention defined. Basically you can't have the DLL function names mangled or use anything but the STDCALL calling convention. If the C++ DLL has not been created with those two things in mind then it will fail to work with VB6.
MSDN Article on Calling convention.
"Cannot load control xxx" errors can be caused by .oca files which were created from a different version of an .ocx than currently used. If that is the case, deleting the .oca files helps.

Resources