At the moment, I can get a list of running processes with a main window using System.Diagnostics.Process.GetProcesses() and executing a simple LINQ query.
Then, I can import user32.dll and the SetWindowPos function and I manipulate other processes' window parameters.
Ok, it works. Now I'd like to select a window of a process, let's say calc.exe, by clicking it. In other words, I'd like to obtain a Process object (and then the MainWindowHandle) with a hook that catches the process name when I click on its window.
How can I achieve this?
I don't know how this is done in C#, but you have also tagged this question WinAPI so I can help there. In WinAPI, it can be done like so:
#include <stdio.h>
#include <Windows.h>
#include <Psapi.h>
#pragma comment(lib, "Psapi.lib")
int main(void)
{
/* Hacky loop for proof of concept */
while(TRUE) {
Sleep(100);
if(GetAsyncKeyState(VK_F12)) {
break;
}
if(GetAsyncKeyState(VK_LBUTTON)) {
HWND hwndPt;
POINT pt;
if(!GetCursorPos(&pt)) {
wprintf(L"GetCursorPos failed with %d\n", GetLastError());
break;
}
if((hwndPt = WindowFromPoint(pt)) != NULL) {
DWORD dwPID;
HANDLE hProcess;
GetWindowThreadProcessId(hwndPt, &dwPID);
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, dwPID);
if(hProcess == NULL) {
wprintf(L"OpenProcess failed with error: %d\n", GetLastError());
} else {
wchar_t lpFileName[MAX_PATH];
DWORD dwSize = _countof(lpFileName);
QueryFullProcessImageName(hProcess, 0, lpFileName, &dwSize);
wprintf(L"%s\n", lpFileName);
CloseHandle(hProcess);
}
}
}
}
return EXIT_SUCCESS;
}
Example result:
In this case, I am simply polling to get the mouse click. A more proper way would be to use some sort of windows hook.
As Mike Kwan said, you'll be better of writing a hook though both approaches have their own drawbacks, but bjarneds already did a good work on this. Have a look # DotNET Object Spy. It's written in C# and will serve for your needs and more.
You should also note that using hooks are becoming redundant by the day. Depending on what you want to do, other winapis like GetForegroundWindow might serve better.
Related
I want to read the Device Descriptor of a USB Serial port (not just the VID/PID -- mainly so I can get the bcdDevice, iProduct, and iManufacturer) so I can know if the USB serial port is capable of what I want it to do (we have a couple of modems in the field, and one of them is much more reliable at half-duplex).
I don't want to use libusbdotnet because I don't want my users to have to figure out ahead of time what to filter for to get the ports I'm interested in.
I see how to do it with C; looking on the USBView application, we're going to need to use the SetupAPI to enumerate the controllers (SetupDiEnumDeviceInfo), then the hubs, and then query the hubs with IOCTL to get what is on each port.
I think I have to do this this way because I can't figure out a mapping from COM13 -> \.\USB#ROOT_HUB30#4&2a9d917a&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}.. is there a better/easier way to get the filename I need for the ioctl, rather than go through them all and filter them?
I've also tried making the usbview application into a C DLL and tried to make a wrapper around that, but I can't get my homegrown DLL to compile properly...
I tried adding this to the enum.c from usbview just as a starting point
typedef struct
{
PSTR Name;
PSTR path;
int port;
} USBDevice;
USBDevice USBDevices[255] = { 0 };
int ndevices;
extern __declspec(dllexport) int GetDevices(PSTR filter)
{
//Enumerate it all and filter for "COM%"
//Store the data in the USBDevice array.
}
extern __declspec(dllexport) PSTR DeviceName(int i)
{
return USBDevices[i].Name;
}
extern __declspec(dllexport) PSTR DevicePath(int i)
{
return USBDevices[i].path;
}
extern __declspec(dllexport) void FreeList(void)
{
char* foo;
int i;
for (i = 0; i<ndevices; i++)
{
free(USBDevices[i].Name);
free(USBDevices[i].path);
}
ndevices = 0;
}
extern __declspec(dllexport) PUSB_COMMON_DESCRIPTOR GetDevuceInfo(PSTR path, int port)
{
//I didn't want to figure out how to marshal this.
}
extern __declspec(dllexport) PSTR GetDevuceInfoValue(PSTR path,int port, int selector)
{
//Open Hub, send IOCTL to it, get data, filter that, return the interesting bit.
}
But I can't load the resulting DLL into my C# project (it crashes when run - even before the window appears). Looking at the DLL, it appears that it is garbage (walking the DLL causes the walker to crash). so... I'm not sure what I'm doing wrong there, so I was going to translate the enum.c into C# pinvoke-style and roll with that, but it seems like a lot of work, especially if there's a simpler way to get the COM14 -> \.\USB#ROOT_HUB30#4&2a9d917a&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8} [Port1] link...
So... Is there a lot of work ahead of me, or is there a simpler way to get the linkage (I've tried from the registry, but it doesn't seem to have the mapping).
It looks like that is the approved way (EnumerateDevices, CM_GetParent, Munge it to a valid path, then ask the Hub to get the data). I've made a C program to do the dirty work and it works like a charm. I think I'll wrap it in a DLL and post it to CodeProject.
Warning though... even if you do a DeviceIOControl, windows does not re-query your device. It only queries your device at Plug-In.
But at least you can find the "Manufacturer String" so you can tell your USB port from all the similar ones that are plugged in. Then again, you can get that from SetupAPI, so... I don't know what it really buys you.
Why does the following code sometimes causes an Exception with the contents "CLIPBRD_E_CANT_OPEN":
Clipboard.SetText(str);
This usually occurs the first time the Clipboard is used in the application and not after that.
This is caused by a bug/feature in Terminal Services clipboard (and possible other things) and the .NET implementation of the clipboard. A delay in opening the clipboard causes the error, which usually passes within a few milliseconds.
The solution is to try multiple times within a loop and sleep in between.
for (int i = 0; i < 10; i++)
{
try
{
Clipboard.SetText(str);
return;
}
catch { }
System.Threading.Thread.Sleep(10);
}
Actually, I think this is the fault of the Win32 API.
To set data in the clipboard, you have to open it first. Only one process can have the clipboard open at a time. So, when you check, if another process has the clipboard open for any reason, your attempt to open it will fail.
It just so happens that Terminal Services keeps track of the clipboard, and on older versions of Windows (pre-Vista), you have to open the clipboard to see what's inside... which ends up blocking you. The only solution is to wait until Terminal Services closes the clipboard and try again.
It's important to realize that this is not specific to Terminal Services, though: it can happen with anything. Working with the clipboard in Win32 is a giant race condition. But, since by design you're only supposed to muck around with the clipboard in response to user input, this usually doesn't present a problem.
I know this question is old, but the problem still exists. As mentioned before, this exception occurs when the system clipboard is blocked by another process. Unfortunately, there are many snipping tools, programs for screenshots and file copy tools which can block the Windows clipboard. So you will get the exception every time you try to use Clipboard.SetText(str) when such a tool is installed on your PC.
Solution:
never use
Clipboard.SetText(str);
use instead
Clipboard.SetDataObject(str);
I solved this issue for my own app using native Win32 functions: OpenClipboard(), CloseClipboard() and SetClipboardData().
Below the wrapper class I made. Could anyone please review it and tell if it is correct or not. Especially when the managed code is running as x64 app (I use Any CPU in the project options). What happens when I link to x86 libraries from x64 app?
Thank you!
Here's the code:
public static class ClipboardNative
{
[DllImport("user32.dll")]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll")]
private static extern bool CloseClipboard();
[DllImport("user32.dll")]
private static extern bool SetClipboardData(uint uFormat, IntPtr data);
private const uint CF_UNICODETEXT = 13;
public static bool CopyTextToClipboard(string text)
{
if (!OpenClipboard(IntPtr.Zero)){
return false;
}
var global = Marshal.StringToHGlobalUni(text);
SetClipboardData(CF_UNICODETEXT, global);
CloseClipboard();
//-------------------------------------------
// Not sure, but it looks like we do not need
// to free HGLOBAL because Clipboard is now
// responsible for the copied data. (?)
//
// Otherwise the second call will crash
// the app with a Win32 exception
// inside OpenClipboard() function
//-------------------------------------------
// Marshal.FreeHGlobal(global);
return true;
}
}
Actually there could be another issue at hand. The framework call (both the WPF and winform flavors) to something like this (code is from reflector):
private static void SetDataInternal(string format, object data)
{
bool flag;
if (IsDataFormatAutoConvert(format))
{
flag = true;
}
else
{
flag = false;
}
IDataObject obj2 = new DataObject();
obj2.SetData(format, data, flag);
SetDataObject(obj2, true);
}
Note that SetDataObject is always called with true in this case.
Internally that triggers two calls to the win32 api, one to set the data and one to flush it from your app so it's available after the app closes.
I've seen several apps (some chrome plugin, and a download manager) that listen to the clipboard event. As soon as the first call hits, the app will open the clipboard to look into the data, and the second call to flush will fail.
Haven't found a good solution except to write my own clipboard class that uses direct win32 API or to call setDataObject directly with false for keeping data after the app closes.
Use the WinForms version (yes, there is no harm using WinForms in WPF applications), it handles everything you need:
System.Windows.Forms.Clipboard.SetDataObject(yourText, true, 10, 100);
This will attempt to copy yourText to the clipboard, it remains after your app exists, will attempt up to 10 times, and will wait 100ms between each attempt.
Ref. https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.clipboard.setdataobject?view=netframework-4.7.2#System_Windows_Forms_Clipboard_SetDataObject_System_Object_System_Boolean_System_Int32_System_Int32_
This happen to me in my WPF application. I got OpenClipboard Failed (Exception from HRESULT: 0x800401D0 (CLIPBRD_E_CANT_OPEN)).
i use
ApplicationCommands.Copy.Execute(null, myDataGrid);
solution is to clear the clipboard first
Clipboard.Clear();
ApplicationCommands.Copy.Execute(null, myDataGrid);
The difference between Cliboard.SetText and Cliboard.SetDataObject in WPF is that the text is not copied to the clipboard, only the pointer. I checked the source code. If we call SetDataObject(data, true) Clipoard.Flush() will also be called. Thanks to this, text or data is available even after closing the application. I think Windows applications only call Flush() when they are shutting down. Thanks to this, it saves memory and at the same time gives access to data without an active application.
Copy to clipboard:
IDataObject CopyStringToClipboard(string s)
{
var dataObject = new DataObject(s);
Clipboard.SetDataObject(dataObject, false);
return dataObject;
}
Code when app or window is closed:
try
{
if ((clipboardData != null) && Clipboard.IsCurrent(clipboardData))
Clipboard.Flush();
}
catch (COMException ex) {}
clipboardData is a window class field or static variable.
That's not a solution, just some additional information on how to reproduce it when all solutions work on your PC and fail somewhere else. As mentioned in the accepted answer - clipboard can be busy by some other app. You just need to handle this failure properly, to explain user somehow why it does not work.
So, just create a new console app with few lines below and run it. And while it is running - test your primary app on how it is handles busy clipboard:
using System;
using System.Runtime.InteropServices;
namespace Clipboard
{
class Program
{
[DllImport("user32.dll")]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll")]
private static extern bool CloseClipboard();
static void Main(string[] args)
{
bool res = OpenClipboard(IntPtr.Zero);
Console.Write(res);
Console.Read();
CloseClipboard();
}
}
}
I've been working on an application that requires monitoring thread specific mouse activity (WH_MOUSE) on another process and encountered something very curious.
After finding out that this is not possible via exclusively managed code if I don't want to use WH_MOUSE_LL and that I'd need a native DLL export to inject itself in the target process, I set out and created it in C++ according to what scattered documentation I could find on the subject and then tried using it to hook into Notepad.
Although according to GetLastWin32Error the injection succeeded, I was not getting notified of mouse events. After nearly giving up and going for the low level global hook option, I re-read the "Remarks" section of this article which made me suspect that the problem may be because of the "bitness" of my code vs notepad:
A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit
DLL cannot be injected into a 32-bit process. If an application
requires the use of hooks in other processes, it is required that a
32-bit application call SetWindowsHookEx to inject a 32-bit DLL into
32-bit processes, and a 64-bit application call SetWindowsHookEx to
inject a 64-bit DLL into 64-bit processes.
However, both my native DLL and managed application were compiled as x64, and I was trying to hook into the 64-bit version of notepad, so it should've worked fine, but I took a shot in the dark anyway and went into the SysWOW64 folder and opened the 32-bit Notepad from there, tried hooking in again and this time the hook worked beautifully!
Curiously, I then recompiled both my native DLL and managed app as x86 and tested it it against the 32-bit Notepad and it didn't work, but it worked on my normal 64-bit Notepad!
How am I possibly seem to be able to inject a 32-bit DLL into a 64-bit process and vice versa!
Although my original problem has been solved and I can continue with my app's development, the curiosity as to why I'm observing this strange inverse behavior from SetWindowsHookEx is driving me insane, so I really hope someone will be able to shed some light on this.
I know this a lot of talk and no code, but the code for even a sample app is rather large and comes in both managed and unmanaged flavors, however I'll promptly post any piece of the code you think might be relevant.
I've also created a sample app so you can test this behavior yourself. It's a simple WinForms app that tries to hook into Notepad and displays its mouse events:
http://saebamini.com/HookTest.zip
It contains both an x86 version and an x64 version. On my machine (I'm on a 64-bit Windows 7), the x86 version only works with 64-bit Notepad, and the x64 version only works with 32-bit Notepad (from SysWOW64).
UPDATE - Relevant Bits of Code:
C# call to the unmanaged library:
public SetCallback(HookTypes type)
{
_type = type;
_processHandler = new HookProcessedHandler(InternalHookCallback);
SetCallBackResults result = SetUserHookCallback(_processHandler, _type);
if (result != SetCallBackResults.Success)
{
this.Dispose();
GenerateCallBackException(type, result);
}
}
public void InstallHook()
{
Process[] bsProcesses = Process.GetProcessesByName("notepad");
if(bsProcesses.Length == 0)
{
throw new ArgumentException("No open Notepad instance found.");
}
ProcessThread tmp = GetUIThread(bsProcesses[0]);
if (!InitializeHook(_type, tmp.Id))
{
throw new ManagedHooksException("Hook initialization failed.");
}
_isHooked = true;
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, IntPtr procid);
// 64-bit version
[DllImport("SystemHookCore64.dll", EntryPoint = "InitializeHook", SetLastError = true,
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.Cdecl)]
private static extern bool InitializeHook(HookTypes hookType, int threadID);
[DllImport("SystemHookCore64.dll", EntryPoint = "SetUserHookCallback", SetLastError = true,
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.Cdecl)]
private static extern SetCallBackResults SetUserHookCallback(HookProcessedHandler hookCallback, HookTypes hookType);
C++:
HookProc UserMouseHookCallback = NULL;
HHOOK hookMouse = NULL;
HINSTANCE g_appInstance = NULL;
MessageFilter mouseFilter;
bool InitializeHook(UINT hookID, int threadID)
{
if (g_appInstance == NULL)
{
return false;
}
if (hookID == WH_MOUSE)
{
if (UserMouseHookCallback == NULL)
{
return false;
}
hookMouse = SetWindowsHookEx(hookID, (HOOKPROC)InternalMouseHookCallback, g_appInstance, threadID);
return hookMouse != NULL;
}
}
int SetUserHookCallback(HookProc userProc, UINT hookID)
{
if (userProc == NULL)
{
return HookCoreErrors::SetCallBack::ARGUMENT_ERROR;
}
if (hookID == WH_MOUSE)
{
if (UserMouseHookCallback != NULL)
{
return HookCoreErrors::SetCallBack::ALREADY_SET;
}
UserMouseHookCallback = userProc;
mouseFilter.Clear();
return HookCoreErrors::SetCallBack::SUCCESS;
}
return HookCoreErrors::SetCallBack::NOT_IMPLEMENTED;
}
int FilterMessage(UINT hookID, int message)
{
if (hookID == WH_MOUSE)
{
if(mouseFilter.AddMessage(message))
{
return HookCoreErrors::FilterMessage::SUCCESS;
}
else
{
return HookCoreErrors::FilterMessage::FAILED;
}
}
return HookCoreErrors::FilterMessage::NOT_IMPLEMENTED;
}
static LRESULT CALLBACK InternalMouseHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
if (code < 0)
{
return CallNextHookEx(hookMouse, code, wparam, lparam);
}
if (UserMouseHookCallback != NULL && !mouseFilter.IsFiltered((int)wparam))
{
UserMouseHookCallback(code, wparam, lparam);
}
return CallNextHookEx(hookMouse, code, wparam, lparam);
}
My best guess about your problem :
The Windows hook system is able to hook both 32-bit and 64-bit application, from any bitness. The thing is, as you pointed, you can't inject a DLL into an application with the wrong bitness. To make this work, Windows will normally inject the DLL if it can, but if it can't, it will setup a callback that use the hooking application message loop. Since the message loop is handled by the OS, it is used to make a call from different bitness.
In your case, the only thing that work is the message loop way. And there is a good reason for that : your 64-to-64 and 32-to-32 calls have no chance to succeed, because the hook is in the injected DLL, that is, in a different process than your application.
Nothing happens in your case because your UserMouseHookCallback stay to NULL. Indeed, the call to SetUserHookCallback() is done in the application DLL instance, but UserMouseHookCallback is unchanged in the target DLL instance. Once injected, the DLL is in a different process, and should be considered as such. You have to find another way to call back the application (maybe post a message, like in the 32-to-64 case, and/or make use of shared sections).
To test this, put something like MessageBox() in InternalMouseHookCallback(). The box should appear even in 64-to-64 and 32-to-32.
A company produces a device with an ethernet connector. I have one of their devices and they gave me a .lib file and some .h files so I can connect and communicate with their device. Works fine, I'm glad.
Then the company produced a new version of their device. I also have this newer device and again they gave me some files so I can connect and communicate with it: this time a .lib file, a .dll file and some .h files.
So much for the background. Now, the problem:
I wrote a simple test application in MS Visual C++ 6.0 to see what happens, when I try to connect to the new device with the old interface: the connect() method provided (using the old .lib file) immediately returns 0 (Could not connect). Makes sense, I agree!
But, when I wrap that .lib file in a .dll file and try the same scenario again, I don't get an immediate result, it takes 42 seconds until I get the 0 result!
The wrapper works fine when I try to connect to the older device, no problem at all.
This is the first project where I have to wrap up a .lib file in a .dll file (I did that so I can use it in C#), so I don't know too much about this topic. All I know is, that I'm passing along values from the original method calls to the wrapper ... How can there be such a delay? It must happen somewhere in the Wrapping, but I have no clue. Networking shouldn't be the problem, because without wrapper I get the result immediately. Maybe something about Threading or DLLs?
I was hoping someone could enlighten me here ...
I didn't post any specific code, because I wouldn't know what is relevant here. If you need more information, please let me know!
EDIT:
In C# I have a class MyDLLImport:
class MyDLLImport
{
[DllImport("Old_Interface_MSVC++GeneratedDLL.dll")]
public static extern int Connect();
[DllImport("Old_Interface_MSVC++GeneratedDLL.dll")]
public static extern int Disconnect();
...
}
In another C# class I access these methods directly, like:
MyDLLImport.Connect();
The .lib and .h files I wrapped up in the Old_Interface_MSVC++GeneratedDLL.dll file like this:
Old_Interface_MSVC++GeneratedDLL.cpp:
#include "stdafx.h"
#include "Old_Interface_MSVC++GeneratedDLL_Class.h"
#include "Old_Interface_MSVC++GeneratedDLL.h"
#include <string.h>
#include <iostream>
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
ALibClass MyDLL::client;
int MyDLL::Connect()
{
int ret;
ret = MyDLL::client.Connect();
if(!ret)
{
std::cerr << "Could not connect" << std::endl;
return false;
}
return true;
}
void MyDLL::Disconnect()
{
MyDLL::client.Disconnect();
}
...
/////////////////////////////////
// exported functions
int Connect()
{
return MyDLL::Connect();
}
void Disconnect()
{
MyDLL::Disconnect();
}
Then, there is the header file for the previous .cpp file, Old_Interface_MSVC++GeneratedDLL.h:
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
// added by myself to prevent export warnings
#pragma warning( disable: 4251 )
/////////////////////////////////////////////
//exported general methods
extern "C" MYDLL_API int Connect();
extern "C" MYDLL_API void Disconnect();
...
And finally the class header file Old_Interface_MSVC++GeneratedDLL_Class.h:
#include "lib_header_file.h"
class MyDLL
{
private:
static ALibClass client;
public:
static int Connect();
static void Disconnect();
...
}
I hope I have added all relevant code parts. If you need more information, let me know!
We are hooking TextOut(),ExtTextOut() and DrawText() methods GLOBALLY .
i.e.
hhook = SetWindowsHookEx(WH_CBT, function_address, module_handle, 0);
But we want to exclude our application (which we are using to install/uninstall hook) from being hooked. If the last argument to SetWindowsHookEx() is 0(zero) it will hook all the existing threads.How to check here if the current thread is "OurApplication.exe" and then exclude it from hooking or immediately unhook it.
Please provide help.
I don't think it's possible. You either hook to everything or to a specific thread.
Why don't you just filter out your application in whatever code yout have at function_address? Most, if not all, CBT hook callbacks provide window handle at either wParam or lParam argument. You can then get process id from that handle and compare it to your application pid.
Off the top of my head:
Pass the hook dll the PID of the process you want to ignore when you install the hook. Make sure that PID is stored in a shared section so all hook instances see the same value.
In your hook function, check to see if the current process PID matches the one passed in. If it does, don't do your hooky stuff, just pass to CallNextHookEx.
I don't like this because it adds to work done in the hook function, which is always bad. But it seems like it should work in principle.
Thank you experts for replying to our question. We found the way to do that.
Now we added the following block of code in the entry point of the injecting dll.And it is working fine.
BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD dwReason, PVOID lpReserved)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
IsDebuggerPresent();
// Exclude the "someapplication.exe" from hooking
GetModuleFileName( GetModuleHandle( NULL ),Work,sizeof(Work) );
PathStripPath(Work );
if ( _stricmp( Work, "someapplication.exe" ) != 0 )
{
InstallWindowHooks();
}
break;
case DLL_PROCESS_DETACH:
hWindowReceiver = NULL;
CleanUp();
break;
}
return TRUE;
}