I went through the tutorial of C# COM INterop. Performed the demo example given in the link successfully runs! Understood every statement present there, their meaning.
And came across the equivalent data types for c# objects.
I Couldn't find any good books (with examples on C# COM interop and calling COM components from native C++) apart from Adam Nathan's .NET and COM interoperability and Nishant Shivakumar's CCLI in Action. I have no problems in registering assembly and other stuff but facing problems in COM syntax.
Before explaining my code, I wanted to understand what VARIANT is. Why is it there?
This is my situation, I have a C# class as below:
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
namespace ManagedDLL
{
// Interface declaration.
[ComVisible(true), Guid("3265C537-E149-4559-B4E1-DBE334927DFA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICalculator
{
int Add(int Number1, int Number2);
int Subtract(Subtraction sb);
};
// Interface implementation.
[ComVisible(true), Guid("0F50D3BE-CEEA-4C57-9882-4A609C7BB36C")]
public class ManagedClass : ICalculator
{
private int a, b;
public int Add(int Number1, int Number2)
{
return Number1 + Number2;
}
public int Subtract(Subtraction sub)
{
int a = 10, b = 3;
return sub.SubtractTwoNumbers(a, b);
}
}
[ComVisible(true)]
[Guid("C718EDDE-541C-4D15-B7EA-0533AB11A839")]
[ClassInterface(ClassInterfaceType.None)]
public class Subtraction
{
public int SubtractTwoNumbers(int a, int b)
{
return a - b;
}
}
}
Got the .tlh file after importing tlb file as below:
#pragma once
#pragma pack(push, 8)
#include <comdef.h>
namespace ManagedDLL {
//
// Forward references and typedefs
//
struct __declspec(uuid("4e5098b7-4e51-45e5-a705-a7e3c51e2a80"))
/* LIBID */ __ManagedDLL;
struct __declspec(uuid("3265c537-e149-4559-b4e1-dbe334927dfa"))
/* interface */ ICalculator;
struct /* coclass */ ManagedClass;
struct /* coclass */ Subtraction;
struct __declspec(uuid("c8e9181c-f064-3ec1-869e-042c6fdd3e46"))
/* dual interface */ _ManagedClass;
//
// Smart pointer typedef declarations
//
_COM_SMARTPTR_TYPEDEF(ICalculator, __uuidof(ICalculator));
_COM_SMARTPTR_TYPEDEF(_ManagedClass, __uuidof(_ManagedClass));
//
// Type library items
//
struct __declspec(uuid("3265c537-e149-4559-b4e1-dbe334927dfa"))
ICalculator : IUnknown
{
//
// Raw methods provided by interface
//
virtual HRESULT __stdcall Add (
/*[in]*/ long Number1,
/*[in]*/ long Number2,
/*[out,retval]*/ long * pRetVal ) = 0;
virtual HRESULT __stdcall Subtract (
/*[in]*/ struct _Object * sb,
/*[out,retval]*/ long * pRetVal ) = 0;
};
struct __declspec(uuid("0f50d3be-ceea-4c57-9882-4a609c7bb36c"))
ManagedClass;
// [ default ] interface _ManagedClass
// interface _Object
// interface ICalculator
struct __declspec(uuid("c718edde-541c-4d15-b7ea-0533ab11a839"))
Subtraction;
// [ default ] interface _Object
struct __declspec(uuid("c8e9181c-f064-3ec1-869e-042c6fdd3e46"))
_ManagedClass : IDispatch
{};
//
// Named GUID constants initializations
//
extern "C" const GUID __declspec(selectany) LIBID_ManagedDLL =
{0x4e5098b7,0x4e51,0x45e5,{0xa7,0x05,0xa7,0xe3,0xc5,0x1e,0x2a,0x80}};
extern "C" const GUID __declspec(selectany) IID_ICalculator =
{0x3265c537,0xe149,0x4559,{0xb4,0xe1,0xdb,0xe3,0x34,0x92,0x7d,0xfa}};
extern "C" const GUID __declspec(selectany) CLSID_ManagedClass =
{0x0f50d3be,0xceea,0x4c57,{0x98,0x82,0x4a,0x60,0x9c,0x7b,0xb3,0x6c}};
extern "C" const GUID __declspec(selectany) CLSID_Subtraction =
{0xc718edde,0x541c,0x4d15,{0xb7,0xea,0x05,0x33,0xab,0x11,0xa8,0x39}};
extern "C" const GUID __declspec(selectany) IID__ManagedClass =
{0xc8e9181c,0xf064,0x3ec1,{0x86,0x9e,0x04,0x2c,0x6f,0xdd,0x3e,0x46}};
} // namespace ManagedDLL
#pragma pack(pop)
and finally this is my native C++ application:
HRESULT hr = CoInitialize(NULL);
ManagedDLL::ICalculatorPtr ptrInterface(__uuidof(ManagedDLL::ManagedClass));
IUnknown* pUnk=NULL;
hr=CoCreateInstance(__uuidof(ManagedDLL::Subtraction), NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)pUnk);
long lResult;
ptrInterface->Subtract(pUnk, &lResult); //what should be written instead of pUnk, because it expects a _Object* as evident in the tlh file
return lResult;
Questions galore:
How to access the Subtract method in the interface? How should we instantiate Subtraction class whose object should be passed as parameter in this Subtract method?
How to collect value of a function returning a COM class as return type?
What are IUnknown and IDispatch?
And most importantly, why the _Object* was created as an argument in the COM .tlh file instead of Subtraction* as argument?
The questions you are asking are classical, fundamental questions about COM that could take a small book to answer. I would suggest you get a tutorial or a book on COM that will bring you up to speed. You won't understand the interop until you understand COM basics. Start there, then work through the harder stuff.
It takes some serious time to get up to speed on all the issues involved, so don't get in a hurry!
Here's a place to start:
http://msdn.microsoft.com/en-us/library/727z646z(v=vs.80).aspx
Related
I'm currently working on a .NET Framework 4.7.2 application using a business logic library written in unmanaged C++. I need to use unmanaged C++.
I need to use the logic from the C++ project, unfortunately I cannot correctly convert the input or output parameters of my program.
When I input 42, and simply want to return that value, I get 17582022 as a result. Which should actually be 42.
My C++ code looks like that:
MYCore header file:
#ifdef MYCORE_EXPORTS
#define MYCORE_API __declspec(dllexport)
#endif
#pragma once
#include <string>
using namespace std;
extern "C"
{
class MYCORE_API TestClass
{
private:
string name;
public:
TestClass(char*);
long Iterate(long &n);
};
MYCORE_API TestClass* TestClass_Create(char* name);
}
MYCore source file:
#include "stdafx.h"
#include "MYCore.h"
TestClass::TestClass(char* n)
{
name = n;
}
long TestClass::Iterate(long &n)
{
return n;
}
extern "C"
{
MYCORE_API TestClass * TestClass_Create(char* name)
{
return new TestClass(name);
}
}
I'm using a .NET 4.7.2 Framework Interface project to export the C++ library functionality:
namespace MYCore.Interface
{
public static class MYProxy
{
private const string coreDLL = "my.core.dll";
[DllImport(coreDLL, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TestClass_Create(string name);
[DllImport(coreDLL, EntryPoint = "?Iterate#TestClass##XXXXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int Iterate(int n);
}
}
In my actual application I further import the dll and use the logic like that:
public static void Initialize()
{
var test = MYProxy.WrapperIterator_Create("test");
var result = MYProxy.Iterate(42); // as a result I'm getting sth. like 17582022 instead of 42
}
Do you know how to correctly convert an int input from C# to C++ and vice versa?
Thank you!
What you're doing in C# does not work in C++ either:
auto result = Iterate(42l);
results in the compiler error
Cannot convert argument 1 from 'long' to 'long &'
I see two solutions:
a) Change the C++ code
long TestClass::Iterate(long n)
(without the reference)
b) Change the C# code
static extern int Iterate(ref int n);
(pass a reference) and call it like
int n = 42;
Console.WriteLine(Iterate(ref n));
The problem is actually called "Marshal an unmanaged C++ Class to C#".
In my Proxy class I created a method to call an actual instance method:
[DllImport(coreDLL, EntryPoint = "?Iterate#TestClass##XXX#X", CallingConvention = CallingConvention.ThisCall)]
public static extern int CallIterate(IntPtr instance, int n);
and the method in my C++ looks like that:
MYCORE_API int CallIterate(TestClass * instance, int n)
{
if (instance!= NULL)
{
return instance->Iterate(n);
}
}
For further reading on how to marshal unmanaged C++ classes, I can suggest the following article:
https://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class
My solution works fine now. Thanks for all the good input!
I have an unmanaged DLL that exports only a C style factory method that returns a new instance of a class (simplified here to look simple).
hello.h
#if defined(HWLIBRARY_EXPORT) // inside DLL
# define HWAPI __declspec(dllexport)
#else // outside DLL
# define HWAPI __declspec(dllimport)
#endif
struct HelloWorld{
public:
virtual void sayHello() = 0;
virtual void release() = 0;
};
extern "C" HWAPI HelloWorld* GetHW();
hello.cpp
#include "hello.h"
struct HelloWorldImpl : HelloWorld
{
void sayHello(){
int triv;
std::cout<<"Hello World!";
std::cin>>triv;
};
void release(){
this->HelloWorldImpl::~HelloWorldImpl();
};
HelloWorld* GetHW(){
HelloWorld* ptr = new HelloWorldImpl();
return ptr;
};
Now, I can use dllimport to access GetHW() but is there a way to access the member functions of the returned 'struct'... ie, sayHello and release?
I was also stuck with the same problem. This question was asked a while before. I commented to it for any better solution but didn't get any reply yet. So, reposting it.
When i googled, able to find out two solutions.
Solution1: Expose all the member functions in the C-style for the existing dll. Which i cant do, as it is a 3rd party dll.
Solution2: Write a managed C++ dll exposing the functionality of native C++ dll, which later can be used in your C# dll. Here many classes/functions are present. So, creating would take most of the time.
i got the above solutions from the link below.
How To Marshall
Please let me know if there is any better solution other than the above two solutions?
i have the source code for C++ solution. But what i though was not to touch C++ dll. If there is any possibility to do it in C#, it would be great.
If there is no alternative, i need to follow any one of the specified two solutions.
The C++ code is using the way abstract classes are implemented by the Visual C++ compiler. http://blogs.msdn.com/b/oldnewthing/archive/2004/02/05/68017.aspx. This memory layout is "fixed" because it is used for implementing COM interfaces. The first member of the struct in memory will be a pointer to a vtable containing the function pointers of your methods. So for a
struct HelloWorldImpl : public HelloWorld
{
public:
int value1;
int value2;
}
the "real" layout in memory would be:
struct HelloWorldImpl
{
HelloWorldVtbl *vtbl;
int value1;
int value2;
}
where vtbl would be:
struct HelloWorldVtbl
{
void *sayHello;
void *release;
}
Just for the sake of doing a complete response, I'm writing the example for this signatures:
struct HelloWorld {
public:
virtual int sayHello(int v1, int v2, int v3) = 0;
virtual void release() = 0;
};
C# code:
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetHW();
[StructLayout(LayoutKind.Sequential)]
struct HelloWorldVtbl
{
public IntPtr sayHello;
public IntPtr release;
}
Your functions are void Func(void) or int Func(int, int, int), but in truth they have a hidden parameter, this, so you can write them as:
int sayHello(HelloWorld*, int, int, int);
void release(HelloWorld*);
so in C# the delegate is
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int Int32MethodInt32Int32Int32(IntPtr ptr, int v1, int v2, int v3);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void VoidMethodVoid(IntPtr ptr);
Then you can use
IntPtr ptr = GetHW();
IntPtr vtbl = Marshal.ReadIntPtr(ptr, 0);
HelloWorldVtblhw = (HelloWorldVtbl)Marshal.PtrToStructure(vtbl, typeof(HelloWorldVtbl));
Int32MethodInt32Int32Int32 sayHello = (Int32MethodInt32Int32Int32)Marshal.GetDelegateForFunctionPointer(hw.sayHello, typeof(Int32MethodInt32Int32Int32));
int res = sayHello(ptr, 1, 2, 3);
Console.WriteLine(res);
VoidMethodVoid release = (VoidMethodVoid)Marshal.GetDelegateForFunctionPointer(hw.release, typeof(VoidMethodVoid));
release(ptr);
int Set(CANMsg &CANObj)
I have to call the method above, from c#. Untill now i have defined a wrapper :
extern "C" __declspec(dllexport) int SetWrapper(CANMsg CANObj);
CANMsg CANObj --- is this parameter ok or should i use CANMsg *CANObj ?
and here i implement the wrapper:
extern "C" __declspec(dllexport) int SetWrapper(CANMsg CANObj)
{
return Set(CANObj);
}
I am creating this wrapper because this is an overloaded version of the function and i had to make a difference somehow.
Here is the CANMsg class:
class CANMsg
{
public:
CANMsg();
~CANMsg();
void AddRef() const;
void Release() const;
unsigned int MsgId;
unsigned int DLC;
unsigned int Handle;
unsigned int Interval;
unsigned int TimeStamp;
unsigned char Data0;
unsigned char Data1;
unsigned char Data2;
unsigned char Data3;
unsigned char Data4;
unsigned char Data5;
unsigned char Data6;
unsigned char Data7;
protected:
mutable int refCount;
};
Now, in C# i have the following :
[StructLayout(LayoutKind.Sequential)]
public class CANmsg
{
public int MsgId;
public int DLC;
public int Handle;
public int Interval;
public int TimeStamp;
public char Data0;
public char Data1;
public char Data2;
public char Data3;
public char Data4;
public char Data5;
public char Data6;
public char Data7;
}
and the import is like this :
[DllImport("engine.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I4)]
public static extern int SetWrapper(IntPtr canMSGObject);
I am a bit confused about that CANMsg object, am I declaring it ok as an IntPtr, is the marshal ok, or the types ? If i let it like so, with the IntPtr, what kind of instantiation should i perform there? If i send a CANMsg object, i get an error regarding some invalid arguments.
Let me know if you need some more details about this.
When I see your C++ class definition, I ask myself "what happens in the constructor and the destructor?" and "what do AddRef() and Release() do?" These are important questions because you can't simply project data from a C# object onto that IntPtr and hope for the best. Instead, you should think about making a helper dll that does this work for you. You might need methods something like this:
public ref class MyLibraryHelper {
public:
IntPtr MakeCANMsg() { return gcnew IntPtr(new CANMsg()); }
void DestroyCANMsg(IntPtr msgPtr) {
CANMsg *msg = reinterpret_cast<CANMsg *>(msgPtr.ToPointer());
if (msg) delete msg;
}
void ProjectTo(CSharpCANMsg ^csh, IntPtr msgPtr)
{
CANMsg *msg = reinterpret_cast<CANMsg *>(msgPtr.ToPointer());
if (!msg) return;
msg->MsgId = csh->get_MsgId();
// etc
}
void ProjectFrom(IntPtr msgPtr, CSharpCANMsg ^csh)
{
CANMsg *msg = reinterpret_cast<CANMsg *>(msgPtr.ToPointer());
if (!msg) return;
csh->set_MsgId(msg->MsgId);
// etc
}
}
My C++/CLI is rusty, so expect some issues. If this looks like hand-marshalling, well, it is because given the class that you've exposed, it seems like you need it.
Now honestly, you probably don't want this. Really, you want a C++/CLI class that constructs a CANMsg and keeps it as a private member and then maps .NET properties onto the lower level object. This type of class will have to be disposable and the !ClassName() destructor will be responsible for deleting the underlying object.
You can not pass a C# object to native C++ like this. Marshal.StructureToPtr is what you need, the details and examples are here
I've worked on a C# version of a C++ API but I can't manage to get it right.
Since the whole API was too big the first time I posted, I've cut it down to some core functions for which I really need some help to make a managed version of.
So basically, this is a C++ dll containing exported functions that are used to communicate with a technical analysis software.
C++ function I'd like to convert
#define PLUGINAPI extern "C" __declspec(dllexport)
PLUGINAPI int GetFunctionTable( FunctionTag **ppFunctionTable )
{
*ppFunctionTable = gFunctionTable;
// must return the number of functions in the table
return gFunctionTableSize;
}
GetFunctionTable is called by the software which provides a pointer to an array of FunctionTag called gFunctionTable:
typedef struct FunctionTag
{
char *Name;
FunDesc Descript;
} FunctionTag;
FunctionTag gFunctionTable[] = {"ExampleA",{ VExampleA, 0, 0, 0, 0, NULL },
"ExampleB",{ VExampleB, 1, 0, 1, 0, NULL }
};
FunctionTag structure contains an embedded structure called Fundesc:
// FunDesc structure holds the pointer to actual
// user-defined function that can be called by AmiBroker.
typedef struct FunDesc
{
AmiVar (*Function)( int NumArgs, AmiVar *ArgsTable );
UBYTE ArrayQty; // number of Array arguments required
UBYTE StringQty; // number of String arguments required
SBYTE FloatQty; // number of float args
UBYTE DefaultQty; // number of default float args
float *DefaultValues; // the pointer to defaults table
} FunDesc;
Finally, Fundesc contains AmiVar type:
#pragma pack( push, 2 )
typedef struct AmiVar
{
int type;
union
{
float val;
float *array;
char *string;
void *disp;
};
} AmiVar;
#pragma pack(pop)
C# conversion so far
Now, this is what I've written so far in an attempt to get my C# dll to "mimic" the C++ original API. The GetFunctionTable() exported function:
namespace AmiBrokerFrontDll
{
internal static class AmiBrokerFrontDll
{
[DllExport("GetFunctionTable", CallingConvention = CallingConvention.Cdecl)]
public static Int32 GetFunctionTable(ref FunctionTag[] ppFunctionTable)
{
FillFunction();
ppFunctionTable=gFunctionTable;
return gFunctionTableSize;
}
Then comes the definition of FunctionTag structure and gFunctionTableSize:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct FunctionTag
{
[MarshalAs(UnmanagedType.LPStr)]
public string Name;
public FunDesc Description;
}
public static FunctionTag[] gFunctionTable=new FunctionTag[1];
public static FunctionTag gfunc;
static Int32 gFunctionTableSize = Marshal.SizeOf(gFunctionTable) / Marshal.SizeOf(gfunc);
public static void FillFunction()
{
gFunctionTable[0].Name = "VExempleA";
gFunctionTable[0].Description.Function += VExempleDeMacd;
//ArrayQty, StringQty, FloatQty, DefaultQty, DefaultTablePtr
gFunctionTable[0].Description.ArrayQty = 0;
gFunctionTable[0].Description.StringQty = 0;
gFunctionTable[0].Description.FloatQty = 2;
gFunctionTable[0].Description.DefaultQty = 0;
gFunctionTable[0].Description.DefaultValues = new IntPtr();
}
FunDesc declaration includes a delegate:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate AmiVar FunctionDelegate(int NumArgs, ref AmiVar ArgsTable);
public struct FunDesc
{
[MarshalAs(UnmanagedType.FunctionPtr)]
public FunctionDelegate Function;
public byte ArrayQty; // The number of Array arguments required
public byte StringQty; // The number of String arguments required
public byte FloatQty; // The number of float args
public byte DefaultQty; // The number of default float args
public IntPtr DefaultValues; // The pointer to defaults table
}
Finally, we have an AmiVar structure:
[StructLayoutAttribute(LayoutKind.Explicit, Size = 8)]
public struct AmiVar
{
[FieldOffset(0)]
public Int32 type;
[FieldOffset(4)]
public Single val;
[FieldOffset(4)]
public IntPtr array;
[FieldOffset(4)]
[MarshalAs(UnmanagedType.LPStr)]
public string name;
[FieldOffset(4)]
public IntPtr disp;
}
Sorry this is too long. Unfortunately, I couldn't make a small consise question.
So this code compiled (maybe not anymore since this is an extract from the bigger picture) but when loading the resulting dll from the technical analysis software, I received an ACCESS VIOLATION ERROR. I believe this means the C# conversion doesn't map the C++ variables size correctly. With the arrays of structures and delegates this project has become too difficult for me to solve alone.
Any help will be much appreciated!
Thanks,
Guillaume
I can't help in your specific case, but I can tell you a couple things that will make your life easier:
Function pointers created from managed delegates should never, ever, ever, be stored in unmanaged code. I don't say this lightly. There are claims that if you create a function pointer from a delegate with GetFunctionPointerForDelegate, that the appropriate thunks will get created and won't ever get garbage collected. This is NOT true. I have watched function pointers that were good on one call go sour on the next. The safest bet is to guarantee that function pointers will never get stored beyond the use of an unmanaged call.
P/Invoke is OK for some tasks, but by far the easiest way to integrate a non-C#-friendly C++ library is to make a better/more appropriate wrapper for it in C++/CLI. See here for a description of one way to approach the problem.
I suppose I have to create a managed C++ code to wrap the native C++. But I have the problem while trying to wrap an array used in function parameter whose type is defined in native C++. The native C++ code is as follows:
//unmanageCPP.h
class __declspec(dllexport) unmanageMoney
{
public:
unmanageMoney(int a, int b) { rmb = a; dollar = b; }
unmanageMoney() { rmb = 0; dollar = 0; }
int rmb;
int dollar;
};
class __declspec(dllexport) unmanageSum
{
public:
//how to wrap this funciton?
int addDollar(unmanageMoney a[], unmanageMoney b[]);
};
//unmanageCPP.cpp
#include "unmanaged.h"
int unmanageSum::adddollar(unmanageMoney a[], unmanageMoney b[])
{
return a[0].dollar + b[0].dollar;
}
Could anyone tell me how to write the manageCPP.h? Thanks very much!
Update
I compose the manageCPP.h as follows, but I don't know how to write addDollar()
//first, I wrap the class unmanageMoney for use in manageSum::addDollar()
public ref class manageMoney
{
private:
unmanageMoney* mMoney;
public:
unmanageMoney getMoney()
{
return *mMoney;
}
manageMoney(int a, int b) { mMoney = new unmanageMoney(a, b); }
~manageMoney() { delete mMoney; }
};
public ref class manageSum
{
// TODO: Add your methods for this class here.
private:
unmanageSum *mSum;
public:
manageSum()
{
mSum = new unmanageSum();
}
~manageSum()
{
delete mSum;
}
//it must be wrong if I code like this, for unmanageSun::adddollar() only
// receives unmanageMoney as arguments. So what should I do?
int adddollar(manageMoney a[], manageMoney b[])
{
return mSum->adddollar(a, b);
}
};
You create a C++/CLI source file with
public ref class SomethingOrOther
{
//...
};
and set the compile options to use the /clr option.
Beyond that, it's almost the same as writing native C++. You'll #include the header file for the class you want to reuse, create instances and call their member functions, just the same as normal C++. But anything inside that ref class will be visible to C#.
And you do NOT put __declspec(dllexport) on the class. Not ever. It's useful for functions, but creates misery when used with classes.