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