Calling unmanaged C++ DLLs

  • Thread starter Thread starter Tom Shelton
  • Start date Start date
T

Tom Shelton

Andrew said:
I am having trouble using Platform Invoke to call C++ DLLs
from C# windows applications. The DLLs seem to load, but I
get run-time errors to the effect that the named entry
points cannot be found. The DLLs are compiled in MS Visual
C++ Version 6 and the C# applications are written in MS VS
C#.NET 2003. I am using the specifier _declspec(dllexport)
for a class in the DLL and I am using DllImport(...) for
each method imported into the C# program.
What am I doing wrong ?
Any suggestions would be welcome.

Andrew Cumming.

If I were you I would use dumpbin on the dll. I suspect that you are
experiencing the effects of namemangling.

Tom Shelton
 
I am having trouble using Platform Invoke to call C++ DLLs
from C# windows applications. The DLLs seem to load, but I
get run-time errors to the effect that the named entry
points cannot be found. The DLLs are compiled in MS Visual
C++ Version 6 and the C# applications are written in MS VS
C#.NET 2003. I am using the specifier _declspec(dllexport)
for a class in the DLL and I am using DllImport(...) for
each method imported into the C# program.
What am I doing wrong ?
Any suggestions would be welcome.

Andrew Cumming.
 
Andrew Cumming said:
I am having trouble using Platform Invoke to call C++ DLLs
from C# windows applications. The DLLs seem to load, but I
get run-time errors to the effect that the named entry
points cannot be found.
....
In general, it's easiest to implement a set of unmangled C wrappers
that serve as glue between C# and C++. My experience shows that there
are 3 essential components, usually separate projects (of course the
C# and C++ side are separate)

A) The baseline C++ Stuff

B) The 'plugin' wrapper, which provides a set of C functions that map
to the relevant C++ bits.

C) A set of classes in C# that map to the plugin.

I'll ignore A, I assume you've got the C++ code.

Here's an example of Plugin C wrappers for C++
// definitions
extern "C" {
CSEXPORT(void) SystemMessage(char* theMessage);
CSEXPORT(void) StartCore(ULONG windowHandle,int serviceID);
CSEXPORT(void) StartQuestor(char* appname);
CSEXPORT(void) OpenQuestor(char* appname);
CSEXPORT(void) ActivateSubsystems();
CSEXPORT(void) StopV3();
// PERSISTANT FILES
CSEXPORT(ULONG) CreateUMF(char* theName);
CSEXPORT(ULONG) OpenUMF(char* theName);
CSEXPORT(ULONG) ReadUMF(ULONG umfptr);
CSEXPORT(void) WriteUMF(ULONG umfptr,ULONG objptr);
CSEXPORT(void) CloseUMF(ULONG umfptr);
CSEXPORT(ULONG) LoadExternalModel(const char* theFile,short
mirrorAxes,short mirrorTexture,short reversals);
};

// and here's an example function
// PERSISTANT FILES
CSEXPORT(ULONG) CreateUMF(char* theName)
{
CUMF* aUMFFile = new CUMF();
try {
aUMFFile->Create(theName);
} catch(...) {
SAFE_DELETE(aUMFFile);
return 0;
}
return (ULONG) aUMFFile;
}

Note that we return direct object pointers to C#, which treats them as
handles, effectively. There are good and bad points to this approach
:-)

CSEXPORT(ULONG) is defined as
#define CSEXPORT(type) __declspec(dllexport) type __cdecl
Supposedly you can use fastcall, but it's a real PITA

C - The C# side of the house

