Here's a piece of code for obtaining the time when a .NET assembly was built. Note this line:
int secondsSince1970 = System.BitConverter.ToInt32(b, i + c_LinkerTimestampOffset);
this code extracts the TimeDateStamp member of IMAGE_FILE_HEADER structure that is stored inside the assembly. The structure is defined as follows:
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
and DWORD is defined as follows:
typedef unsigned long DWORD;
and the struct description says that TimeDateStamp is a number of seconds since an arbitrary moment in the past, so it can't be negative.
Why does the C# code use signed type int to store that unsigned value?
It is because unsigned int is not a CLS compliant variable type and all .NET libraries should follow Common Language Specification.
More info about CLS compliance:
http://msdn.microsoft.com/en-us/library/12a7a7h3.aspx
Related
I have C++ dll which has below method signature. I would like to know what should be compatible C# method to call C++ dll. I am getting error on execution as attempted to read write protected memory. The dll used here is third party dll. I am able to call simple methods without pointers from C++ dll.
C++ Declaration:
int __stdcall Deduct(int V, unsigned char *AI);
C# Declaration
[System.Runtime.InteropServices.DllImport("rwl.dll",EntryPoint = "_Deduct#8", CallingConvention = CallingConvention.Cdecl)]
public static extern long Deduct(long i,ref string AI);
As per the document of third party dll.
AI Used as input and output buffer. The buffer shall have at least 80 bytes
Input Additional Information for the transaction. It is a byte pointer containing 7 bytes.
e.g. Assume the usage information is not used,
if receipt number = 1234567,
hex value = 0x12D687,
7 bytes AI = D6 87 00 00 00 D6 87
Output On return, the AI contains the UD.
Please help.
I think the c# signature looks wrong.
[System.Runtime.InteropServices.DllImport("rwl.dll",EntryPoint = "_Deduct#8", CallingConvention = CallingConvention.Cdecl)]
public static extern long Deduct(int i,ref char[] AI);
if it is expecting a char[80] as it's input you may need to declare it as such and null terminate it yourself as whatever is reading from it might be reading past the end of the array.
usage
char[] tmp = new char[80];
tmp[0] = (char)0x00;
int i = 0;
Deduct(i, ref tmp);
I'm not sure if this will work but hopefully it helps.
Besides string marshaling, I found a likely reason for getting an exception.
You have specified the return type and the first parameter as int in C, but long int C#. This could cause problems.
C language specifies minimum bits of integer types which means that different for environments.
int in C : unsigned integer, at least 16 bits
long in C : unsigned integer, at least 32 bits
int in C# : 32-bit unsigned integer, always
long in C# : 64-bit unsigned integer, always
Most of major 32/64-bit OS/compilers use 32 bits for type int in C. However long is always 64-bit in C#. So the signature mismatches. I would suggest to revise:
public static extern int Deduct(int i, ref string AI); // Still not sure about string
Is there a way to emulate special C datatypes, like uint64 (_int64), anyID (_int16) in C#?
I defined the special datatypes in C like this:
typedef unsigned _int16 anyID;
typedef unsigned _int64 uint64;
Its for using the TS3 Plugin API.
It has to be C# though, and I just want to use the from TS3 defined C datatypes in C#.
The equivalent of a typedef is using:
using anyID = System.UInt16;
using uint64 = System.UInt64;
The sizes of the different numeric types in C# can be found here: Integral Types Table.
One thing to note: the sizes of the different numeric types are fixed in C#, unlike in C where they are platform-dependent, so it's usually redundant to define aliases for numeric type sizes like int64.
unsigned ints are already predefined See MS c# types
in short: ushort is an unsigned 16bit int, and ulong is an unsigned 64bit int..
For unsigned integers you have ushort, uint and ulong, which is the equivalent to unsigned int16, unsigned int32 and unsigned int64 respectively.
I've noticed that DWord and QWord values when written to the Registry supposed to be Signed Integers, not Unsigned. This code will throw an exception if value is UInt64 or UInt32:
registryKey.SetValue(name, value);
According to MSDN DWORD is a 32-bit unsigned integer (range: 0 through 4294967295 decimal) https://msdn.microsoft.com/en-us/library/cc230318.aspx
So, to write new DWORD value to the Registry I need to cast it to signed integer like so:
UInt32 unsignedValue = (UInt32)someValue;
Int32 signedValue = (Int32)unsignedValue;
registryKey.SetValue(name, signedValue);
Passing unsigned value to SetValue method will throw an exception.
Am I missing something or I just being retarded?
For historical reasons, the .NET API/libraries is normally "signed" instead of being "signed + unsigned".
But in the end, a signed int and a unsigned int both occupy the same memory space, and there is no special handling done for negative values. So you can do as you said: cast the unsigned value to signed, write it with SetValue and then if you look at the value in Regedit you'll see that it was written "unsigned".
Note that if your program is compiled in "checked" mode, a more correct code would be:
uint unsignedValue = ... // Your original value
int signedValue = unchecked((int)unsignedValue);
registryKey.SetValue(name, signedValue);
Because in "checked" mode casting between int and uint can throw an exception if the conversion isn't possible.
Note that as written here:
This overload of SetValue stores 64-bit integers as strings (RegistryValueKind.String). To store 64-bit numbers as RegistryValueKind.QWord values, use the SetValue(String, Object, RegistryValueKind) overload that specifies RegistryValueKind.
Clearly you'll have to do the same handling for signed-unsigned.
From the RegistryKey.SetValue page' example:
// Numeric values that cannot be interpreted as DWord (int) values
// are stored as strings.
It seems the stored values are signed ints or strings.
Is this list correct?
unsigned int(c) -> uint(c#)
const char*(c) -> String(c#)
unsigned int*(c) -> uint[](c#)
unsigned char*(c) -> byte[](c#)
I think there's a mistake here because with these 4 parameters for native function I have PInvokeStackImbalance.
C function is:
bool something
(unsigned char *a,
unsigned int a_length,
unsigned char *b,
unsigned int *b_length);
PInvoke is:
[DllImport(#"lib.dll", EntryPoint = "something")]<br>
public static extern bool something(
byte[] a,
uint a_length,
byte[] b,
uint[] b_length);
First, PInvoke.net is your friend.
Second, You conversions are correct except that you should use a StringBuilder for functions that take a char* as a buffer to fill ([in out]).
Your stack imbalance may be due to the use of different calling conventions. The default calling convention for C# is __stdcall, but your C function is probably __cdecl. If that is the case you will need to add the CallingConvention to your DLLImport attribute.
EDIT: Also, as Groo pointed out, if the pointer arguments in your C function are actually just pointers to unsigned int (for example, as opposed to expecting an array of int) then you should use ref uint instead of an int[].
The pinvoke documentation fro GetExitCodeProcess shows exit codes returned as unsigned integers (uint). How do I handle a process with negative exit code values? Is LPDWORD correctly assigned to uint or is that a bug in pinvoke doc?
pinvoke doc:
http://www.pinvoke.net/default.aspx/kernel32.getexitcodeprocess
win32 api doc:
http://msdn.microsoft.com/en-us/library/ms683189(v=vs.85).aspx
DWORD in unsigned integer.
A 32-bit unsigned integer. The range is 0 through 4294967295 decimal.
This type is declared in WinDef.h as follows:
typedef unsigned long DWORD;
No bug here.