Ok, I am using the SetFilePointer function in C# with .NET 4.0. Below are the dllimports I used to call this function:
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
static extern uint SetFilePointer([In] SafeFileHandle hFile, [In] long lDistanceToMove, [Out] out int lpDistanceToMoveHigh, [In] EMoveMethod dwMoveMethod);
Whenever I run the code that uses the SetFilePointer function in the debugger I get this exception:
PInvokeStackImbalance was detected
Message: A call to PInvoke function 'MyDiskStuff!MyDiskStuff.HardDisk::SetFilePointer' 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.
Whenever I run the same code outside of the debugger I do not get the above exception.
Below is the code I am using to make the calls to SetFilePointer:
public enum EMoveMethod : uint
{
Begin = 0,
Current = 1,
End = 2
}
uint retvalUint = SetFilePointer(mySafeFileHandle, myLong, out myInt, EMoveMethod.Begin);
Is there something wrong with my dllimport signatures?
Your P/Invoke signature is a little off:
Here's the Win32 definition:
DWORD WINAPI SetFilePointer(
_In_ HANDLE hFile,
_In_ LONG lDistanceToMove,
_Inout_opt_ PLONG lpDistanceToMoveHigh,
_In_ DWORD dwMoveMethod
);
And here's the P/Invoke with your enum specified:
[DllImport("kernel32.dll", EntryPoint="SetFilePointer")]
static extern uint SetFilePointer(
[In] Microsoft.Win32.SafeHandles.SafeFileHandle hFile,
[In] int lDistanceToMove,
[In, Out] ref int lpDistanceToMoveHigh,
[In] EMoveMethod dwMoveMethod) ;
EDIT: Oh, and some test code:
var text = "Here is some text to put in the test file";
File.WriteAllText(#"c:\temp\test.txt", text);
var file = File.Open(#"c:\temp\test.txt", FileMode.OpenOrCreate);
int moveDistance = 10;
int moveDistanceHighBits = 0;
uint retvalUint = SetFilePointer(file.SafeFileHandle, moveDistance, ref moveDistanceHighBits, EMoveMethod.Begin);
Debug.Assert(Encoding.ASCII.GetBytes(text)[moveDistance] == file.ReadByte());
Also note from the docs:
lDistanceToMove [in]
The low order 32-bits of a signed value that specifies the number of bytes to move the file pointer.
If lpDistanceToMoveHigh is not NULL, lpDistanceToMoveHigh and lDistanceToMove form a single 64-bit signed value that specifies the distance to move.
If lpDistanceToMoveHigh is NULL, lDistanceToMove is a 32-bit signed value. A positive value for lDistanceToMove moves the file pointer forward in the file, and a negative value moves the file pointer back.
lpDistanceToMoveHigh [in, out, optional]
A pointer to the high order 32-bits of the signed 64-bit distance to move.
If you do not need the high order 32-bits, this pointer must be set to NULL.
When not NULL, this parameter also receives the high order DWORD of the new value of the file pointer. For more information, see the Remarks section in this topic.
Likely.
pinvoke.net lets CallingConvention default to StdCall (instead of your Cdecl setting) and since SetFilePointer is declared as WINAPI (which is __stdcall). Incorrect calling convention will damage your stack.
Related
I'm using mpusbapi.dll in WPF project using C#.
In C++ the function prototype is:
DWORD MPUSBWrite(HANDLE handle,
PVOID pData,
DWORD dwLen,
PDWORD pLength,
DWORD dwMilliseconds);
And my p/invoke in C# is:
[DllImport("mpusbapi.dll")]
private static extern DWORD _MPUSBWrite(void* handle, void* pData, DWORD dwLen, DWORD* pLength, DWORD dwMilliseconds);
So, because I dont make all my project unsafe, I want to buid this next method to send data:
unsafe private void SendPacket(byte* SendData, UInt32 SendLength)
{
uint SendDelay = 10;
UInt32 SentDataLength;
openPipes();
MPUSBWrite(myOutPipe, &SendData, SendLength, &SentDataLength, SendDelay);
closePipes();
}
But Visual Studios show me an error with the variable type of the parameters. "Cannot convert from 'byte**' to 'system.IntPtr'" and "Cannot convert from 'uint*' to 'system.IntPtr'".
I am new using C # and now the pointers have me stuck. How should I translate the c++ parameter to C#? Thanks!
EDIT:
I didn't notice that I've changed the Pinvoke to:
[DllImport("mpusbapi.dll", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 _MPUSBWrite(IntPtr handle,IntPtr pData, UInt32 dwLen,IntPtr pLength, UInt32 dwMilliseconds);
ADD:
Removing the & in the method call did not fix the last errors and added this one "Use of unassigned local variable 'SendDataLenght'".
I don't see the need for any of this to be unsafe and use pointers. Making some basic assumptions on the parameter semantics, you might use this declaration:
[DllImport("mpusbapi.dll")]
private static extern uint _MPUSBWrite(
IntPtr handle,
byte[] data,
uint dataLength,
out uint sentDataLength,
uint timeoutMS
);
I don't know what the calling convention should be. You use both stdcall and cdecl in the question. Rather than guessing, you need to find out definitively.
I am getting a PInvokeStackImbalance: 'PInvokeStackImbalance was detected
Message: A call to PInvoke function 'ConvertedClass::MapViewOfFile' 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.'
I am fairly new to DLL use, and just managed to work out a few tutorials today.
Any help would be appreciated.
using System.Runtime.InteropServices;
//dll
[DllImport("kernel32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
public static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, FileMapAccessRights dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, ulong dwNumberOfBytesToMap;)
string szSharedMemory = "FUNKY_BUSINESS";
//other dll call is successful and returns value
IntPtr hMem = OpenFileMapping(FileMapAccessRights.Write, FALSE, szSharedMemory);
///BOOM.. not this one
IntPtr pvHead = MapViewOfFile(hMem, FileMapAccessRights.Write, 0, 0, 0);
Edit: It was a bad argument.. The 5th arg should be UIntPtr instead of ulong.
this is how i feel right now
The final parameter is SIZE_T. That's unsigned, and 32 bits in a 32 bit process and 64 bits in a 64 bit process. So the best solution is to use UIntPtr for the final parameter.
I would use the following:
[DllImport("kernel32")]
public static extern IntPtr MapViewOfFile(
IntPtr hFileMappingObject,
FileMapAccessRights dwDesiredAccess,
uint dwFileOffsetHigh,
uint dwFileOffsetLow,
UIntPtr dwNumberOfBytesToMap
);
Your code uses ulong which is always 64 bits wide. And your process is a 32 bit process which explains why the P/invoke marshaller has detected a stack imbalance.
The 5th parameter should be an uint, not ulong.
public static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, FileMapAccessRights dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap;)
For P/Invoke, you can use sample code from pinvoke.net.
http://www.pinvoke.net/default.aspx/kernel32.mapviewoffile
The task is to determine the last write time for the registry key. As standard RegistryKey class doesn't provide this feature, I have to use WinAPI function "RegQueryInfoKey". To get the key handle I open it by "RegOpenKeyEx".
This is the WinAPI prototype of the function (taken from MSDN):
LONG WINAPI RegOpenKeyEx(
__in HKEY hKey,
__in LPCTSTR lpSubKey,
DWORD ulOptions,
__in REGSAM samDesired,
__out PHKEY phkResult
);
I use the following declaration:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int RegOpenKeyEx(UIntPtr hkey, string lpSubKey, uint samDesired, ref UIntPtr phkResult);
Then I call it in the following way:
UIntPtr hKey = UIntPtr.Zero;
string myKeyName = "blablabla";
UIntPtr HKEY_USERS = (UIntPtr)0x80000003;
uint KEY_READ = 0x20019;
RegOpenKeyEx(HKEY_USERS, myKeyName, KEY_READ, ref hKey);
At this point I get "Access violation" exception. What am I doing wrong?
I think something is wrong with parameters passing, but how to do it right?
Thank you.
There are 5 parameters in the native function's prototype, and only 4 in your P/Invoke signature.
In particular, you're missing DWORD ulOptions. This parameter is "reserved and must be zero" according to the MSDN documentation, but it must still be passed in the function call.
Also, you don't need to set the SetLastError field because the RegOpenKeyEx function returns its error code; you don't have to retrieve it by calling GetLastError. Consequently, you don't need the marshaler to save that value for you automatically. Just check the return value for the error code.
Change your P/Invoke signature to look like this:
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(UIntPtr hkey, string lpSubKey,
uint ulOptions, uint samDesired,
out UIntPtr phkResult);
The wrong P/Invoke signature is almost always the cause of "access violation" errors. When you see one of those, make sure you double-check it twice!
You've missed ulOptions from your P/Invoke signature.
I'm using EasyHook, a C# library for injecting and detouring functions from unmanaged applications. I'm trying to hook onto GetDlgItemTextA, which takes the arguments:
UINT WINAPI GetDlgItemText(
__in HWND hDlg,
__in int nIDDlgItem,
__out LPTSTR lpString,
__in int nMaxCount
);`
In my hook, I am casting it as:
[DllImport("user32.dll",
// CharSet = CharSet.Unicode,
SetLastError = true,
CallingConvention = CallingConvention.StdCall)]
static extern uint GetDlgItemTextA(IntPtr hWin, int nIDDlgItem, StringBuilder text, int MaxCount);
And my hook is:
static uint DGetDlgItemText_Hooked(IntPtr hWin, int nIDDlgItem, StringBuilder text, int MaxCount)
{
// call original API...
uint ret = GetDlgItemTextA(hWin, nIDDlgItem, text, MaxCount);
MessageBox.Show(text.ToString());
return ret;
}
Unfortunately, the moment this is called, the hooked application crashes. Is there a better cast I can use to successfully hook onto this function? Thanks!
I've compiled, editted, and confirmed the working condition of my EasyHook setup. This is just casing and hooking only.
Well, it appears that the code I had did work, but the the only difference I had to put a try catch statement in the hook for an unknown reason. StringBuilder does correctly get converted from LPCSTR and back to LPCSTR, and the program reads it just fine. The program does not crash now, so I thought I would add this as my own answer.
I am making a wrapper to read TDM and TDMS files but i have a problem
[DllImport(lib, CharSet = CharSet.Auto)]
static extern int DDC_OpenFileEx(
[MarshalAs(UnmanagedType.LPStr)]
string filePath,
[MarshalAs(UnmanagedType.LPStr)]
string fileType,
int read_only,
ref long file);
works fine but
[DllImport(lib, CharSet = CharSet.Auto, SetLastError = true)]
static extern int DDC_GetNumChannelGroups(long file,
[MarshalAs(UnmanagedType.U4)]
ref int numChannelGroups);
int numGru = 0;
errCode = ReadTDM.DDC_GetNumChannelGroups(file,ref numGru);
System.Console.WriteLine("Error Code {0} GetNumChannelGroups", errCode);
gives an error -6202, // An invalid argument was passed to the library.
i have tried ref uint, uint * (unsafe), UIntPtr. The def from .h file
int __stdcall DDC_GetNumChannelGroups (DDCFileHandle file,unsigned int *numChannelGroups);
the second parametr is the problem.
it seems that unsigned int* != uint.
Does anyone have an idea how to call this function form the dll?
http://forums.ni.com/ni/board/message?board.id=60&thread.id=11821
It is the 1st argument that's declared wrong. That throws off the stack frame and prevents the unmanaged code from properly reading the pointer for the 2nd argument. "long" is 64-bits, DDCFileHandle is almost certainly a pointer, 32-bits on a 32-bit operating system.
Change the argument declaration to IntPtr. You'll also need to change the declaration of the function that returns that handle.