Leveraging C++ functions compiled with /CLR from other .NET languages

B

Bern McCarty

I have a DLL written in C++ (it's really C code that was adjusted to compile
OK as C++) that I compile successfully into IL with the /CLR switch of
Visual C 7.1. I use the resultant library successfully from other C++ code
(both native and managed) and it works fine. I would like to figure out the
shortest and most convenient path to making this library callable from other
..NET languages. The function signatures in this librariy's API are really
pretty simple. There are a small handful of value types (C structs) passed
by reference (pointer) to lots of functions. So how to arrange for these
functions to be callable from say, C#, by passing value types as reference
or out parameters?
The first problem that I see is that the library is just a bunch of
functions in the global namespace and C# apparently doesn't let you look up
anything in the global namespace except other namespaces so the functions
aren't accessible. This, if I understand correctly, is to be addressed in
Whidbey.
The next problem is that it looks like the the Visual C++ compiler defines
..NET value types to represent my C structs to the managed world with dummy
definitions. If run ildasm on my mixed DLL and have a look I will see that
my C structure which looked like this in C++

struct dPoint3d {double x; double y; double z;};

....get's represented on the managed side as a value type with nothing in it
like this:

.class /*02000006*/ public sequential ansi sealed 'dPoint3d'
extends ['mscorlib'/* 23000001 */]'System'.'ValueType'/* 01000003 */
{
} // end of class 'dPoint3d'

I can see how it is that the C++ compiler was nevertheless able to generate
the correct IL for my functions; afterall it had the real definitions of
these structs from my header files. It was the compiler's decision to make
the corresponding .NET value types opaque to the rest of the world by not
filling them in. So how might I arrange to have these value types made
"real" so that this API can be called from other .NET languages? Is there a
secret VisualC++ compiler switch that will get it to define these types
fully? Some other way to make this API callable from C#?

Bern McCarty,
Bentley Systems, Inc.
 
G

Gary Chang

Hi Bern,
So how to arrange for these functions to be callable from say, C#, by
passing value types as reference or out parameters?You can create an equivalent structure in the C#(maybe need some
Marshalling work for some unmanaged types), then pass its reference.

So how might I arrange to have these value types made "real" so that this
API can be called from other .NET languages? Is there a secret Visual C++
compiler switch that will get it to define these types fully? Some other
way to make this API callable from C#?There is no such a "Silver Bullet" compiler switch, my opinion is to use
the types which have the directly corresponding .NET value types as the
types of your DLL functions' parameters.


By the way, I think maybe you needn't to care for such problems, the .NET
program can PInvoke an unmanaged DLL easily:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/
vcwlkPlatformInvokeTutorial.asp

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmxspec/ht
ml/vcmg_PlatformInvocationServices.asp

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmex/html/
vcgrfmarshalingstructures.asp


Thanks!

Best regards,

Gary Chang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
 
B

Bern McCarty

Gary,

Can you explain how P/Invoke is relevant to me here? I'm trying to call
managed code from managed code. I'm pretty sure all of my structures are
perfectly representable as .NET value types so why would any marshaling be
necessary?

The code that I want to call just happens to be C++ code compiled with /CLR.
The functions I want to call are managed functions that take value types as
reference or out parameters. The problem appears to simply be that the
value types emitted by the compiler are, well, wrong in that they're all
empty. I will try to define redundant/extra value types that are right and
see how it goes trying to pass them into these methods. Assuming that I can
figure out how to get the compiler to be willing to convert a reference to
one value type (the one that I defined explicitly) to the one that the C++
compiler defined (that the methods actually expect and which are wrong) then
I guess it should work.

Even if it does work it is a lot of manual transformation of code that
already exists and already compiles into IL just fine and is already
callable from C++ just fine. Seems like there should be a shorter path to
having it be callable from C# too. I don't know if round tripping mixed
dlls through ildasm/ilasm is possible (probably not easily if at all) but if
it were I could imagine automating it and putting the missing value type
members back in... In my case these C structures are all "Plain Old Data".
No pointer members, nothing fancy. So it seems easy to imagine the C++
compiler at least having an option to emit accurate value types for POD
structures. It would be a huge boon to language interoperability. Yeah I
can do great stuff very easily with Managed C++, but only C++ programmers
are able to call the managed code that results. I don't want to cut out
programmers that prefer C#.

I'm trying to be friendly to the .NET programmer by having this library be
in managed code in order to eliminate managed/unmanaged transitions. Due to
the nature of and purpose of this library that seems important. But if I
can't figure this out I'm left with forgetting about that and just providing
a P/Invoke layer to the native version of the library. I'd hate to have to
trade performance for language interoperability (or for writing and
maintaining a bunch of new code that makes it work somehow).

-Bern
 
G

Gary Chang

Hi Bern,

Thanks for your response!
Can you explain how P/Invoke is relevant to me here? I'm trying to call
managed code from managed code. I'm pretty sure all of my structures are
perfectly representable as .NET value types so why would any marshaling be
necessary?

