I am using Detours on my project in order to save the GL Context of a Window.
So I have followed the code sample that comes with Detours 3.0 Express Edition:
static BOOL (WINAPI * trueWglMakeCurrent)(HDC, HGLRC) = wglMakeCurrent;
BOOL WINAPI hookedWglMakeCurrent(HDC hdc, HGLRC hglrc);
BOOL WINAPI hookedWglMakeCurrent(HDC hdc, HGLRC hglrc)
{
wContext = hglrc;
wDC = hdc;
return trueWglMakeCurrent(hdc, hglrc); //CRASH HERE
//return TRUE;
}
But it crashes when invoking the original WGL method. If I change that return to return TRUE, it won't crash. But it obviously won't render anything either.
hdc and hglrc have valid addresses and they probably correspond to the GL Context I need, since this is called right after the desired Window creation.
EDIT
It does not crash when applying the same method to other OpenGL functions, e.g. SwapBuffers, glFinish, etc.
In the case of an isolated test in which I simply load my DLL and execute the wglMakeCurrent it works.
In the case of injecting my DLL into an application and then using wglMakeCurrent on this application, Detours causes an infinite recursion on the trueWglMakeCurrent call.
The problem was the infinite recursion that was being caused by not linking the functions properly.
Detours creates a function pointer to the original wglMakeCurrent, which in my case I stored in trueWglMakeCurrent. However it works only if I declare it and use it on the same file that has the DLLMain and the DetourAPI usage. Exactly like the example:
static BOOL (WINAPI * trueWglMakeCurrent)(HDC,HGLRC) = wglMakeCurrent;
GTNSPECTRA_C_EXPORT BOOL WINAPI hookedWglMakeCurrent(HDC hDC, HGLRC hRC)
{
...
return trueWglMakeCurrent(hDC, hRC);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD reason,
LPVOID lpReserved)
{
...
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)trueWglMakeCurrent, hookedWglMakeCurrent);
DetourTransactionCommit();
...
return TRUE;
}
Related
I want to write a program that needs sometimes to start processes of another applications (mainly Sumatra PDF) on Windows 10, version 1803 (April 2018 Update).
These applications should be started on a specific monitor. I also want to be able to close the processes when needed.
The preferred languages are C# and Java, but any help is appreciated.
EDIT
I've tried to use the ShellExecuteExW function suggested by IInspectable in C++ code directly, but it doesn't work, as applications appear on the main monitor. I must have certainly made a mistake as I am absolutely new to WinAPI and know very little C++.
#include <Windows.h>
HMONITOR monitors[2]; // As it's only a test and I have currently only two monitors.
int monitorsCount = 0;
BOOL CALLBACK Monitorenumproc(HMONITOR hMonitor, HDC hdc, LPRECT lprect, LPARAM lparam)
{
monitors[monitorsCount] = hMonitor;
monitorsCount++;
return TRUE;
}
int main()
{
EnumDisplayMonitors(NULL, NULL, Monitorenumproc, 0);
_SHELLEXECUTEINFOW info;
ZeroMemory(&info, sizeof(info));
info.cbSize = sizeof(info);
info.fMask = SEE_MASK_HMONITOR;
//info.lpVerb = L"open";
info.lpFile = L"C:\\Windows\\System32\\cmd.exe";
info.nShow = SW_SHOW;
info.hMonitor = monitors[1]; // Trying to start on the second monitor.
ShellExecuteExW(&info);
return 0;
}
As suggested by others, this is intended behavior of Windows and for the good reasons.
Also, you can not rely on default window placement atleast for SumatraPDF, since it surely does not use CW_USEDEFAULT, and instead stores these values in :
%USERPROFILE%\AppData\Roaming\SumatraPDF\SumatraPDF-settings.txt
There are multiple options though:
Use third party tools that monitor top level windows and based on pre-configured rules moves them to specified display. E.g. DisplayFusion, etc.
Use ligher weight solutions like AutoHotkey/AutoIt.
Try and do this in code itself. Following is a working solution. I smoke tested on my box.
Disclaimer: I have not written the entire code, for saving time I pulled it up from couple of sources, tweaked it, glued it together, and tested with SumantraPDF. Do also note, that this code is not of highest standards, but solves your problem, and will act as a can-be-done example.
C++ code: (scroll down for C# code)
#include <Windows.h>
#include <vector>
// 0 based index for preferred monitor
static const int PREFERRED_MONITOR = 1;
struct ProcessWindowsInfo
{
DWORD ProcessID;
std::vector<HWND> Windows;
ProcessWindowsInfo(DWORD const AProcessID)
: ProcessID(AProcessID)
{
}
};
struct MonitorInfo
{
HMONITOR hMonitor;
RECT rect;
};
BOOL WINAPI EnumProcessWindowsProc(HWND hwnd, LPARAM lParam)
{
ProcessWindowsInfo *info = reinterpret_cast<ProcessWindowsInfo*>(lParam);
DWORD WindowProcessID;
GetWindowThreadProcessId(hwnd, &WindowProcessID);
if (WindowProcessID == info->ProcessID)
{
if (GetWindow(hwnd, GW_OWNER) == (HWND)0 && IsWindowVisible(hwnd))
{
info->Windows.push_back(hwnd);
}
}
return true;
}
BOOL CALLBACK Monitorenumproc(HMONITOR hMonitor, HDC hdc, LPRECT lprect, LPARAM lParam)
{
std::vector<MonitorInfo> *info = reinterpret_cast<std::vector<MonitorInfo>*>(lParam);
MonitorInfo monitorInfo = { 0 };
monitorInfo.hMonitor = hMonitor;
monitorInfo.rect = *lprect;
info->push_back(monitorInfo);
return TRUE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
// NOTE: for now this code works only when the window is not already visible
// could be easily modified to terminate existing process as required
SHELLEXECUTEINFO info = { 0 };
info.cbSize = sizeof(info);
info.fMask = SEE_MASK_NOCLOSEPROCESS;
info.lpFile = L"C:\\Program Files\\SumatraPDF\\SumatraPDF.exe";
info.nShow = SW_SHOW;
std::vector<MonitorInfo> connectedMonitors;
// Get all available displays
EnumDisplayMonitors(NULL, NULL, Monitorenumproc, reinterpret_cast<LPARAM>(&connectedMonitors));
if (ShellExecuteEx(&info))
{
WaitForInputIdle(info.hProcess, INFINITE);
ProcessWindowsInfo Info(GetProcessId(info.hProcess));
// Go though all windows from that process
EnumWindows((WNDENUMPROC)EnumProcessWindowsProc, reinterpret_cast<LPARAM>(&Info.ProcessID));
if (Info.Windows.size() == 1)
{
// only if we got at most 1 window
// NOTE: applications can have more than 1 top level window. But at least for SumtraPDF this works!
if (connectedMonitors.size() >= PREFERRED_MONITOR)
{
// only move the window if we were able to successfully detect available monitors
SetWindowPos(Info.Windows.at(0), 0, connectedMonitors.at(PREFERRED_MONITOR).rect.left, connectedMonitors.at(PREFERRED_MONITOR).rect.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
}
CloseHandle(info.hProcess);
}
return 0;
}
To emphasize one of my comments in code. This code will only work if the process in question is not already running. You can tweak the code as per your requirements otherwise.
Update: Added C# code below, as I realized OP prefers C#. This code also has the termination logic cooked in.
C# code:
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOZORDER = 0x0004;
private const int PREFERRED_MONITOR = 1;
static void Main(string[] args)
{
// NOTE: you will have to reference System.Windows.Forms and System.Drawing (or
// equivalent WPF assemblies) for Screen and Rectangle
// Terminate existing SumatraPDF process, else we will not get the MainWindowHandle by following method.
List<Process> existingProcesses = Process.GetProcessesByName("SumatraPDF").ToList();
foreach (var existingProcess in existingProcesses)
{
// Ouch! Ruthlessly kill the existing SumatraPDF instances
existingProcess.Kill();
}
// Start the new instance of SumantraPDF
Process process = Process.Start(#"C:\Program Files\SumatraPDF\SumatraPDF.exe");
// wait max 5 seconds for process to be active
process.WaitForInputIdle(5000);
if (Screen.AllScreens.Length >= PREFERRED_MONITOR)
{
SetWindowPos(process.MainWindowHandle,
IntPtr.Zero,
Screen.AllScreens[PREFERRED_MONITOR].WorkingArea.Left,
Screen.AllScreens[PREFERRED_MONITOR].WorkingArea.Top,
0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
}
SEE_MASK_HMONITOR is only a request. Applications can choose their own window placement. SEE_MASK_HMONITOR only works when the executed application relies on default window placement, i.e. it creates its first top-level window with CW_USEDEFAULT
So what you want is not generally possible. Your code is as good as you can get, if you don't control the launched application.
I'm trying to create an application that will be notified about each active window change in Windows so it could do some tasks like detecting window titles, therefore "punishing" bad people accessing bad content on our PC machines. So, this is really important for the application because it's purpose is to log "bad" applications from history.
So, in my main function, I started a thread for my WindowLogger.
windowThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) WindowLogger,
(LPVOID) argv[0], 0, NULL );
if (windowThread)
{
// Also a bit of protection here..
return WaitForSingleObject(windowThread, INFINITE);
}
Then, here is my WindowLogger procedure:
// Function called by main function to install hook
DWORD WINAPI
WindowLogger(LPVOID lpParameter)
{
HHOOK hWinHook;
HINSTANCE hExe = GetModuleHandle(NULL);
if (!hExe)
{
return 1;
}
else
{
hWinHook = SetWindowsHookEx(WH_CBT, (HOOKPROC) CBTProc, hExe, 0);
MSG msg;
// I AM UNSURE ABOUT THIS PART..
// Probably wrong code :D ..
while (GetMessage(&msg, NULL, 0, 0) != 0)
{
if (msg.message == HCBT_ACTIVATE) {
// my code to log the window name
}
}
UnhookWindowsHookEx(hWinHook);
}
return 0;
}
And finally, my CBTProc callback function, it logs the windows using my log() function:
LRESULT CALLBACK
CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
switch (nCode)
{
case HCBT_ACTIVATE:
{
HWND foreground = GetForegroundWindow();
char window_title[50];
if (foreground)
GetWindowText(foreground, window_title, 25);
log("|");
log(&window_title[0]);
log("|");
}
}
}
So I had debugged the program and what I figured out is that hWinHook becomes NULL after SetWindowsHookEx() -- this is what probably causes my program to mailfunction..
.. Can you help me out with this?
Thanks in advance.
Passing 0 for the dwThreadId parameter to SetWindowsHookEx is used to register a hook for all threads in the system, i.e. a global hook. However to do this, your hook code needs to be located within a DLL (so that the DLL can be mapped into the address space of other processes). Since your hook code is in your main executable rather than a DLL the call is failing.
I have a fairly complex requirement. My STA COM object is implemented in a DLL (can't move it to out-of-process EXE). By the means of DllSurrogate I am hosting my object in a dllhost.exe process. My object has an UI attached to it (a plain modeless dialog) but I need the PreTranslateAccelerator mechanism in order for some shortcuts to work, etc. Since COM activates my object and hosts it in the default dllhost.com, I am obviously not controlling the message pump.
Is there still a way to pre-translate messages in this scenario? I doubt COM has foreseen such a specific scenario but maybe I am missing something.
Okay here it is. I hope I didn't leave out anything important. Basically, I have created a custom CMyComCreator instead of the default one. Instead of just creating a COM object and returning an interface pointer, I spin a worker UiThread. I use MyData structure to pass data across threads. Once the worker thread has finished setting up, I use the CComGITPtr to transfer the marshalled interface pointer from the UiThread back to the main. The consumers (out-of-process) end up with interface pointers that talk directly to the UiThread bypassing the main thread. You may think of CMyDialog as a modeless dialog which sends a PostQuitMessage on destruction to terminate the message loop. That's all. May look cumbersome but it works good.
struct MyData
{
ATL::CComGITPtr<IUnknown> Unk;
ATL::CEvent Event;
HRESULT hr;
MyData() : hr(E_OUTOFMEMORY), Event(FALSE, FALSE) { }
};
static CMessageLoop * MessageLoop;
class CMyComCreator
{
public:
static HRESULT WINAPI CreateInstance(
_In_opt_ void* pv,
_In_ REFIID riid,
_COM_Outptr_ LPVOID* ppv)
{
ATLASSERT(ppv != NULL);
if (ppv == NULL)
return E_POINTER;
*ppv = NULL;
HRESULT hRes = E_OUTOFMEMORY;
MyData* data = NULL;
ATLPREFAST_SUPPRESS(6014 28197)
/* prefast noise VSW 489981 */
ATLTRY(data = _ATL_NEW MyData)
ATLPREFAST_UNSUPPRESS()
if (data != NULL)
{
HANDLE thread = (HANDLE)_beginthreadex(NULL, 0, UiThread, (void *)data, 0, NULL);
if (thread)
{
WaitForSingleObject(data->Event, INFINITE);
CloseHandle(thread);
hRes = data->hr;
if (SUCCEEDED(hRes))
{
ATL::CComPtr<IUnknown> unk;
hRes = data->Unk.CopyTo(&unk);
if (SUCCEEDED(hRes))
{
hRes = unk->QueryInterface(riid, ppv);
}
}
}
delete data;
}
return hRes;
}
};
typedef CMyComCreator _CreatorClass;
static unsigned __stdcall UiThread(void * param)
{
CoInitializeEx(0, COINIT_APARTMENTTHREADED);
MyData * data = (MyData *)param;
ATL::CComObject<CMyDialog> * bb;
data->hr = ATL::CComObject<CMyDialog>::CreateInstance(&bb);
ATL::CComPtr<IUnknown> unk((IDispatch *) bb);
data->Unk = unk;
unk.Release();
data->Event.Set();
if (SUCCEEDED(data->hr))
{
CMessageLoop theLoop;
MessageLoop = &theLoop;
int nRet = theLoop.Run();
MessageLoop = NULL;
}
CoUninitialize();
return 0;
}
I needed to pack everything in a single DLL.
In which case, DllSurrogate is not the only way of doing this. There's also Rundll32:
INFO: Windows Rundll and Rundll32 Interface
This would allow you to run your own message loop inside the DLL's EntryPoint and have complete control over message processing, including PreTranslateMessage. You can copy the message loop logic from an ATL EXE server.
Bear in mind, there's still 32-bit and 64-bit version of "RunDll32.exe" in every 64-bit Windows OS. Use the one which matches the bit-ness of your DLL.
I have set a SetWindowsHookEx on the Edit class of notepad, but I am not sure how to continue from here.
I wish to subclass the Edit class to my own procedure, then manipulate the text or just save it to a file, then it must be send back the notepad control/class.
There are easier ways to get the text from notepad, but I am trying to learn WINAPI and Subclassing so this is a good way for me to learn it.
My SetWindowsHookEx looks like this:
SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInstDll, npThreadId);
How do I use the hook type : WH_GETMESSAGE to get the text from the Edit class in notepad and transfer it to my GetMsgProc(I think) function?
Is it the right hook type at all?
Do I send it a message? If yes, how do I do that?
My code look like this:
dllHeader.h:
#ifdef DLLAPI
#else
#define DLLAPI extern "C" __declspec(dllimport)
#endif
DLLAPI bool hookNotepad();
dll.cpp:
#include "stdafx.h"
#include <windows.h>
#define DLLAPI extern "C" __declspec(dllexport)
#include "dllHeader.h"
// Forward references
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
// Global variables
HHOOK g_hHook = NULL; // Hook for Notepad 'EDIT' class
HINSTANCE g_hInstDll = NULL; // DllMain entry (DLL_PROCESS_ATTACH)
HWND npHWND = NULL; // Notepad handle
DWORD npThreadId = NULL; // Notepad thread ID
HWND npeHWND = NULL; // Notepad 'EDIT' class handle
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInstDll = hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
bool hookNotepad ()
{
npHWND = FindWindow(TEXT("Notepad"), NULL); // Finds Notepad
if (npHWND) //Notepad found
{
npeHWND = FindWindowEx(npHWND, NULL, L"Edit", NULL); // Finds the 'EDIT' class in notepad
npThreadId = GetWindowThreadProcessId(npeHWND, NULL); //Find ThreadID for Notepad 'EDIT' class
g_hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_hInstDll, npThreadId); //Set hook in Notepad 'EDIT' class
if (g_hHook) //if the hook is a succes then...
{
MessageBox(NULL,TEXT("Hook set in Notepad EDIT class!"), NULL, MB_OK);
// Now what? How to subclass the npeHWND (The 'EDIT' class of Notepad) to my own procedure?
}
else
{
MessageBox(NULL,TEXT("SetWindowsHookEx error!"), NULL, MB_OK); //If the hook fails.
}
}
return 0;
}
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode >= 0)
{
MessageBox(NULL,TEXT("This never get called. Why?"), NULL, MB_OK);
}
return(CallNextHookEx(g_hHook, nCode, wParam, lParam));
}
exe.cpp
#include <stdlib.h>
#include "stdafx.h"
#include <strsafe.h>
#include "C:\Users\Kristensen\Documents\Visual Studio 2012\Projects\dll\dll\dllHeader.h"
int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
hookNotepad();
return 0;
}
I am a bit a lost on how to proceed from here… Any hints? Links? Tips?
I have read the documentation on MSDN regarding the SetWindowsHookEx function – but I did not find a clear answer there.
WH_GETMESSAGE is called when the hooked thread calls GetMessage() or PeekMessage() to retrieve a message from its message queue, but not all messages go through the message queue, so you may have to use the WH_CALLWNDPROC/RET hooks as well, depending on what kind of messages you are trying to intercept.
Your global variables need to be stored in a block of shared memory or else they will only be accessible to the process that installs the hook, since a new copy of the DLL gets loaded when hooking other processes.
You cannot subclass an HWND that is owned by another process. You would have to inject code into that process and then that code can subclass locally as needed. SetWindowsHookEx() can be used to inject code into other processes, but CreateRemoteThread() may be better to use for that, depending on your needs.
I am trying to subclass the window that currently has focus. I do this by monitoring for HCBT_ACTIVATE events using a CBT hook, and set and unset the WndProc of the focused and previously focused windows.
The problem is that it only works whenever I have a breakpoint set somewhere in the code.
If there is no breakpoint, once my application exits, all the windows that I have subclassed crashes in order, even though I have removed the subclassing and restored the original WndProc.
I have verified that Unsubclass() is called whenever my application shuts down.
// code extracts
HINSTANCE hInst;
HHOOK hHook;
#pragma data_seg(".shared")
HWND hWndSubclass = 0;
FARPROC lpfnOldWndProc = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:.shared,rws")
void Unsubclass()
{
// if the window still exists
if (hWndSubclass != 0 && IsWindow(hWndSubclass))
{
SetWindowLongPtr(hWndSubclass, GWLP_WNDPROC, (LPARAM)lpfnOldWndProc);
hWndSubclass = 0;
}
}
static LRESULT CALLBACK SubClassFunc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_MOVING)
{
// this is just test code so I can see it works (it does)
RECT* r = (RECT*)lParam;
r->right = r->left + 500;
r->bottom = r->top + 500;
return TRUE;
}
else if (message == WM_DESTROY)
{
Unsubclass();
}
return CallWindowProc((WNDPROC)lpfnOldWndProc, hWndSubclass, message, wParam, lParam);
}
void SubclassWindow(HWND hWnd)
{
// remove the subclassing for the old window
Unsubclass();
// subclass the new window
lpfnOldWndProc = (FARPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LPARAM)SubClassFunc);
hWndSubclass = hWnd;
}
static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HCBT_ACTIVATE)
{
SubclassWindow((HWND)wParam);
}
return 0;
}
// ... code that initializes the CBT proc
__declspec(dllexport) BOOL Setup()
{
hHook = SetWindowsHookEx(WH_CBT, CBTProc, hInst, 0);
}
__declspec(dllexport) BOOL Teardown()
{
UnhookWindowsHookEx(hHook);
Unsubclass();
}
BOOL APIENTRY DllMain( HINSTANCE hInstance,
DWORD Reason,
LPVOID Reserved
)
{
switch(Reason)
{
case DLL_PROCESS_ATTACH:
hInst = hInstance;
return TRUE;
case DLL_PROCESS_DETACH:
Unsubclass();
return TRUE;
}
return TRUE;
}
Your problems hinge on several fronts:
UnHookWindowsHook does not unload injected dlls, all it does is remove the hook proc. If the dlls need to be unloaded its up to them to invent some kind of unloading mechanism.
SetWindowLongPtr typically fails when called from a process other than the process that owns the window.
The nett result of this is, its very difficult to safely remove windows hooks. First thing, your OldWindowProc pointer should not be stored in the shared data area. Next, in order to remove the subclass, you need to be able to co-erce the (currently) subclassed process to perform the un-subclassing.
What you could do is, first, register a new unique message id and place it in your shared area using RegisterWindowMessage. WM_REMOVE_HOOK.
UINT idWM_REMOVE_HOOK = RegisterWindowMessage("WM_REMOVE_HOOK");
Now, whenever you need to remove a hook,
SendMessage(hWndSubClass,idWM_REMOVE_HOOK,0,0);
In your subclass proc:
if(uMsg == WM_DESTROY || uMsg == idWM_REMOVE_HOOK)
{
Unsubclass(hwnd);
}
Remove the call to UnSubClass in DLL_PROCESS_DETATCH. Its a dangerous race condition thats going to cause your dll being unloaded in some random process to trash the hook data of a potentially valid hook in another process.
lpfnOldWndProc and hWndSubclass are global pointers. Seems like you've got only one per process. What if a process creates more than one window?
Then you will unsubclass only the last one.
EDIT: Also, why do you tear down in Process DETACH?
You are creating a global system-wide hook in a DLL. You need to store the HHOOK handle and your subclassing information in a block of shared memory so all instances of your DLL in all running processes can have access to them. Your variables are declared global in code, but each individual instance of the DLL will have its own local copy of them, and thus they will not be not initialized in all but 1 of your DLL instances (the one that calls Setup()). They need to be shared globally within the entire system instead.
You also should not be calling TearDown() in DLL_PROCESS_DETACH, either. Every instance of the DLL is going to call TearDown() when their respective processes terminate, but only the single instance that actually called Setup() should be the one to call Teardown().
If the debugger will cause the process to succeed by adding a breakpoint then most likely, this is a timing issue.
What possibly happens is that your main application is closing itself and freeing resources just before the subclassed windows get the messages they need to remove the subclass again. You might want to give them a few processing cycles to handle their own messages between the unhooking and the unsubclassing. (In Delphi you could do this by calling Application.ProcessMessages but in your C++ version? Don't know the answer to that.