Importing C++ dll function in C# - c#

I would like to call a function inside MediaInfo.dll.
The function is:
const wchar_t* __stdcall MediaInfo_Option (void* Handle, const
wchar_t* Option, const wchar_t* Value);
I have declared it in the c# code in this way:
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)] internal
static extern string MediaInfo_Option(IntPtr Handle, string OptionString, string Value);
here is the code in which it's called:
MediaInfo.MediaInfo_Open(this.h, path);
MediaInfo.MediaInfo_Option(this.Handle, "Complete", "1");
myTextBox.Text = MediaInfo.MediaInfo_Inform(this.h, 0);
MediaInfo.MediaInfo_Close(this.h);
The problem is that any c# application compiled with .NET Framework equal or greater than the version 4 crash when calling that function.
How can I solve it?
Thanks in advance.

You did not Marshall the wchar_t* of both input and output, you'll get random errors.
There is a C# binding of MediaInfo which takes care of all the ugly stuff.
You can use it with a C# example of MediaInfo.
You may be interested in downloading the DLL package with a C# example project.

Related

How to call an external function that takes pointers as input, loaded from a C++ dll

I have a C# project where I need to use an external dll.
The dll was built using C++. I have only the c++ header file (.hpp) that describes functions interfaces (signatures), and the .dll (binary code).
I want to load the dll dynamically in my C# project and use two functions :
void OpenService(long &handle);
int GetCode(long handle, const char* pin, char* passcode, char* nextPasscode, char* tokencode, char* nextTokencode);
I am able to load the dll using DllImport from System.Runtime.InteropServices and it works for the first function using the code bellow :
[DllImport(#"C:\Program Files\Path to my dll\myDll.dll")]
public static extern void OpenService(ref long handle);
But for the second one, I tried the code bellow :
[DllImport(#"C:\Program Files\Path to my dll\myDll.dll")]
unsafe public static extern int GetCode (long handle, string* pin, string* passcode, string* nextPasscode, string* tokencode, string* nextTokencode);
But the compiler is complaining that I should use the unsafe mode while compiling to be able to use C# pointers which is something I cannot do due to rules imposed by the Tech lead.
So, my question is how can I call the second function without using C# pointers ?
I tried to use references as bellow :
[DllImport(#"C:\Program Files\Path to my dll\myDll.dll")]
public static extern int GetCode (long handle, ref string pin, ref string passcode, ref string nextPasscode, ref string tokencode, ref string nextTokencode);
But the program crashes whenever I call GetCode.
Any idea on how can I solve this problem ?
Thank you in advance.
Instead of using unsafe and pointers you can marshal (transform) the string to a char* type using the MarshalAs method:
[DllImport(#"C:\Program Files\Path to my dll\myDll.dll")]
public static extern int GetCode (long handle, [MarshalAs(UnmanagedType.LPStr)]string pin, [MarshalAs(UnmanagedType.LPStr)]string passcode, [MarshalAs(UnmanagedType.LPStr)]string nextPasscode, [MarshalAs(UnmanagedType.LPStr)]string tokencode, [MarshalAs(UnmanagedType.LPStr)]string nextTokencode);
UnmanagedType.LPStr means that the string will be marshalled as
A pointer to a null-terminated array of ANSI characters.
If you need unicode you can use UnmanagedType.LPWStr
Here is more info:
https://learn.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-for-strings

C# EXE w/ Unmanaged C++ Unicode DLL linking to unmanaged C++ ANSI DLL crash

I have a C# executable which loads in a DLL which is a unicode unmanaged C++ DLL. This unmanaged C++ DLL also links to another DLL, an unmanaged C++ DLL that happens to be ANSI.
When I run my C# executable, the program ends up crashing in the ANSI portion of the DLL calls (I haven't been able to pull the exception yet). However, by simply switching the ANSI DLL to Unicode, everything works except for the fact that there is a third DLL, which is from a SDK from another company, which has an apparent sensitivity to unicode/ANSI so it works best if the calling DLL is in ANSI.
So we have one executable calling functions in only one unmanaged unicode C++ DLL which serves as a wrapper for an unmanaged ANSI C++ DLL which is a wrapper for the final unmanaged DLL which we have no information about.
Switching the two intermediary DLL's to unicode corrects the crashing only to have it fail with the third separate vendor DLL (but not fail catastrophically with an exception, they just output incorrectly). We can't switch the first DLL to ANSI because we use Unicode in our C# application and that's our standard across the board.
I don't understand the sensitivity to a second-order DLL. Can someone shed some light on this for me?
I use this class to dynamically link to the DLL's:
static class NativeMethods
{
[DllImport("kernel32", SetLastError = true)]
public static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllToLoad);
}
with delegates similar to:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Auto)]
private delegate int ExampleFunction();
and switching the CharSet.Auto to .Ansi or .Unicode has no effect.
with function calls and such:
m_pDll = NativeMethods.LoadLibrary(#strDLLName);
if (m_pDll == IntPtr.Zero) this.Close();
IntPtr pAddressForExampleFunction = NativeMethods.GetProcAddress(m_pDll, "ExampleFunction");
if (pAddressForExampleFunction == IntPtr.Zero) this.Close();
m_ExampleFunction = (ExampleFunction)Marshal.GetDelegateForFunctionPointer(pAddressForExampleFunction, typeof(ExampleFunction));
with function call:
m_ExampleFunction();
elsewhere in code.
Edit:
As requested, the C++ EXE Counterpart:
In the .h file, defined as a member:
ExampleFunction pExampleFunction;
with
typedef BOOL __declspec(dllimport) (*ExampleFunction)();
The pExampleFunction being defined as:
pExampleFunction= (ExampleFunction) ::GetProcAddress(m_hDll,"ExampleFunction");
using this call, prior:
m_hDll = AfxLoadLibrary(m_DllName);
Most probably the problem happens between two unmanaged dlls because string data transfer between them is inconsistent.
ANSI/Unicode dll flag is a compile-time property. Compiler selects types and functions depending on this flag. TCHAR for Unicode compiled as wchar_t and for ANSI it's char. E.g. such difference could cause out of bound problem if one dll expects to get wchar_t* with length in symbols, but actual received value is char*. This is Undefined Behavior and could cause application crash.
Also many Win API functions have two versions xxxW for Unicode and xxxA for ANSI. E.g:
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif.
On C# side CharSet attribute controls string marshaling and determines how platform invoke finds function names in a DLL. It doesn't affect further string manipulations inside unmanaged C++ dll. Method
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Auto)]
private delegate int ExampleFunction();
has no strings to marshal, so CharSet doesn't affect it. There can be a difference if you have two implementations of this method on your unmanaged C++ side: ExampleFunctionA for ANSI and ExampleFunctionW for Unicode.

Invoking C# methods from C++ with usage of CoreCLR on Linux

I've found that code which allows to execute C# assembly on hosted CLR in Linux. But I want to invoke only some methods from C# dll. I've tried this and this, but I've no idea how to properly on Linux include or redefine:
ICLRMetaHost, ICLRRuntimeInfo, ICLRRuntimeHost, CLSID_CLRMetaHost,
IID_ICLRMetaHost, IID_ICLRRuntimeInfo, CLSID_CLRRuntimeHost,
IID_ICLRRuntimeHost
Do you have any idea or link to some code that invokes C# from C++ with CoreCLR on Linux?
I'm only interested in CoreCLR on Linux ( not Mono! ).
Ok, I found that in order to get delegate to C# function you have to use this three functions provided by coreCLR:
// this one first, to initialize coreCLR
int (coreclrInitializeFunction)(
const char* exePath,
const char* appDomainFriendlyName,
int propertyCount,
const char** propertyKeys,
const char** propertyValues,
void** hostHandle,
unsigned int* domainId);
// this one to get delegate to your C# function
int (coreclrCreateDelegateFunction)(
void* hostHandle,
unsigned int domainId,
const char* entryPointAssemblyName,
const char* entryPointTypeName,
const char* entryPointMethodName,
void** delegate);
// this one on the end, to close coreCLR
int (coreclrShutdownFunction)(
void* hostHandle,
unsigned int domainId);
Here's my example code calling C# function that calls C++ method on C++ object: https://github.com/Marqin/simpleCoreCLRHost

Function Parameters for LabVIEW DLL

I am trying to call a function from a DLL generated in LabVIEW. I thought this was going to be far more straightforward than it is turning out to be. The function is described below:
void __cdecl Device_Init(char DevName[]);
So in my C# code I am trying the following:
[DllImport(#"Device.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void Device_Init(StringBuilder name);
I call this in my application by simply using the following:
StringBuilder devName = new StringBuilder(DeviceName);
Device_Init(devName);
Rather than getting any initialization on my device, I see a LabVIEW vi window pop up that has a title akin to a different method within the dll (i.e. AF1_GetPressure.vi). The application then hangs with this LabVIEW window popped up and I have to exit the debugging session.
I guess my question is how my function signature might be erroneous... I used StringBuilder as I found an example on the NI website that seemed to indicate that LabVIEW requires this variable type to better ascertain the number of characters in the array. http://www.ni.com/example/31050/en/
I have tried all kinds of different combinations of parameter types but I simply can't seem to get this to work. If I try calling the dll from C++ then I can get things to work. Although, oddly, I had to dynamically load the dll in C++ because I was getting a dll initialization failure when I tried to load it with the application.
Any help would be greatly appreciated!
I was able to build a DLL with LabView 2012, and import it into a .NET 4.0 console application, call the function, and receive a result. Here is a screenshot of the VI:
And here is the import statement in C#:
[DllImport(#"SharedLib.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int StringLength(string str);
I would recommend trying something very simple like this and see if you can get it working.
I should note that I tried passing my parameter as a StringBuilder object and that worked as well - and I didn't expect it to!
Also, I recommend posting this question on the LabView forums. I was always able to get a very quick response there, and I think with LabView, you're likely to get a better response there than StackOverflow.
As requested, here are the contents of the .h file generated by LabView:
#include "extcode.h"
#pragma pack(push)
#pragma pack(1)
#ifdef __cplusplus
extern "C" {
#endif
/*!
* StringLength
*/
int32_t __cdecl StringLength(char String[]);
long __cdecl LVDLLStatus(char *errStr, int errStrLen, void *module);
#ifdef __cplusplus
} // extern "C"
#endif
#pragma pack(pop)

Defining extern "C" function in C#

I have an ActiveX control written in C# and working when run in an ActiveX compatible program (CoDeSys). The problem I've come across is that in order to allow CoDeSys to interact with the ActiveX control, CoDeSys requires the dll to export the function prototype:
void ExecuteActiveXCall(IUnknown* pUnk, char* pszId, char* pszParam, char* pszReturnBuffer, int nReturnBufferSize, DWORD* pdwReturnFlag);
I've looked without success on how to export this like you can in C++, as shown in this example:
extern "C" __declspec (dllexport) void ExecuteActiveXCall(IUnknown* pUnk, char* pszId, char* pszParam, char* pszReturnBuffer, int nReturnBufferSize, DWORD* pdwReturnFlag)
{
if (strcmp(pszId, "IWebBrowser|GoBack") == 0)
{
IUnknown* pNewUnk;
IWebBrowser* pwb;
pUnk->QueryInterface(IID_IWebBrowser, (void**) &pNewUnk);
pwb = (IWebBrowser*) pNewUnk;
if (pwb)
{
pwb->GoBack();
pwb->Release();
}
}
else if (strcmp(pszId, "IWebBrowser|GoForward") == 0)
{
IUnknown* pNewUnk;
IWebBrowser* pwb;
pUnk->QueryInterface(IID_IWebBrowser, (void**) &pNewUnk);
pwb = (IWebBrowser*) pNewUnk;
if (pwb)
{
pwb->GoForward();
pwb->Release();
}
}
}
C# does have an extern keyword, but it doesn't allow you to provide the function definition (at least I haven't found a way). After attempting this:
extern unsafe void ExecuteActiveXCall(
[MarshalAs(UnmanagedType.IUnknown)] object pUnk,
char* pszId,
char* pszParam,
char* pszReturnBuffer,
int nReturnBufferSize,
UInt32* pdwReturnFlag)
{
}
The following error occurs:
'AlarmsCSharp.AlarmsControl.ExecuteActiveXCall(object, char*, char*, char*, int, uint*)' cannot be extern and declare a body
Has anyone attempted exporting a function in a C# dll?
Are there any workarounds? (I had the thought of [DllImport("AlarmsCSharp.dll")] and calling C# in the C++ dll, but figured I'd see if anyone had a solution before)
Perhaps I'm over thinking this and don't need to export this function since the ActiveX control is able to interact already with the C# code.
EDIT: I have a feeling my translation from the C++ function prototype to the C# interface declaration. If someone with more experience with C++/C# programming could verify that I did that translation correct or incorrect, it may be of some help.
You say that CoDeSys is ActiveX-compatible; have you tried using COM interop?
There seems to be 3 main options; the first is to set up COM interop where you can instantiate your C# control / class using COM. I'm sure if you do some searching you can find some more info on that.
The second option is to make your c++ module mixed managed/unmanaged, as described here:
http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/5345a27a-a614-4a74-9f6d-ea7a999ddf83/
The third option is to use "reverse PInvoke" as described here: http://tigerang.blogspot.com/2008/09/reverse-pinvoke.html
There may be other options; those are the ones I know of.

Categories