I could use help with how to properly import several functions from a C++ DLL into my C# application. Here are a couple examples from the C++ side that show what I am trying to
It depends what this DLL is besides written in C++.
If it is a C++ .NET DLL, you can use it like any other .NET DLL. And like the ones you already use provided by the Framework.
If it was written with .NET's predecessor COM in mind, you can use COM interop. Backwards compatibiltiy was thought about when making .NET
If it is neither of those, there is P/Invoke.
Note that COM interop and P/Invoke usually involve handling naked pointers. That means binarity issues and having to go into Unmanaged Code. I do not envy you for having to go that low level language.
As you have stated in comments (subsequently deleted), you can't be sure whether the problem lies in the interop or the parameters passed to the function. How are you going to resolve that doubt?
The way to do that is to create a test bed DLL that has functions with the same signatures, and then prove that you can move data correctly between that DLL and your C# p/invoke code. Once you can do that you can remove interop as a potential source of your problem, and concentrate on the parameters passed to the function. So, here is what is needed to make that test bed DLL.
dllmain.cpp
#include <Windows.h>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Dll1.cpp
#include <iostream>
extern "C"
{
unsigned long __stdcall mfcsez_initialisation(unsigned short serial)
{
std::cout << "mfcsez_initialisation, " << serial << std::endl;
return 1;
}
unsigned char __stdcall mfcs_get_serial(unsigned long int handle,
unsigned short * serial)
{
std::cout << "mfcs_get_serial, " << handle << std::endl;
*serial = 2;
return 3;
}
unsigned char __stdcall mfcs_read_chan(unsigned long int handle,
unsigned char canal,
float * pressure,
unsigned short * chrono)
{
std::cout << "mfcs_read_chan, " << handle << ", " << static_cast<int>(canal) << std::endl;
*pressure = 4.5f;
*chrono = 5;
return 6;
}
}
Dll1.def
LIBRARY Dll1
EXPORTS
mfcsez_initialisation
mfcs_get_serial
mfcs_read_chan
Note that I am using a .def file to ensure that functions are exported using their undecorated names.
The C# program that calls this looks like so:
Program1.cs
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApp1
{
class Program
{
const string dllname = "Dll1.dll";
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
static extern uint mfcsez_initialisation(ushort serial);
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
static extern byte mfcs_get_serial(uint handle, out ushort serial);
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
static extern byte mfcs_read_chan(uint handle, byte canal, out float pressure, out ushort chrono);
static void Main(string[] args)
{
uint retval1 = mfcsez_initialisation(11);
Console.WriteLine("return value = " + retval1.ToString());
Console.WriteLine();
ushort serial;
byte retval2 = mfcs_get_serial(12, out serial);
Console.WriteLine("serial = " + serial.ToString());
Console.WriteLine("return value = " + retval2.ToString());
Console.WriteLine();
float pressure;
ushort chrono;
byte retval3 = mfcs_read_chan(13, 14, out pressure, out chrono);
Console.WriteLine("pressure = " + pressure.ToString());
Console.WriteLine("chrono = " + chrono.ToString());
Console.WriteLine("return value = " + retval3.ToString());
Console.ReadLine();
}
}
}
The output is:
mfcsez_initialisation, 11 return value = 1 mfcs_get_serial, 12 serial = 2 return value = 3 mfcs_read_chan, 13, 14 pressure = 4.5 chrono = 5 return value = 6
As you can see, all the desired values travel between the two modules correctly. This demonstrates that the p/invoke interop code here is correct.
Notes:
int
and long int
are 32 bit types. They therefore map to int
on C#, or
uint` for unsigned variants.long
and ulong
are 64 bit types, and so do not match C++ int
or long int`.unsigned char
on C++ to byte
on C#.unsafe
if you needed to use pointers, but you don't, and seldom do.out
rather than ref
because I infer that these parameters are only used for data flowing out of the DLL.If you use this interop code against your DLL and still encounter failure then there are two plausible explanations: