How would you declare DLL import signature? - c#

this is a follow-up post of Using pHash from .NET
How would you declare following C++ declaration in .NET?
int ph_dct_imagehash(const char* file,ulong64 &hash);
So far i have
[DllImport(#"pHash.dll")]
public static extern int ph_dct_imagehash(string file, ref ulong hash);
But I am now getting following error for
ulong hash1 = 0, hash2 = 0;
string firstImage = #"C:\Users\dance2die\Pictures\2011-01-23\177.JPG";
string secondImage = #"C:\Users\dance2die\Pictures\2011-01-23\176.JPG";
ph_dct_imagehash(firstImage, ref hash1);
ph_dct_imagehash(secondImage, ref hash2);
It basically says that my declartion of ph_dtc_imagehash is wrong.
What am I doing wrong here?

Stack imbalance indicates that the C++ code uses cdecl and your C# uses stdcall calling convention. Change your DLLImport to this:
[DLLImport(#"pHash.dll", CallingConvention=CallingConvention.Cdecl)]
The function signature in C# (return value and parameters) is otherwise correct.

Check the calling convention. If you don't specify one on the DllImport attribute, it defaults to WinApi (stdcall). The C snippet you posted doesn't specify a calling convention, and at least in VC++ the default calling convention is cdecl.
So you should try:
[DllImport(#"pHash.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int ph_dct_imagehash(string file, ref ulong hash);

Try explicitly setting DllImportAttribute.CharSet property to CharSet.Auto as if you don't specify it, it will default to Ansi. This may be causing issues as your declaration seems to be fine.
Even if this is not the issue, take habit of specifying the CharSet property whenever a Dll function deals with text.

Related

PInvoke marshalling of wchar_t parameter

According to https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-data-with-platform-invoke, if I have a parameter of type wchar_t in a C++ function I need to "decorate it with Unicode". However, the MarshalAs attribute doesn't seem to have any option for this. The UnmanagedType enum in System.Runtime.InteropServices doesn't have a Unicode element. Or Ansi for that matter, either, though the page suggests that it should.
So, how is this done?
Edit
Suppose I had a function that looked like this:
int doSomething(wchar_t charIn) { ... }
or even
int doSomethingElse(wchar_t *charArr) {...}
taking an array of wchar_t values. According to the page I should annotate (decorate) these parameters with Unicode. How would I do this?
wchar_t* is basically a String and it's the same as LPWSTR. For the method doSomethingElse, I would say either this:
[DllImport("MyLib.dll", CharSet = CharSet.Unicode)]
internal static extern Int32 doSomethingElse(StringBuilder builder);
or this:
[DllImport("MyLib.dll")]
internal static extern Int32 doSomethingElse([MarshalAs(UnmanagedType.LPWStr)] String str);
For what concerns the method doSomething, I'm kinda uncertain. I mean, it asks for a single char, and it's weird. A byte would not be the same thing and I'm wondering if your declaration is correct.

typedef void* alternative in c#

I am using DLL runtime which is made with C language into C#.
I came across below statement.
typedef void *JCCP_PROPERTY_HANDLE;
In function it is being used as:
JCCP_RESULT __JCCP_FUNCTION__ jccpGetProperty(
JCCP_HANDLE hjccp,
const char *name,
JCCP_PROPERTY_HANDLE *phproperty);
Now I want to call jccpGetProperty() method in my C# code.
Can anybody tell how can I pass third parameter(JCCP_PROPERTY_HANDLE *phproperty) to function from C#.
I tried with below code but not working.
Extern Method:
[DllImport(DLL_NAME, EntryPoint = "_jccpGetProperty", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jccpGetProperty(IntPtr hjccp, string name, ref IntPtr JCCP_PROPERTY_HANDLE);
Usage
IntPtr handle = IntPtr.Zero;
string tag = "server.version";
var result = jccpGetProperty(hjccp, tag, ref handle);
Can anybody help me in this?
IntPtr is the correct type mapping for void*. The native type void* is generally used for an opaque pointer, and that is mapped to IntPtr in C#.
Those parts of the p/invoke declaration that we can verify are correct. The unverifible parts are:
The calling convention. You believe that it is cdecl, but we can't check.
The return type. You believe it to be pointer sized. Again we cannot check. My guess is that a 32 bit integer, int or uint is more likely. That would make a difference in a 64 bit process.
The values passed to the function. It's perfectly possible that the function is declared correctly, but you are passing invalid values.
Because you only showed partial code and details, it's hard to say much more. You will have to verify all the parts of the program that we cannot.
I suggest that you start with working C or C++ code and translate that, looking for the first point of deviation in behaviour between that code and your C# translation.

C# : Pass int array to c++ dll

I have a C++ dll which is used to card printing( ID cards ). My implementation done using C#.Net. I used following code to call c++ dll.
[DllImport(#"J230i.dll",CallingConvention = CallingConvention.Cdecl,SetLastError=true)]
public static extern int N_PrintJobStatus(ref int[] nPrtintjobStatus);
int[] pJob = {0,0,0,0,0,0,0,0} ;
ret = N_PrintJobStatus( ref pJob);
N_PrintJobStatus method signature given as bellow
N_PrintJobStatus(int *pJobStatus )
After calling the method it gives following error
A call to PInvoke function '********!*********.frmCardPrint::N_PrintJobStatus' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
How can I fix this issue
thank you .....
Your translation is incorrect. An int array, int* does not map to ref int[]. The latter would be marshalled as int**. You need instead to use int[].
[DllImport(#"J230i.dll", CallingConvention = CallingConvention.Cdecl,
SetLastError = true)]
public static extern int N_PrintJobStatus(int[] nPrtintjobStatus);
Allocate the array before calling the function. Presumably you have some way to determine how long it should be. As it stands this function looks like a buffer overrun waiting to happen. How can the function know how long the array is and so take steps to avoid writing beyond its end?
It's not clear that this is the only problem. We cannot be sure that the return type really is int. Or that the calling convention is cdecl. Or that the function really does call SetLastError.

Call to external DLL from C# with integer pointer

I'm trying to call an external .dll function from c#. The doc for the dll defines the function:
int funcName(int *retVal)
I've tried various configurations and always the unbalanced stack error from p/invoke; My c# code currently looks like this:
[DLLImport("dllName");
unsafe static extern int funcName(ref IntPtr retVal);
unsafe IntPtr retNum;
int status = funcName(ref retNum);
Any ideas are appreciated!
Your p/invoke declaration has the wrong parameter type.
ref Int32 is the correct match for int*.
IntPtr can also work.
ref IntPtr would be int**. Definitely not what you want.
Use
[DLLImport("dllName")]
static extern int funcName(ref Int32 retVal);
Also make sure that the calling convention matches. You should never use a dllexport in C or C++ without also using an explicit calling convention, and then the C# DllImport needs to have the matching convention.
Generally the prototype in C++ should be
extern "C" int __stdcall funcName(int* arg);
Is there a header file provided for C and C++ clients that you could check to verify the signature?

Marshalling char *

i have the following method signature on my c++ dll:
extern char *bpStringCalc(char *bpDirectory, char *issString);
And i'm trying to call it from c# using this:
[DllImport(#"C:\MuniAxis\Bp\BpDLL.dll", CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string bpStringCalc([MarshalAs(UnmanagedType.LPStr)] string bpDirectory,
[MarshalAs(UnmanagedType.LPStr)] string issString);
But it keep getting this exception:
'ConsoleApplication1!ConsoleApplication1.Program::bpStringCalc'
has unbalanced the stack. This is
likely because the managed PInvoke
signature does not match the unmanaged
target signature. Check that the
calling convention and parameters of
the PInvoke signature match the target
unmanaged signature.
Any ideas?
Thanks
Try specifying a Cdecl calling convention on import or __stdcall on export. See this almost similar question.
Unbalancing the stack probably has more to do with calling convention than it does the actual arguments. C++, by default, uses the __cdecl calling convention. C# defaults to __stdcall because __stdcall is the convention used by Win32. You need to either set calling convention on your import statement in C#, or you need to specify __stdcall in your C++ binary.
EDIT: The above was edited to fix the fact that __cdecl and __stdcall had only one leading underscore each ;)

Categories

Resources