I have the following code in win32, which sets a hook in a target application.
void InstallHook(DWORD ThreadId)
{
g_hHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, g_hInstDll, ThreadId);
}
I wish to call this function from C# (.net).
I have this so far:
[DllImport("TheHookDll.dll")]
public extern static void InstallHook(UInt32 ThreadId);
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
and I call it like this:
IntPtr hWnd = FindWindow(null, "MyTargetAppWindowTitle");
UInt32 threadID = GetWindowThreadProcessId(hWnd, IntPtr.Zero);
InstallHook(threadID);
This gives me the handle(hWnd) for the target, and the threadID which is used in the InstallHook function in win32. (It’s just decimals instead of hex)
But I get this error messages:
PInvokeStackImbalance was detected Message: A call to PInvoke function
'TheOperator!TheOperator.Form1::InstallHook' 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 have tried to change the Calling Convention in my dll file (configuration properties -> C/C++ -> All Options -> Calling Convention) from __cdel to __stdcall, but without any luck. (same error)
What am I doing wrong?
I have changed the DWORD to UInt32, because c# do not support DWORD etc. But is that the right way to do it?
Any hints?
Define your PInvoke like this:
[DllImport("TheHookDll.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static void InstallHook(UInt32 ThreadId);
Reason being that when you call this specific function, the caller needs to clean the stack. Without explicitly specifying this in your PInvoke signature, the runtime won't clean the stack and therefore it'll mess the stack up resulting in the error message you've been seeing.
If you don't specify a CallingConvention explicitly, the runtime assumes the function you are trying to call is an StdCall, in which the callee cleans the stack. That's not the case, and the stack will be left all messed up and dirty.
Other than that your signature looks correct; a DWORD is indeed a uint or UInt32 in C#. If that one gets you into trouble, you could try decorating it with the MarshalAs attribute and have the Marshal return it as unmanaged type U8.
Related
I am writing a C# binding for an unmanaged C dll.
The dll provides 5 hooks to return several data:
typedef void (*t_libpd_printhook)(const char *recv);
and exports a field like:
EXTERN t_libpd_printhook libpd_printhook;
now i was using Interop Assistant to generate the binding code, which gave me just a delegate definition:
public delegate void t_libpd_printhook([In] [MarshalAs(UnmanagedType.LPStr)] string recv);
So is there some magic Interop function call i can use, to set the t_libpd_printhook field in the DLL?
You can use LoadLibrary and GetProcAddress to obtain a pointer to the exported libpd_printhook variable. You can then use Marshal.WriteIntPtr and Marshal.GetFunctionPointerForDelegate to assign to the delegate.
[DllImport("kernel32", SetLastError=true, CharSet=CharSet.Unicode)]
static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32", CharSet=CharSet.Ansi, ExactSpelling=true,
SetLastError=true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool FreeLibrary(IntPtr hModule);
.....
IntPtr lib = LoadLibrary(#"mydll.dll");
IntPtr plibpd_printhook = GetProcAddress(lib, "libpd_printhook");
Marshal.WriteIntPtr(plibpd_printhook,
Marshal.GetFunctionPointerForDelegate(mydelegate));
FreeLibrary(lib);
You will want to add the error checking that I excised in the interests of a concise example.
Now, if you are in control of the unmanaged library I would still recommend adding a function to encapsulate writing to this function pointer. That feels like a better interface to me.
PInvoke does not support exported variable. You need to create an unmanaged function which takes your delegate and copies it into the field.
I'm sure I'm missing something obvious in the documentation. I'm calling LoadLibrary from C# and passing in a DLL that isn't there. I'm getting back IntPtr.Zero as I would expect, but when I call Marshal.GetLastWin32Error I always get 0.
Here's the sample.
class Program {
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
private static extern bool FreeLibrary(IntPtr hModule);
static void Main(string[] args) {
IntPtr pDll = LoadLibrary(#"c:\NotThere.dll");
int err = Marshal.GetLastWin32Error();
Console.WriteLine(err);
Console.ReadLine();
}
}
I'm adding code like this to a program that's already failing to load a DLL for no apparent reason. Any idea why I'm not getting an error message?
For errors to get logged so that they can be read by Marshal.GetLastWin32Error(), you need to have SetLastError=true on the DllImport attribute:
[DllImport("kernel32.dll", SetLastError=true)]
private static extern IntPtr LoadLibrary(string dllToLoad);
Your code doesn't set the DllImport.SetLastError attribute flag, which might be why GetLastWin32Error isn't returning anything:
[DllImport("kernel32.dll", SetLastError=true)]
private static extern IntPtr LoadLibrary(string dllToLoad);
To diagnose why modules aren't being loaded you should check Fusion logs (if the module is a managed assembly), and / or dependency walker.
Bear in mind that the load error might be caused because a dependent module could not be loaded (e.g. the VC9 redistributable, on which all C++ modules compiled with the VS2008 compiler depend on), and so even if the module is architecture compatible and located in a searchable directory (such as the application or system directory), the module may still fail to load - Dependency walker is very good at highlighting these sorts of issues.
You have to declare in your DllImport attribute that you want the last error to be captured using the SetLastError field. For example:
[DllImport("kernel32.dll", SetLastError=true)]
private static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll", SetLastError=true)]
private static extern bool FreeLibrary(IntPtr hModule);
Use the following DllImport:
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibrary(string dllToLoad);
try to add this attribute:
[DllImportAttribute("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
from msdn:
Returns the error code returned by the last unmanaged function that was called using platform invoke that has the DllImportAttribute.SetLastError flag set.
I am trying to make a wrapper for a native c++ .dll using P/Invoke.
The source code for the .dll has the following entry point specified:
// .h-file
CHROMAPRINT_API ChromaprintContext *chromaprint_new(int algorithm);
And the method implementation:
// .cpp-file
ChromaprintContext *chromaprint_new(int algorithm)
{
ChromaprintContextPrivate *ctx = new ChromaprintContextPrivate();
ctx->algorithm = algorithm;
ctx->fingerprinter = new Fingerprinter(CreateFingerprinterConfiguration(algorithm));
return (ChromaprintContext *)ctx;
}
The ChromaprintContextPrivate type is a structure:
>// .cpp-file
struct ChromaprintContextPrivate {
int algorithm;
Fingerprinter *fingerprinter;
vector<int32_t> fingerprint;
};
My C# wrapper code:
// .cs-file
[System.Runtime.InteropServices.DllImportAttribute(
"libchromaprint.dll",
EntryPoint = "chromaprint_new")]
private static extern System.IntPtr chromaprint_new(int algorithm);
public static IntPtr Chromaprint_New(ChromaprintAlgorithm algorithm)
{
// Hardcoded parameter for testing
return chromaprint_new(0); // (int)algorithm
}
Calling IntPtr ptr = Chromaprint_New(0); raises the following MDA exception:
A call to PInvoke function 'MyProject.ChromaprintWrapper!'MyProject.ChromaprintWrapper.LibChromaPrint::chromaprint_new' 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.
So I understand what the problem is (the number of entries on the stack isn't what is expected). I'm assuming the method parameter int algorithm is ok. I'm not sure about the return type though. Should it be a structure instead of a pointer?
I got the C# code above by running the .h-file through P/Invoke Interop Assistant. Is the return type wrong? what should it be?
What is the C# representation of vector<int32_t> fingerprint;?
(See ChromaprintContextPrivate structure above.)
You most likely need to specify the calling convention.
Try the following:
[System.Runtime.InteropServices.DllImportAttribute("libchromaprint.dll",
EntryPoint = "chromaprint_new",
CallingConvention=CallingConvention.Cdecl)]
By default, this uses Winapi (which is effectively StdCall) to make it easier to call into the Windows API, but this is not typically the default on most C++ libraries.
It's because of how the parameters are actually passed, the calling convention.
Try
[DllImport("libchromaprint.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr chromaprint_new(int algorithm);
You need to declare cdecl calling convention on the dllimport attribute.
While working with WinAPI, I decided to implement a call to GetProcessAfinityMask in my C# application. However, I've seen two different signatures for this function.
One of them uses SafeProcessHandle for the handle:
[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool GetProcessAffinityMask(SafeProcessHandle handle, out IntPtr processMask, out IntPtr systemMask);
The other possible version (on P/Invoke) uses IntPtr:
[DllImport("kernel32.dll",SetLastError = true)]
static extern bool GetProcessAffinityMask(IntPtr hProcess,
out UIntPtr lpProcessAffinityMask, out UIntPtr lpSystemAffinityMask);
Given that both functions return the same values, what is the difference between passing a SafeProcessHandle instance or IntPtr?
Safe Handles and Critical Finalization MSDN Article is describing this difference.
I saw a method called Control.FromHandle which (should) give you the access to it.
Now, I wanted to try it using this code
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("user32.dll")]
private static extern bool ReleaseDC(IntPtr hwnd, IntPtr hdc);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
IntPtr ptr = FindWindowByCaption(IntPtr.Zero, "Download");
Control f = Control.FromHandle(ptr);
f.Text = "Something";
}
but it won't, obviously, work.
I checked personally that the handle is correct... but the method returns a null control.
Any explaining?
This method only works if the handle you pass in actually is a Control in your application.
In your particular case, since you just want to set the text, call SetWindowText from user32.dll
For anyone else googling that found this answer and wondered why this behavior is the case this post http://www.codeguru.com/forum/showthread.php?t=443191 is particularly enlightening, specifically the last post from 'MadHatter':
well just from looking over what goes on in Control.FromHandle in reflector, it looks like as windows are added to the .net world, it stores a table of what handles it has loaded, the problem comes when you pass in a handle that is not listed in its tables. There may be some hack that would allow you to register the window through all the subsystems that are used when creating windows from within the .net app, but it would probably be better / more consistent to wrap whatever functionality you need directly through the windows api then try to hack in Control.FromHandle to allow you to access / manipulate the window of some other process.
Reading more into your question it seems like you are trying to do some automation or at very least manipulate the window in some way. Might I recommend looking at the Managed Windows API project on SourceForge. It is pretty well written and we've used it for the purposes you are describing.