Calling C DLL from C#
I found the reason for my failed attempts by utilising a tool called;
Microsoft(R) P/Invoke Interop Assistant as suggested by an answer on this thread.
I utilised this tool to input some of the C function prototypes and get it to generate the required C# prototype on my behalf. The C prototype looked like the following;
long __stdcall TransProjPt(LPSTR psGridFile, long lDirection, double dEasting, double
dNorthing, long lZone, double* pdEastNew, double* pdNorthNew, double* pdEastAcc,
double* pdNorthAcc)
When entering this into the Interop assistant tool, it showed that rather than using longs (as I had done in my original question), these should be declared as an int. It produced the following output that meant my code above now worked as I'd hoped. Yay.
/// Return Type: int
///psGridFile: LPSTR->CHAR*
///lDirection: int
///dEasting: double
///dNorthing: double
///lZone: int
///pdEastNew: double*
///pdNorthNew: double*
///pdEastAcc: double*
///pdNorthAcc: double*
[System.Runtime.InteropServices.DllImportAttribute("<Unknown>", EntryPoint="TransProjPt", CallingConvention=System.Runtime.InteropServices.CallingConvention.StdCall)]
public static extern int TransProjPt([System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] System.Text.StringBuilder psGridFile, int lDirection, double dEasting, double dNorthing, int lZone, ref double pdEastNew, ref double pdNorthNew, ref double pdEastAcc, ref double pdNorthAcc) ;
Thanks for everyones help with this.
How to call C++ DLL in C#
The following code in VS 2012 worked fine:
#include <Windows.h>
extern "C"
{
__declspec(dllexport) void HelloWorld ()
{
MessageBox (0, L"Hello World from DLL!\n", L"Hi",MB_ICONINFORMATION);
}
__declspec(dllexport) void ShowMe()
{
MessageBox (0, L"How are u?", L"Hi", MB_ICONINFORMATION);
}
}
NOTE: If I remove the extern "C"
I get exception.
Using C dll in C#
You can use PInvoke
Platform Invocation Services (PInvoke)
allows managed code to call unmanaged
functions that are implemented in a
DLL.
Here is a great tutorial by the NAG (Numerical Algorithms Group) group
Call C dll function from C#
You have a number of issues with your PInvoke declarations
- You need to make sure to free your HGlobal memory, otherwise it will leak.
- The strings are declared as fixed size
char
arrays in C, so need to beCharSet.Ansi
... - ... and they need to be declared
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 127)]
so they have fixed nested size. - The parameters to the function need to be
ref
, possibly with[In]
attribute also. - You need
CallingConvention = CallingConvention.Cdecl
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CredPair
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 127)]
public string usr;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 127)]
public string pas;
}
public enum CredType
{
PAIR,
KEY
}
public enum EndpointType
{
DIRECT
}
public struct CredData
{
public CredType credType;
public IntPtr credVal;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct EndpointDirect
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 127)]
public string url;
}
public struct EndpointData
{
public EndpointType endpointType;
public IntPtr endpointVal;
}
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern MyErrCode CheckUser([In] ref CredData cred_Data, [In] ref EndpointData endpoint_data);
IntPtr pnt;
IntPtr urlptr;
try
{
CredPair objPair = new CredPair
{
usr = "abc@xyz.com"
pas = "admin@1234",
};
pnt = Marshal.AllocHGlobal(Marshal.SizeOf(objPair));
Marshal.StructureToPtr(objPair, pnt, false);
CredData objCredData = new CredData
{
credType = CredType.PAIR,
credVal = pnt,
};
endpointValue = new EndpointDirect
{
url = "example.com",
};
urlptr = Marshal.AllocHGlobal(Marshal.SizeOf(epd));
Marshal.StructureToPtr(epd, urlptr, false);
EndpointData objData = new EndpointData
{
endpointType = EndpointType.DIRECT,
endpointValue = urlptr,
};
error_code = CheckUser(ref objCredData, ref objData);
}
finally
{
Marshal.FreeHGlobal(pnt);
Marshal.FreeHGlobal(urlptr);
}
Call c++ DLL from C# application
Thanks for the answers.
I resolved the issue by making one extra class(Wrapper class) that contains the managed code. This wrapper class is called by the c# classes in the same way as I mentioned in the question. This wrapper class than call the c++ class and return the result to the UI.
Calling C dll from C#, return types slightly different
Pointers in an unmanaged language do not map to managed arrays as you have done, this is why it is complaining. char
is (almost always, with very limited exceptions) an 8 bit value that maps well to byte
in C# as you've noticed, but you need to make them pointers in the managed struct as well:
unsafe struct Envelope
{
public byte* Payload;
public byte* Signature;
}
Related Topics
Displaying Standard Datatables in MVC
Azure Asp .Net Webapp the Request Timed Out
How to Use ASP.NET Identity 2.0 to Allow a User to Impersonate Another User
ASP.NET Identity Get All Roles of Logged in User
Automatic Native and Managed Dlls Extracting from Nuget Package
Getting Absolute Urls Using ASP.NET Core
Does a Locked Object Stay Locked If an Exception Occurs Inside It
Method Overloading VS Optional Parameter in C# 4.0
Using the C# Dispatcher in Wpf Applications
Can Bindings Create Memory Leaks in Wpf
Why Does Wcf Return Myobject[] Instead of List<T> Like I Was Expecting
How to Pass a Username/Password in the Header to a Soap Wcf Service
I Need a Event to Detect Internet Connect/Disconnect
Why Does C# Limit the Set of Types That Can Be Declared as Const
How to Create an Instance of an Arbitrary Array Type at Runtime