After searching, I heard that UInt32 was the C# equivalent of C++ DWORD.
I tested results by performing the arithmetic
*(DWORD*)(1 + 0x2C) //C++
(UInt32)(1 + 0x2C) //C#
They produce completely different results. Can someone please tell me the correct match for DWORD in C#?
Your example is using the DWORD as a pointer, which is most likely an invalid pointer. I'm assuming you meant DWORD by itself.
DWORD is defined as unsigned long, which ends up being a 32-bit unsigned integer.
uint (System.UInt32) should be a match.
#import <stdio.h>
// I'm on macOS right now, so I'm defining DWORD
// the way that Win32 defines it.
typedef unsigned long DWORD;
int main() {
DWORD d = (DWORD)(1 + 0x2C);
int i = (int)d;
printf("value: %d\n", i);
return 0;
}
Output: 45
public class Program
{
public static void Main()
{
uint d = (uint)(1 + 0x2C);
System.Console.WriteLine("Value: {0}", d);
}
}
Output: 45
DWord definition from microsoft:
typedef unsigned long DWORD, *PDWORD, *LPDWORD;
https://msdn.microsoft.com/en-us/library/cc230318.aspx
Uint32 definition from microsoft
typedef unsigned int UINT32;
https://msdn.microsoft.com/en-us/library/cc230386.aspx
now you can see the difference.... one is unsigned long and the other is unsigned int
Your two snippets do completely different things. In your C++ code, you are, for some strange reason, converting the value (1 + 0x2C) (a strange way to write 45) to a DWORD*, and then dereferencing it, as if that address is actually a valid memory location. With the C#, you are simply converting between integer types.
Related
I am trying to call a C# lib from a C++ program. C# and this interop is pretty new for me. Some of the communications have been easy until I had to face strings inside structures. I have been reading a lot and saw several examples, but I am unable to replicate them and make them work.
I extracted the code with the example of sending a string, and retriving the string which works (_string_exchange). And the method _return_struct which returns a struct with a string that doesnt works. Debugger fails when I try to use the variable for std::cout, with an unhandled exception from 0x00007FFFEA98A388 (KernelBase.dll). The Console.WriteLine havent wrote anything during the call.
I assume this is a problem matching the scrutures. Both are compiled in release x64, using .NET Framework 4.6.1. Also I have been checking with sizeof() and Marshal.SizeOf() to check that both have the same byte length. Also tried to change c++ project character from unicode to multibyte without success.
I saw examples like this that were pretty good explaning everything, but I dont know what I am missing: Passing strings/arrays within structures between C++/C#
C++ program:
struct myStruct
{
int myInt;
double myDouble;
bool myBool;
char myString[64];
};
int main() {
const TCHAR* pemodule = _T("F:\\PATH\\TO\\DLLsi\\LibCSharp.dll");
HMODULE lib = LoadLibrary(pemodule);
typedef LPCSTR(_cdecl *_string_exchange)(LPCSTR s);
auto pString_exchange = (_string_exchange)GetProcAddress(lib, "_string_exchange");
LPCSTR test = pString_exchange("LPCSTR test works fine");
std::cout << test << std::endl;
typedef myStruct(_cdecl *_return_struct)();
auto pReturn_struct = (_return_struct)GetProcAddress(lib, "_return_struct");
myStruct aStruct = pReturn_struct();
std::cout << aStruct.myString << aStruct.myBool << " " << aStruct.myDouble << " " << aStruct.myInt << std::endl;
return 0;
}
C# library:
namespace LibCSharp
{
public class Class1
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct myStruct
{
public int myInt;
public double myDouble;
[MarshalAs(UnmanagedType.U1)]
public byte myBool;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string myString;
}
[DllExport]
public static myStruct _return_struct()
{
Console.WriteLine("test");
myStruct a;
a.myInt = 3;
a.myDouble = 2;
a.myBool = 1;
Console.WriteLine(a.myBool);
a.myString = "Hello world! My values are: ";//28
Console.WriteLine(a.myString);
return a;
}
[DllExport]
public static string _string_exchange(string s)
{
Console.WriteLine(s);
return s;
}
}
}
I am also aiming in a future to make this structure an array, I hope once this is solved I wont face much problems, but any comment in advance is also wellcome.
Thank you in advance
Maybe you should try using Named Pipes to communicate between two processes. It allows you to open stream and transfer any byte arrays. So you can serialize any object to array and transfer it.
Here's working solution for C++ and C# communication.
Pipes are more flexible and system-independent than interop. But probably less efficient...
At first glance, it appears your c# code is marshaling that return string as a tchar[64] which is almost certainly a unicode wchar[64]; while your C++ code is expecting an ascii char[64] to be there.
Try changing the c++ definition to
struct myStruct
{
int myInt;
double myDouble;
bool myBool;
TCHAR myString[64];
};
I want to call unmanaged c++ program with struct as parameter.
The c++ struct is like down below.
typedef union {
BYTE byBuffer[32];
struct {
union {
DWORD dwOptionFlag;
struct {
unsigned BIT_IGNOREEXSTOP : 1;
unsigned BIT_USE_CUSTOMACCDEC : 1;
};
} flagOption;
WORD wCustomAccDecTime;
};
} VELOCITY_OPTION_EX;
Function parameter documentation in c++
int FAS_MoveVelocityEx(
BYTE nPortNo,
BYTE iSlaveNo,
DWORD lVelocity,
int iVelDir,
VELOCITY_OPTION_EX* lpExOption
);
C# code I use to import the dll function
[DllImport("EziMOTIONPlusR.dll")]
public static extern EziServoResult FAS_MoveVelocityEx
(byte nPortNo, byte iSlaveNo, long lVelocity, int iVelDir, out VELOCITY_OPTION_EX lpExOption);
How to create the class from c# that match VELOCITY_OPTION_EX in c++?
Note: I only have the documentation + dll library and it's written in c++.
I'm invoking a C++ function from within C#.
This is the function header in C++ :
int src_simple (SRC_DATA *data, int converter_type, int channels) ;
And this is the equivilent C# function :
[DllImport("libsamplerate-0.dll")]
public static extern int src_simple(ref SRC_DATA sd, int converter_type, int channels);
This is the SRC_DATA structure in C++ and then in C# :
typedef struct
{ float *data_in, *data_out ;
long input_frames, output_frames ;
long input_frames_used, output_frames_gen ;
int end_of_input ;
double src_ratio ;
} SRC_DATA ;
This is the C# struct I have defined :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SRC_DATA
{
public IntPtr data_in, data_out;
public long input_frames, output_frames;
public long input_frames_used, output_frames_gen;
public int end_of_input;
public double src_ratio;
}
The big problem is that the last parameter , src_ratio, doesn't get delivered properly to the C++ function, (it sees it as 0 or something invalid).
Are my declarations correct?
Thanks
Are you sure that the problem is in src_ratio member? long in C# is 64 bit. In C++ on Win32 platform long is 32 bit, I think you need to use int in C# for long C++ structure members. Also, Pack = 1 looks a bit strange, do you use the same structure member alignment in C++?
You force packing in C# but not in C++. What could be happening is that the C++ compiler is padding the 7 in32's with four additional bytes to ensure the double is 8-byte aligned.
Check the #pragma pack compiler directives.
Check what size an int is in your C++ compiler. A C# int is always 32 bits.
It seems I have yet another problem in the understanding of marshalling to C++ DLL.
Here is the def of the C++ function & struct :
#define SIZE_PLATE (28l)
#define SIZE_HJT (15l)
#define SIZE_DATE (10)
typedef struct _tyrfdePlate
{
TCharA PlateID[SIZE_PLATE];
TInt32 NetworkID;
TInt32 CityID;
TCharA DateS[SIZE_DATE];
TCharA DateE[SIZE_DATE];
TInt32 Width;
TInt32 Height;
TBool Light;
TBool Roll;
TCharA CycleID[4];
TInt16 OrHjt1;
TCharA HJTID1[SIZE_HJT];
TInt16 OrHjt2;
TCharA HJTID2[SIZE_HJT];
TInt16 OrHjt3;
TCharA HJTID3[SIZE_HJT];
TInt16 OrHjt4;
TCharA HJTID4[SIZE_HJT];
} tyrfdePlate;
TInt32 __stdcall tyrfdeSetResults(TInt32 TargetNbr, const TInt32* pTargets, TInt32 PlateNbr, const tyrfdePlate* pPlates);
This is what I made in C# based on a previous question I asked:
[StructLayout(LayoutKind.Sequential, Size = 138), Serializable]
public struct Plate
{
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 28)]
public string PlateID;
public int NetworkID;
public int CityID;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 10)]
public string DateS;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 10)]
public string DateE;
public int Width;
public int Height;
public bool Light;
public bool Roll;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 4)]
public string CycleID;
public short OrHJT1;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
public string HJTID1;
public short OrHJT2;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
public string HJTID2;
public short OrHJT3;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
public string HJTID3;
public short OrHJT4;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 15)]
public string HJTID4;
}
[DllImport("tyrfde.dll", EntryPoint = "tyrfdeSetResults")]
public static extern int SetResults(int targetNbr, [MarshalAs(UnmanagedType.LPArray)] int[] targetIds, int plateNbr, [MarshalAs(UnmanagedType.LPArray)] Plate[] plates);
Here is an example of the call:
List<Plate> plates = new List<Plate>();
plates.Add(new Plate() { PlateID = "56013208", NetworkID = 992038, CityID = 60010, DateS = "01012009", DateE = "31122010", Width = 400, Height = 300, Light = false, Roll = false, CycleID = "0", OrHJT1 = 2, HJTID1 = "579026356", OrHJT2 = 2, HJTID2 = "579026377", OrHJT3 = 2, HJTID3 = "58571903", OrHJT4 = 0, HJTID4 = "0" });
int[] targets = new int[]{1,2,11,12,130};
int result = SetResults(5, targets, 1, plates.ToArray());
Note that I also tried with native Array instead of generic list with same result.
So basically I'm redoing a test app made in C++ with the same Data. Unfortunately, the function return me -1 which means an error occurred, but the C++ app returns 23. So I guessed something is wrong with either my struct and/or the parameter I pass. Probably the int[]. I've tried to let the default marshal with ref, but didn't work. Any ideas?
EDIT
Since the type definition seem to be very important here is the def :
typedef void TVoid;
typedef bool TBool;
typedef char TCharA; // character 8
typedef TCharA TChar; // character 8
typedef wchar_t TCharW; // character 16
typedef signed char TInt08; // integer signed 8
typedef unsigned char TUnt08; // integer unsigned 8
typedef signed short TInt16; // integer signed 16
typedef unsigned short TUnt16; // integer unsigned 16
typedef signed long TInt32; // integer signed 32
typedef unsigned long TUnt32; // integer unsigned 32
typedef float TFlt32; // float 32
typedef double TFlt64; // float 64
The Size attribute you gave in the [StructLayout] attribute is a good hint. Verify that with this snippet:
int len = Marshal.SizeOf(typeof(Plate));
System.Diagnostics.Debug.Assert(len == 138);
The only way you are going to get passed this assertion is when you replace "bool" with "byte" (So TBool = 1 byte) and use a packing of 1:
[StructLayout(LayoutKind.Sequential, Pack=1), Serializable]
public struct Plate {
//...
}
If that still doesn't work then you'll really have to debug the unmanaged code.
What you really want is a way to debug this. The easiest way is to write your own dll that consumes this data type and see what happens to the struct on the other side.
I suspect that your real problem is structure alignment and how that's working out. What I see in your code is a bunch of elements with odd sizes (15, 28, 10). Chances are the target system has gone and aligned the structure elements on at least 2 byte, if not 4 byte boundaries. Nonethless you should check.
You can also save yourself some time by writing the C that consumes the actual struct and outputs the results of a bunch of offsetof() invocations on the struct elements.
You should be methodical in your approach instead of shotgun, and part of the methodology is to have measurements and feedback. This will give you both.
1) How many bytes is a TBool? 1? 4?
2) You can remove the StructLayout(LayoutKind.Sequential, Size = 138) attribute because it's Sequential by default and the Size can be determined by the runtime.
3) You can drop the [MarshalAs(UnmanagedType.LPArray)] attribute. The marshaller knows how to marshal arrays, but note, by default arrays are Marshalled as [IN], so if the c++ code is going to edit the contents of the arrays, you need to use the [IN,OUT] attribute.
How about the const TInt32* pTargets argument in the dll. I don't know how it's used, but that suggests a pointer to a single TInt32 instance, not an array of TInt32. Granted, it depends on how it's used in the code.
I am using Mono/C# on Linux and have the following C# code:
[DllImport("libaiousb")]
extern static ResultCode QueryDeviceInfo(uint deviceIndex,
ref uint PID, ref uint nameSize, StringBuilder name,
ref uint DIOBytes, ref uint counters);
And I call a Linux shared library call defined as follows:
unsigned long QueryDeviceInfo(
unsigned long DeviceIndex
, unsigned long *pPID
, unsigned long *pNameSize
, char *pName
, unsigned long *pDIOBytes
, unsigned long *pCounters
)
I have set the parameters to known values before calling the Linux function. I've also put a printf at the beginning of the Linux function and all the parameters are printing values as expected. So the parameters seem to be passed from C# to Linux ok. The return value is also good.
However, all the other parameters that are passed by reference come back garbage.
I modified the Linux function so it simply modifies the values and returns. Here's that code:
unsigned long QueryDeviceInfo(
unsigned long DeviceIndex
, unsigned long *pPID
, unsigned long *pNameSize
, char *pName
, unsigned long *pDIOBytes
, unsigned long *pCounters
) {
printf ("PID = %d, DIOBYtes = %d, Counters = %d, Name= %s", *pPID, *pDIOBytes, *pCounters, pName);
*pPID = 9;
*pDIOBytes = 8;
*pCounters = 7;
*pNameSize = 6;
return AIOUSB_SUCCESS;
All the ref parameters still come back as garbage.
Any ideas?
libaiousb.c
unsigned long QueryDeviceInfo(
unsigned long deviceIndex
, unsigned long *pPID
, unsigned long *pNameSize
, char *pName
, unsigned long *pDIOBytes
, unsigned long *pCounters
)
{
*pPID = 9;
*pDIOBytes = 8;
*pCounters = 7;
*pNameSize = 6;
return 0;
}
libaiousb.so
gcc -shared -o libaiousb.so libaiousb.c
Test.cs
using System;
using System.Runtime.InteropServices;
using System.Text;
class Test
{
[DllImport("libaiousb")]
static extern uint QueryDeviceInfo(uint deviceIndex,
ref uint pid, ref uint nameSize, StringBuilder name,
ref uint dioBytes, ref uint counters);
static void Main()
{
uint deviceIndex = 100;
uint pid = 101;
uint nameSize = 102;
StringBuilder name = new StringBuilder("Hello World");
uint dioBytes = 103;
uint counters = 104;
uint result = QueryDeviceInfo(deviceIndex,
ref pid, ref nameSize, name,
ref dioBytes, ref counters);
Console.WriteLine(deviceIndex);
Console.WriteLine(pid);
Console.WriteLine(nameSize);
Console.WriteLine(dioBytes);
Console.WriteLine(counters);
Console.WriteLine(result);
}
}
Test.exe
gmcs Test.cs
Run:
$ mono Test.exe
100
9
6
8
7
0
Somewhat unrelated, but something to keep in mind is that the sizes of C and C++ types are not fixed in stone. Specifically, sizeof(unsigned long) will vary between 32-bits on 32-bit platforms (ILP32 systems) and 64-bits on 64-bit platforms (LP64 platforms).
Then there's Win64, which is a P64 platform, so sizeof(unsigned long) == 4 (32 bits).
The short of it is that your P/Invoke signature:
[DllImport("libaiousb")]
static extern uint QueryDeviceInfo(uint deviceIndex,
ref uint pid, ref uint nameSize, StringBuilder name,
ref uint dioBytes, ref uint counters);
Is broken -- it will only work correctly on 32-bit platforms (because C# uint is always 32-bits, while the unsigned long will be 64-bits on LP64 platforms), and will FAIL (rather horribly) on 64-bit platforms.
There are three fixes:
IFF you will always be on Unixy platforms (e.g. ILP32 and LP64 platforms only, not P64 Win64), you can use UIntPtr for unsigned long. This will cause it to be 32-bits on ILP32 platforms, and 64-bits on LP64 platforms -- the desired behavior.
Alternatively, you can provide multiple sets of P/Invoke signatures in your C# code, and perform a runtime check to determine which ABI you're running on to determine which set of signatures to use. Your runtime check could use IntPtr.Size and Environment.OSVersion.Platform to see if you're on Windows (P64) or Unix (ILP32 when IntPtr.Size == 4, LP64 when IntPtr.Size == 8).
Otherwise, you need to provide an ABI-neutral C binding to P/Invoke to, which would export functions using e.g. uint64_t (C# ulong) instead of exporting unsigned long. This would allow you to use a single ABI from C# (64-bits everywhere), but requires that you provide a wrapping C library that sits between your C# code and the actual C library you care about. Mono.Posix.dll and MonoPosixHelper follow this route to bind ANSI C and POSIX functions.