From my view, the native C/C++ program compiled with /CLR option is not a
real managed code program, it only has been added the corresponding .NET
metadata and just look as an assembly, it will be executed as a unmanaged
code(it really is.)

So my meaning is you can directly PInvoke the unmanaged C/C++ DLL from your
..NET program(e.g. C#) without compile it into an assembly with the /CLR
option.


Thanks!

Best regards,

Gary Chang
Microsoft Online Partner Support

Get Secure! - www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
--------------------
 
B

Bern McCarty

For anyone who might be interested here is what I was able to figure out. I
was able to define .NET value types that were "equivalent" to the empty ones
that are produced by the C++ compiler when compiling my C structures with
/CLR. Of course I had to use a different type name/namespace. After
wrapping the C API in a __gc class I was even able to call the API from C#
successfully by casting from a pointer-to-type A to a pointer-to-type A'. I
had to use unsafe C# to do that. Wrapping the API as static methods inside
of a __gc class was necessary I think for two reasons: 1) You cannot access
global/static functions or data from C# and my API consists of a bunch of
functions at global scope. 2) C# doesn't support calling an managed method
that has cdecl calling convention and that is apparently what you get when
you compile plain old C++ with /CLR. MEC++ does let you call managed
methods with cdecl calling convention (naturally, since it generates them)
so it was necessary to write the __gc wrapper class in MEC++.

The experience made me think that it might be worth trying to adjust my C++
codebase cleverly so that I could henceforth compile the same codebase two
different ways: once into native code as it has always been but also with
the /CLR switch such that the C structs that are passed by reference to/from
the API are declared as MEC++ __value types instead of native structs that
end up getting represented by dummy __value types. Then the clients of the
managed form of the library could have these types on the stack, embedded in
reference objects, on the native heap, etc. This should obviate the need
for pointer support in the client language as was required with the approach
of manually creating "equivalent" types and casting pointers between them.
This idea fell apart quickly as I realized the degree to which the C structs
used by my API contain embedded arrays. I see that support for embedded
arrays is planned for version 2.x of the CLR.

If I can ever get my hands on the C++/CLI version of the compiler and
runtime I still think that this might fly.

Bern McCarty
Bentley Systems, Inc.

Bern McCarty said:
I have a DLL written in C++ (it's really C code that was adjusted to compile
OK as C++) that I compile successfully into IL with the /CLR switch of
Visual C 7.1. I use the resultant library successfully from other C++ code
(both native and managed) and it works fine. I would like to figure out the
shortest and most convenient path to making this library callable from other
.NET languages. The function signatures in this librariy's API are really
pretty simple. There are a small handful of value types (C structs) passed
by reference (pointer) to lots of functions. So how to arrange for these
functions to be callable from say, C#, by passing value types as reference
or out parameters?
The first problem that I see is that the library is just a bunch of
functions in the global namespace and C# apparently doesn't let you look up
anything in the global namespace except other namespaces so the functions
aren't accessible. This, if I understand correctly, is to be addressed in
Whidbey.
The next problem is that it looks like the the Visual C++ compiler defines
.NET value types to represent my C structs to the managed world with dummy
definitions. If run ildasm on my mixed DLL and have a look I will see that
my C structure which looked like this in C++

struct dPoint3d {double x; double y; double z;};

...get's represented on the managed side as a value type with nothing in it
like this:

.class /*02000006*/ public sequential ansi sealed 'dPoint3d'
extends ['mscorlib'/* 23000001 */]'System'.'ValueType'/* 01000003 */
{
} // end of class 'dPoint3d'

I can see how it is that the C++ compiler was nevertheless able to generate
the correct IL for my functions; afterall it had the real definitions of
these structs from my header files. It was the compiler's decision to make
the corresponding .NET value types opaque to the rest of the world by not
filling them in. So how might I arrange to have these value types made
"real" so that this API can be called from other .NET languages? Is there a
secret VisualC++ compiler switch that will get it to define these types
fully? Some other way to make this API callable from C#?

Bern McCarty,
Bentley Systems, Inc.
 
Y

Yan-Hong Huang[MSFT]

Hello Bern,

You are right here. In order to make managed C++ class easily used by other
dev languages, we'd better use managed extensions for C++ programming. In
VS.NET 2003, we have a sample on it.
C:\Program
Files\Microsoft.NET\FrameworkSDK\Samples\Technologies\CrossDevLanguage

This sample demonstrates the use different development languages in a
single project. This sample creates two assemblies. The first is a library
or DLL assembly that defines a simple base class written in managed
extensions for C++. The second assembly is an executable assembly that
defines three derived classes written in C#, VB, and IL (Intermediate
Language). These types derive from each other and ultimately from the base
class written in managed C++. Finally, the executable creates instances of
each of the derived types and calls a virtual method for each. The .NET
Framework is an environment where various developers can work together
seamlessly while developing in their language of choice.

Hope that helps.

Thanks.

Best regards,
Yanhong Huang
Microsoft Community Support

Get Secure! ¨C www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top