public class V3UMF
{
#region V3 Interface DLL Imports
[DllImport("CS2V3.dll")]
private static extern IntPtr CreateUMF(string theName);
[DllImport("CS2V3.dll")]
private static extern IntPtr OpenUMF(string theName);
[DllImport("CS2V3.dll")]
private static extern IntPtr ReadUMF(IntPtr umfptr);
[DllImport("CS2V3.dll")]
private static extern void WriteUMF(IntPtr umfptr,IntPtr objptr);
[DllImport("CS2V3.dll")]
private static extern void CloseUMF(IntPtr umfptr);
[DllImport("CS2V3.dll")]
private static extern IntPtr LoadExternalModel(string theFile,short
mirrorAxes,short mirrorTexture,short reversals);
#endregion
private IntPtr m_umf;

public V3UMF()
{
m_umf = (IntPtr) 0;
}
public void Open(string filename)
{
m_umf = OpenUMF(filename);
}
public void Create(string filename)
{
m_umf = CreateUMF(filename);
}
public void Close()
{
CloseUMF(m_umf);
m_umf = (IntPtr) 0;
}

~V3UMF()
{
if(m_umf != (IntPtr) 0)
CloseUMF(m_umf);
}
public bool Opened
{
get { return m_umf != (IntPtr) 0;}
}
public V3Object ReadObject()
{
IntPtr ip = ReadUMF(m_umf);
if(ip == (IntPtr) 0)
return null;
else
return new V3Object(ip).DownCast();
}
public void WriteObject(V3Object anything)
{
WriteUMF(m_umf,anything.Handle);
}
static public V3Shape LoadExternalModel(string theName,bool
mirrorAxes,bool mirrorTexture,bool revFaces,bool revNormals,bool
revMatrix)
{
short mt = (short) (mirrorTexture ? 1 : 0);
short ma = (short) (mirrorAxes ? 1 : 0);
short revs = 0;
if(revFaces)
revs += 1;
if(revNormals)
revs += 2;
if(revMatrix)
revs += 4;
IntPtr theShapePtr = LoadExternalModel(theName,ma,mt,revs);
if(theShapePtr == (IntPtr) 0)
return null;
else
return new V3Shape(theShapePtr);
}
}


One last detail of note, architecture wise. You end up with object
representations on both sides of the fence, i.e. in C++ and in C#.
The thing that separates the crazed from the dead is the method by
method decision process as to whether
A) You'll reimplement the functionality on the C# side because it's
faster to do it than call it
B) Or vice versa
 
