Reputation: 23
First of all - I know how to call unmanaged functions. I'm stuck with interface methods in unmanaged dll. I think, writing wrapper - is what I need.
Please, help me with real code.
I have:
Here is some code in .h file (point of interest - DECLARE INTERFACE):
#ifdef __BUILD_DLL
#define BLNETPASS_EXPORT __declspec(dllexport)
#else
#define BLNETPASS_EXPORT
#endif
#include "BlNetpassUid.h"
extern "C"
{
BLNETPASS_EXPORT HRESULT WINAPI BlNetPassCreateA
(LPCSTR pHostName, const GUID *, VOID **);
BLNETPASS_EXPORT HRESULT WINAPI BlNetPassCreateW
(LPCWSTR pHostName, const GUID *, VOID **);
BLNETPASS_EXPORT HRESULT WINAPI CanBeUnload (DWORD dTimeout );
...
#ifdef UNICODE
#define BlNetPassCreate BlNetPassCreateW
#else
#define BlNetPassCreate BlNetPassCreateA
#endif
#undef INTERFACE
#define INTERFACE INetPass
DECLARE_INTERFACE_ (INetPass, IUnknown)
{
// IUnknown methods
STDMETHOD ( QueryInterface)(REFIID, VOID **) PURE;
STDMETHOD_ (ULONG, AddRef)() PURE;
STDMETHOD_ (ULONG, Release)() PURE;
// INetPass methods
STDMETHOD ( CreateNetPassEnum)(VOID **, REFIID cid,
REFIID iid) PURE;
};
It's not a COM object, i think, i can't use type library import for this dll
What I need:
That's how i did it before (it's PInvoke):
[DllImport("BlNetpassApi")]
public static extern int BlNetPassCreateA(string pHostName, ref Guid GUID, out IntPtr rINetPass);
...
...
IntPtr something;
Guid temp_guid = Guid.Empty;
int temp_int = BlNetPassCreateA(null, ref temp_guid, out something);
Ok! IntPtr contains some data, but what's next - how do I describe the interface that is behind and call its method?
Upvotes: 2
Views: 9449
Reputation: 106
I would create a C# equivalent of the C-interface, with dll-imports for all the corresponding functions. You can even define structs that corresponds to the ones on the C-side. An example of how I would do it:
// This defines a custom datatype present in our dll.
[StructLayout(LayoutKind.Sequential)]
public struct MyDllStruct
{
int aValue;
[MarshalAs(UnmanagedType.Bool)]
bool anotherValue;
}
public static class MyDllInterface
{
// The function in the interface can be customized by instead
// specifying the EntryPoint in the DllImport-attribute.
[DllImport("MyDll.dll", EntryPoint = "mydll_function")]
public static extern int MyDllFunction(
int param1,
// We want this to become a C-style boolean
// (false == 0, true != 0)
[MarshalAs(UnmanagedType.Bool)]
bool param2,
// We want the dll to write to our struct, and to be
// able to get the data written to it back we need to
// specify the Out-attribute. If we were just feeding the
// parameter to the dll we could just use the In-attribute
// instead, or combine them if we want to achieve both.
[Out, MarshalAs(UnmanagedType.LPStruct)]
MyDllStruct resultStruct
);
[DllImport("MyDll.dll")]
// Sometime we need to do a little magic on our own so we let
// this function return an IntPtr, and not the struct.
public static extern IntPtr get_a_pointer_struct();
// By not defining the EntryPoint-parameter the application
// expects that the dll function is named just as the
// C#-function declaration.
[DllImport("MyDll.dll")]
// Here we specify that we are expecting a char-array
// back from the function, so the application should
// marshal it as such.
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string mydll_get_errormsg();
}
// This class "converts" the C-style functions
// into something more similar to the common
// way C# is usually written.
public class MyDllWrapper
{
// Calls the MyDllInterface.MyDllFunction and returns
// the resulting struct. If an error occured, an
// exception is thrown.
public MyDllStruct CallFunction(int param1, bool param2)
{
MyDllStruct result = new MyDllStruct();
int errorCode = MyDllInterface.MyDllFunction(
param1, param2, result);
if (errorCode < 0)
throw new Exception(String.Format(
"We got an error with code {0}. Message: ",
errorCode, MyDllInterface.mydll_get_errormsg()));
return result;
}
// Gets a pointer to a struct, and converts it
// into a structure.
public MyDllStruct GetStruct()
{
IntPtr structPtr = MyDllInterface.get_a_pointer_struct();
return (MyDllStruct)Marshal.PtrToStructure(
structPtr, typeof(MyDllStruct));
}
}
The above code probably has a bunch of errors in it and the marshaling is most likely all wrong. The purpose was mainly to display a pattern for how I usually implement C-interfaces in C#.
EDIT: I am not really sure how your C-code works, but looking at the header I would say that the IntPtr that you got from the call to BlNetPassCreateA is your this-pointer in subsequent calls to the interface. Calling CreateNetPassEnum would probably be done in this way:
[DllImport("BlNetPassApi")]
public static extern void CreateNetPassEnum(IntPtr this, int cid, int reffid);
...
IntPtr something;
Guid temp_guid = Guid.Empty;
int temp_int = BlNetPassCreateA(null, ref temp_guid, out something);
CreateNetPassEnum(in something, 10, 10);
I don't really know what type the REFIID are, our how you get the appropriate values for them, but I hope that this gets you started on the solution at least.
Upvotes: 1
Reputation: 4157
You should write a managed C++ wrapper to call c++ functions and then use in your .Net project. Because there is no normal way to call functions on unmanaged object instances from .Net.
Upvotes: 1