Trying to marshal C++ pointer to unsigned long into a C# ulong - c#

I have a C++ DLL that I am trying to interface with using C#. I do not have access to the DLL source code so it cannot be changed. One of the methods I need to call contains a pointer to an unsigned long:
int __stdcall Foo(FooHandle messageHandle, unsigned long* tag, char* part, char* value)
I have created a test DLL project with the same method signature so I can try to figure this out but I have not successfully been able to have the DLL change the value of the unsigned long that the pointer is referencing and get that value back on the C# side. The C# side declaration is:
[DllImport("Foo.dll", EntryPoint = "Foo", CallingConvention = CallingConvention.StdCall)]
public static extern int Foo(int messageHandle, IntPtr tag, StringBuilder part, StringBuilder value);
When I make the call from C# and then try to convert the IntPtr to Int32 or Int64 I get a zero. I have searched on here and the net in general as well as MSDN and can't seem to find an explaination of how to do this. I figured the char* would be the hard part but the StringBuilder just works.
How can you use a C++ DLL that changes the value of an unsigned long that is passed by pointer?
Any help would be appreciated. Thanks.

Just use a uint passed by ref:
public static extern int Foo(int messageHandle, ref uint tag, StringBuilder part, StringBuilder value);

Related

How to define dllimport of c++ function with void *buffer in C#

