"Andrew Cumming" <am********@big pond.com> wrote in message news:<06******* *************** ******@phx.gbl> ...
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(c har* theMessage);
CSEXPORT(void) StartCore(ULONG windowHandle,in t serviceID);
CSEXPORT(void) StartQuestor(ch ar* appname);
CSEXPORT(void) OpenQuestor(cha r* appname);
CSEXPORT(void) ActivateSubsyst ems();
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) LoadExternalMod el(const char* theFile,short
mirrorAxes,shor t mirrorTexture,s hort 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(aUM FFile);
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(dlle xport) 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("CS2V 3.dll")]
private static extern IntPtr CreateUMF(strin g theName);
[DllImport("CS2V 3.dll")]
private static extern IntPtr OpenUMF(string theName);
[DllImport("CS2V 3.dll")]
private static extern IntPtr ReadUMF(IntPtr umfptr);
[DllImport("CS2V 3.dll")]
private static extern void WriteUMF(IntPtr umfptr,IntPtr objptr);
[DllImport("CS2V 3.dll")]
private static extern void CloseUMF(IntPtr umfptr);
[DllImport("CS2V 3.dll")]
private static extern IntPtr LoadExternalMod el(string theFile,short
mirrorAxes,shor t mirrorTexture,s hort reversals);
#endregion
private IntPtr m_umf;
public V3UMF()
{
m_umf = (IntPtr) 0;
}
public void Open(string filename)
{
m_umf = OpenUMF(filenam e);
}
public void Create(string filename)
{
m_umf = CreateUMF(filen ame);
}
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).Do wnCast();
}
public void WriteObject(V3O bject anything)
{
WriteUMF(m_umf, anything.Handle );
}
static public V3Shape LoadExternalMod el(string theName,bool
mirrorAxes,bool mirrorTexture,b ool 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 = LoadExternalMod el(theName,ma,m t,revs);
if(theShapePtr == (IntPtr) 0)
return null;
else
return new V3Shape(theShap ePtr);
}
}
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