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.
Related
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.
I ran Code Analysis on a utility I'm maintaining, and it advised me to change this:
private static extern int ReadMenu1File(string Menu1Path);
...to this:
private static extern int ReadMenu1File(UnmanagedType.LPWStr Menu1Path);
...with this verbiage: "Specify marshaling for P/Invoke string arguments To reduce security risk, marshal parameter 'Menu1Path' as Unicode, by setting DllImport.CharSet to CharSet.Unicode, or by explicitly marshaling the parameter as UnmanagedType.LPWStr. If you need to marshal this string as ANSI or system-dependent, specify MarshalAs explicitly, and set BestFitMapping=false; for added security, also set ThrowOnUnmappableChar=true."
...but when I did, it says, "The type name 'LPWStr' does not exist in the type 'System.Runtime.InteropServices.UnmanagedType'" and "'System.Runtime.InteropServices.UnmanagedType.LPWStr' is a 'field' but is used like a 'type'"
Code completion is not helping (no suggestions after typing "UnmanagedType.") nor is there a context menu option to add a missing using.
Are you sure it didn't want you to decorate the parameter with the [MarshalAs(UnmanagedType.LPWStr)] attribute? For example:
private static extern int ReadMenu1File(
[MarshalAs(UnmanagedType.LPWStr)] string Menu1Path);
UnmanagedType is an enum, so you're trying to use one of its members as a type, which is why the compiler is complaining.
I suspect you've misinterpreted the advice. I suspect it was actually suggesting:
private static extern int ReadMenu1File([MarshalAs(UnmanagedType.LPWStr)]
string Menu1Path);
EDIT: This fits in with the advice:
or by explicitly marshaling the parameter as UnmanagedType.LPWStr
That's not the same as saying "Change the parameter type to UnmanagedType.LPWStr" - it's just telling you that's how you ought to marshal the parameter.
The other recommendations are to be set on the [DllImport] instead.
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.
I'm having an issue calling a function in a c++ dll inside of a c# app. I'm calling the function inside of c# like so:
[DllImport("cryptopp.dll")]
public static extern IntPtr RSAEncryptString(string filename, string seed, string message);
It is being exported in the c++ dll as shown below.
extern "C" __declspec(dllexport) const char* __cdecl RSAEncryptString(const char *pubFilename, const char *seed, const char *message);
What I get when I try to call this, however, is an "An External component has thrown an exception." exception, which is not very descriptive at all, and extremely unhelpful.
When I pull up the dll in an export viewer, it shows all the other exported functions with fully quantified declarations (I.E. public: void __cdecl CryptoPP::X509PublicKey::`vbase destructor'(void) __ptr64 ) , except for the function I am calling, which just displays the function name RSAEncryptString.
This is the only possible issue I can see, besides maybe mis-calling the function with an invalid declaration on the c# side. Am I using System.Runtime.InteropServices.Marshal wrong?
Please help <3 and thanks in advance.
I think you need to change the first line to:
[DllImport("cryptopp.dll",
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
If you want to get very descriptive, you can also add these:
public static extern IntPtr RSAEncryptString(
[In, MarshalAs(UnmanagedType.LPStr)] string filename,
[In, MarshalAs(UnmanagedType.LPStr)] string seed,
[In, MarshalAs(UnmanagedType.LPStr)] string message);
IIRC think the CharSet should take care of the encoding thing for you, but if it doesn't, use the MarshalAs also, as shown above.
Edit:
Oh I think I got why you still get an error! Your code still had the above problems, but it's still erring because you can't return a string object since it's not a managed object; you need to return a pointer (like IntPtr) and then use Marshal.PtrToStringAnsi!
(I didn't really look at your return type when answering this at first.)
It appears you're trying to store the return value of type const char * (an LPCSTR) into an IntPtr type (usually used for HANDLEs, not LPSTRs.) Try this:
[DllImport("cryptopp.dll", CharSet = CharSet.Auto)]
public static extern String RSAEncryptString(String filename, String seed, String message);
Also keep in mind that if any argument is getting written to, you'll need to add out before its type, i.e. ..., out String message)
I'm using a DLL written in c++ in my C# project. I have been able to call functions within the DLL using this code:
[DllImport("hidfuncs", EntryPoint = "vm_hid_scan", ExactSpelling = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr VmHidScan();
Now I need to call a function that requres a custom type pointer. The Docs for the DLL layout the function like this:
hid_get_info(int n,PDEV_INFO *pdi)
I don't know how to use this custom pointer. Is this defined in the DLL? If so how can use it from C# project? If not do I need to include the header file in c#? Thanks in advance for your help.
Given the "P" prefix, it looks like the real declaration is
hid_get_info(int n, DEV_INFO **pdi)
where DEV_INFO is a structure. You'll need to find the declaration of this structure and add it to your C# code with the [StructLayout] attribute. You'd then declare the function like this in your C# code:
[DllImport("blah.dll")]
private static extern something hid_get_info(int n, out IntPtr pdi);
and use Marshal.PtrToStructure() to obtain the structure value. Hopefully you don't have to free the structure, you'd be screwed.
A second interpretation is that "pid" returns an array of pointers to DEV_INFO structures. Somewhat likely given the "n" argument, which could well mean the number of elements in the array you pass to be filled by the function. In that case, pass an IntPtr[] and set "n" to its Length.
You need to create a struct in C# that mirrors the C++ PDEV_INFO struct in C++.
You should apply [StructLayout(LayoutKind.Sequential)] to the struct and then copy the fields from the C++ struct (look at the header file) in order.
You can then write an extern method that takes the struct as a ref parameter.
I'll safely assume PDEV_INFO* is a DEV_INFO**.
Use this in C#:
class DEV_INFO
{
// fields go here
}
static class NativeMethods
{
[DllImport...]
public static extern int hid_get_info(int n, ref DEV_INFO pdi);
}