I am trying to integrate with photo printer CHC-S6145 using C++ DLL S6145usb.dll. There is a function defined there as below. I want to know what is the mapping c# DLL import for that.
Function name - chcusb_getPrinterInfo
Format
BOOL APIENTRY chcusb_getPrinterInfo (WORD tagNumber, void *rBuffer, DWORD *rLen);
Function details
Obtain the information of the specified printer based on tag identifier.
BOOL is a 32bit integer in Win32. PInvoke has a Boolean type, which can be marshaled as a 32bit integer via MarshalAs.
WORD is a 16bit unsigned integer. C# has a Uint16 type for that.
void* is a raw pointer. C# uses the (U)IntPtr type for that.
DWORD is a 32bit unsigned integer. C# has a UInt32 type for that.
DWORD*, on the other hand, is a pointer to a DWORD. C# has ref and out specifiers for passing a parameter variable by reference. Which one you need to use depends on whether the parameter is input/output (ref) or output-only (out).
Try something like this:
[DLLImport("S6145usb.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean chcusb_getPrinterInfo(UInt16 tagNumber, IntPtr rBuffer, ref UInt32 rLen);
...
UInt32 bufLen = ...;
IntPtr buffer = Marshal.AllocHGlobal((int)bufLen);
chcusb_getPrinterInfo(..., buffer, ref bufLen);
// use buffer as needed...
Marshal.FreeHGlobal(buffer);

C++/C# Interoperability Type Conversion

I am developing a program which uses dll driver file. I successfully linked the dll into my code but have a problem with one method
Dll function:
word NdEnumDevices(const char* const ** devs);
I've linked it to c# like this
[DllImport("NeurobitDrv.dll")]
public static extern ushort NdEnumDevices(ref string[] devs);
As i don't have much experience with C++ I dont really understand what const char* const ** devs means. I know for sure that this argument receives an array of strings but i only get one element where should be 3
And it Throws out an Access violation Exception
Can someone tell me what type should i use in c#??
I've worked around it, just hardcoded string array with device names and it works just fine :D
const char* const ** devs is likely the same as const char* const *devs[] which would be
pointer to
an array of
constant pointers to
constant characters.
This is most likely caused be the ref (although I'm not too sure about that). A ref to an array of strings could be compiled as a pointer to an array of strings.
As pointed out in this question, you should be safe to use const char* for that.
I think what you could be missing is the length of the array. The function should be declared as
word NdEnumDevices(const char* devs[], unsigned int length);
// C#
[DllImport("NeurobitDrv.dll")]
private static extern ushort NdEnumDevices(string[] devs, UInt32 length);
public static ushort NdEnumDevices(string[] devs) {
NdEnumDevices(devs, devs.Length); // Delegate for length
}

Return DWORD from c++ to c# (DllImport)

I have a c++ function like,
extern "C" __declspec ( dllexport ) DWORD GetSizeAfterMergeISOTemplates(BYTE* StoredISOTemplate, BYTE* CapturedISOTemplate)
{
}
I would like to return the size of array after merging the two byte arrays in the argument. The c++ part is working and its returning the correct value.
But my problem is I need to get the value in DWORD in c#. The function is,
[DllImport("XFPLibISO")]
extern static UInt32 GetSizeAfterMergeISOTemplates(Byte[] StoredISOTemplate, Byte[] CapturedISOTemplate);
But its returning the wrong value. Its 3477800 something. But actual value is around 964.
Can anybody suggest how can I do this?
C++ function you provided means CallingConvention.Cdecl. You must specify it, when applying DllImportAttribute:
[DllImport("XFPLibISO", CallingConvention = CallingConvention.Cdecl)]

Reading string from C# managed code to C++ wchar* [] getting AccessViolation

The question is easy, Want to read a string from managed C# code to my unmanaged C++ code in WCHAR* [].
The C function is:
extern "C" __declspec(dllexport) int __cdecl myfunc(int argc, WCHAR* argv[])
and in C# I imported the DLL:
[DllImport("mydll.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
public static extern int myfunc(int argc, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder str);
I run, but when I tried to read the string in my C++ code I get AccessViolationException : Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
What is the correct way to do this and vice versa (i.e. passing a string from C++ unmanaged to C# managed code)?
Help Appreciated.
Thanks
It seems that your C function expects an array of strings, and you're passing a single string instead.
I haven't used P/Invoke myself, but this question might provide some insight.
I am not sure about C# to C++ but i can help you out in your C++ to C# problem.
Export the function from C++ code like this:
DllExport std::string MyFunction( std::string MyParameter) ;
This can be imported in your C# code as:
[DllImport("DLLName.dll", EntryPoint = "MyFunction", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string MyFunction([System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string MyParameter);
Now, in your C# code the function "MyFunction" will take in a string and return a string. You can then call MyFunction and the operations can be carried out.
If you are using a WCHAR*, perhaps you should try marshalling as UnmanagedType.LPWStr instead to avoid passing half as much memory as expected?
The documentation on Default Marshaling for Strings should provide you with more details.

c# p/invoke difficulties marshalling pointers

I am attempting to call into a native .dll from c# using p/invoke. I'm able to make the call (no crash, the function returns a value), but the return code indicates "A pointer parameter does not point to accessible memory." I've resorted to trial and error in order to solve this, but I haven't made any progress so far.
Here's the signature of the native function I'm calling:
LONG extern WINAPI MyFunction ( LPSTR lpszLogicalName, //input
HANDLE hApp, //input
LPSTR lpszAppID, //input
DWORD dwTraceLevel, //input
DWORD dwTimeOut, //input
DWORD dwSrvcVersionsRequired, //input
LPWFSVERSION lpSrvcVersion, //WFSVERSION*, output
LPWFSVERSION lpSPIVersion, //WFSVERSION*, output
LPHSERVICE lphService //unsigned short*, output
);
Here's the imported signature in C#:
[DllImport("my.dll")]
public static extern int MyFunction( [MarshalAs(UnmanagedType.LPStr)]
string logicalName,
IntPtr hApp,
[MarshalAs(UnmanagedType.LPStr)]
string appID,
int traceLevel,
int timeout,
int srvcVersionsRequired,
[Out] WFSVersion srvcVersion,
[Out] WFSVersion spiVersion,
[Out] UInt16 hService
);
Here's the C definition of WFSVERSION:
typedef struct _wfsversion
{
WORD wVersion;
WORD wLowVersion;
WORD wHighVersion;
CHAR szDescription[257];
CHAR szSystemStatus[257];
} WFSVERSION, * LPWFSVERSION;
Here's the C# definition of WFSVersion:
[StructLayout(LayoutKind.Sequential)]
public class WFSVersion
{
public Int16 wVersion;
public Int16 wLowVersion;
public Int16 wHighVersion;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
public char[] szDescription;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
public char[] szSystemStatus;
}
Here's the call to MyFunction from C#:
WFSVersion srvcVersionInfo = new WFSVersion();
WFSVersion spiVersionInfo = new WFSVersion();
UInt16 hService = 0;
IntPtr hApp = IntPtr.Zero;
string logicalServiceName = tbServiceName.Text;
int openResult = MyFunction(logicalServiceName, hApp, null, 0,
XFSConstants.WFS_INDEFINITE_WAIT,
0x00000004, srvcVersionInfo, spiVersionInfo,
hService);
As I said, this call returns, but the return value is an error code indicating "A pointer parameter does not point to accessible memory." I must be doing something wrong with parameters 1,3,7,8, or 9. However, I've made successful calls to other functions in this .dll which required WFSVERSION* as parameters, so I don't think parameters 7 or 8 are the issue here.
I would appreciate any thoughts you might have about the cause of this issue, or any constructive criticisms of my code. This is my first experience with P/Invoke, so I'm not sure where to begin. Is there any way to narrow down the issue, or is trial an error my only option?
You have two obvious errors here. In your struct definition you should be using byte[] instead of char[] for szDescription and szSystemStatus.
Also the last parameter in your pInvoke call is not a pointer. When you make your call to MyFunction hService is zero and therefore an invalid pointer as far as the function is concerned. [Out] is a Marshaling directive telling the runtime when and where to copy data not an indicator that the parameter is a pointer. What you need is to change [Out] to out or ref this tells the runtime that hService is a pointer:
[DllImport("my.dll")]
public static extern int MyFunction( [MarshalAs(UnmanagedType.LPStr)]
string logicalName,
IntPtr hApp,
[MarshalAs(UnmanagedType.LPStr)]
string appID,
int traceLevel,
int timeout,
int srvcVersionsRequired,
[Out] WFSVersion srvcVersion,
[Out] WFSVersion spiVersion,
out UInt16 hService);
Some ideas:
The C# WFSVersion class should probably be a struct. I don't know if the P/Invoke marshaller cares, but I've always seen structs used.
Character size might be an issue.
C's CHAR is 8 bits wide (ANSI), and .Net's System.Char is 16 bits wide (Unicode). To give the marshaller as much info as possible so it does the correct conversion, try adding "CharSet = CharSet.Ansi" to the DllImport and StructLayout attributes, and changing the string declarations in WFSVersion:
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
public string szDescription;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
public string szSystemStatus;
Another issue could be data alignment in the structs. If no alignment was specified when the C struct was compiled, the data elements in the struct were probably aligned on a one or two byte boundary. Try using Pack in WFSVersion's StructLayout attribute:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
// Try pack values of 2, 4 and 8 if 1 doesn't work.
And some questions:
Was MyFunction intended to be called from non-C code? The original author may have written code that assumes the data passed in is allocated with the C runtime memory manager.
Does code in the C DLL use the pointers passed to it for later processing after MyFunction has returned? If so - and assuming it's possible/wise/sane to go forward with such a situation - it may be necessary to "pin" the structs passed to MyFunction using the fixed keyword. Plus there are probably security issues to deal with.
I'm not sure exactly what the issue with this one is but hopefully this will be a starting point.
Try looking into the options on the DllImport attribute, this could be to do with the marshalling of the strings.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
The CharSet option is the one I think you may need.
I have found AppVerifier useful for tracking down P/Invoke marshal issues before. It's free and from Microsoft.
My guess would be that one of your pointers (lpSrvcVersion, lpSPIVersion or lphService) isn't accessible from your .NET application. Can you try modifying the DLL (if it's yours) and see if you can get it working without the pointers? (I know you'll have to add them back later, but at least you can narrow down the location of the problem.)
Are you sure that it is not hApp? That looks like an input parameter, the handle to the requesting application. Quick google...yes, there is a function to create an app handle, and a default parameter you can use.

Categories