In general, it's easiest to implement a set of unmangled C wrappers
that serve as glue between C# and C++. My experience shows that there
are 3 essential components, usually separate projects (of course the
C# and C++ side are separate)

I think a case could be made for managed C++ wrapping rather than C
wrapping.

Cheers,

4Space
 
True iff you want to incorporate automatic garbage collection. Not
always the best idea when the stuff in C++ is there for significant
performance reasons. I've avoided this thus far, because of 1
question and 1 issue

question - what overhead does gc introduce into the C++ side? I care
a lot about this, the example was from a 3d engine.

issue/question - It doesn't seem I can have my cake and eat it too,
i.e. __gc is a global class operator, I can't say sometimes I want
this class managed and sometimes I don't.

Probably not bad in general, but using C wrappers makes everything
nice and clean and keeps msfts mitts out of things when performance is
an issue. All the examples I've seen thus far for managed C++ are
actually better arguments for why you shouldn't be using C++ in the
first place, or are simply contrived.

All that aside, yes a case can be made. I just have trouble buying
it.

l8ter
 
question - what overhead does gc introduce into the C++ side? I care
a lot about this, the example was from a 3d engine.

Well, if you're wrapping the unmanaged C++ class, and managing to not copy
data back and forth from the managed code, then negligible, if existent.

Like you, we have some computationally intensive numerical algorithms for
which execution speed is a serious consideration. We've simpy created a __gc
class that aggregates a __nogc C++ class, and exposes a similar interface.
This way, the functionality is available to managed clients, but the
computation is done by unmanaged code.
issue/question - It doesn't seem I can have my cake and eat it too,
i.e. __gc is a global class operator, I can't say sometimes I want
this class managed and sometimes I don't.

I'm not sure I understand what it is you're trying to do. The unmanaged
class will still exist. Managed clients would talk to the managed wrapper,
unmanaged clients would talk directly to the unmanaged class.
Probably not bad in general, but using C wrappers makes everything
nice and clean and keeps msfts mitts out of things when performance is
an issue. All the examples I've seen thus far for managed C++ are
actually better arguments for why you shouldn't be using C++ in the
first place, or are simply contrived.

Perhaps something to consider, is that the Managed C++ compiler is (or at
least was - not tested VC7.1) better at producing optimised MSIL code. But
yes, managed C++ is an awful language, but often a necessary evil.

Post back if you have any further queries on the subject. Can't guarantee an
answer, but I'll happily share what I know.

Cheers,

4Space
 
Like you, we have some computationally intensive numerical algorithms for
which execution speed is a serious consideration. We've simpy created a __gc
class that aggregates a __nogc C++ class, and exposes a similar interface.
This way, the functionality is available to managed clients, but the
computation is done by unmanaged code.

Hmmm. __gc aggregating a _nogc. Seemes like roughly the same amount
of work as a C wrapper. Granted its probably prettier. Mebbe I'll
look into that.
I'm not sure I understand what it is you're trying to do. The unmanaged
class will still exist. Managed clients would talk to the managed wrapper,
unmanaged clients would talk directly to the unmanaged class.

Didn't know of your approach. I guess if I had __nogc CShape being
aggregated by __gc CShapeGC class, I could have external clients
instantiate the _gc and internal libraries use the CShape class that
would work. On the other hand, calls between C# and C++ aren't
exactly high performance beasts. Usually end up with the shared
classes mirrored on both sides, so that C# can _do_ instead of _call_
when that would be quicker. Which then leads to the question - if
there's a heavyweight C# class and a heavyweight C++ class, isn't the
_gc class in the middle pretty transient, i.e. it really only exists
to map the two classes together ? Seems C function wrappers are still
a cleaner solution with all things considered, albeit uglier.

An extreme in our world is 2,3, and 4 d points, 4space :-). Those are
basically duplicated on both sides, C# can compute a dot product a lot
faster than go through all the trouble to get the C++ library to do
it.
The other extreme would be a CScene class instance. Certainly some
applicability there, other than minor nits noted above. In the middle
are things like geometry and polygon classes, where some functions get
reimplemented in C# and some are just calls into C++. I'd be curious
if you had similar situations, and how you mapped _gc and _nogc to
issues of efficiency.
Perhaps something to consider, is that the Managed C++ compiler is (or at
least was - not tested VC7.1) better at producing optimised MSIL code. But
yes, managed C++ is an awful language, but often a necessary evil.

Yeah, but thats my point. If I want MSIL code I use C#, unless you're
telling me C# can't optimize as well as C++. I use C++ because I
really really care, and I don't want Pascal.
 
Yeah, but thats my point. If I want MSIL code I use C#, unless you're
telling me C# can't optimize as well as C++. I use C++ because I
really really care, and I don't want Pascal.

I spoke to Stann Lippman about it just over a year ago at a conference here
in the UK. I think at the time, he was working on the managed C++ compiler
team, and his viewpoint was that although in theory, the same application
coded in C# or managed C++ should yield the same MSIL, the truth of it was
that the managed C++ compiler produced MSIL with better optimisation.

But like I said, I haven't benchmarked it using the VS2003. Come to think of
it, I'm not sure that it isn't still illegal to publish benchmark results on
..NET ;¬)

Cheers,

4Space
 
Well, Stan's likely to say that, he's many years in the C++ saddle. I
don't know if I'd agree or wonder on principle, on the one hand the
C++ code may provide additional hintage, on the other hand managed C++
probably isn't too different from C# in the parse tree, ignoring a few
of the newer instructions.

Perhaps I'll profile some of this at some point, it's a public product
now.

Thx for the info!

MMM
 
Many thanks to those who responded.

It seems that you cannot export into C# classes of static
C++ methods in the way the C# documentation suggests
(Consuming Unmanaged DLLs). But, as the respondents
suggest, you can wrap the methods as global C functions
and export these using syntax such as :

extern "C"
{
_declspec(dllexport) int _cdecl func();
// ...
}
 
Back
Top