[RAD Studio XE3 / C++]
I have a FMX project running in Windows only at this stage, but I need to detect events when USB devices are connected and disconnected. I have a similar VCL app that can do this fine, but the Application->HookMainWindow is not exposed in FMX (only VCL).
Is there an elegant way to handle this? Or do I have to hack some VCL stuff into my FMX app to make that work? I'd imagine I have to abstract it so I can support other platforms down the track. For the meantime though I need to get the Windows solution working.
If the 'VCL hack' thing is required, how would I reference the vcl::Forms::Application from within my Fmx app?
Cheers.
This could help, using the TMessage way?
type
TMyMessageClass = class(TMessage)
MyProp1 : Integer;
MyProp2 : string;
end;
procedure MyForm.FormCreate(Sender: TObject);
begin
TMessageManager.DefaultManager.SubscribeToMessage(TMyMessageClass, Self.ProcessMessage);
end;
procedure MyForm.ProcessMessage(Sender : TObject; M : TMessage);
begin
if M is TMyMessageClass then
begin
//Do something
end;
end;
From the thread I do something like...
procedure TMyThread.Execute;
var
FMyMessage : TMyMessageClass;
begin
//stuff
Synchronize(
procedure
begin
FMyMessageClass := TMyMessageClass.Create;
FMyMessageClass.MyProp1 := 1;
FMyMessageClass.MyProp2 := 'Hello';
TMessageManager.DefaultManager.SendMessage(nil, FMyMessageClass);
end);
Hope this helps
I have found a solution, thanks to http://www.haogongju.net/art/1480814
It'd be nice to be able to attach some files but it looks like it's going to have to go inline.
SystemEvents.h
#ifndef SystemEventsH
#define SystemEventsH
#include <System.Classes.hpp>
#include <FMX.Controls.hpp>
#include <FMX.Forms.hpp>
#include "DeviceChanged.h"
//---------------------------------------------------------------------------
class TSystemEvents : TObject
{
private:
TDeviceChangedMethod pDeviceChangeHandler;
TForm *pOwnerForm;
#ifdef _Windows
HWND Hwnd; // Save the window handle
LONG OldWndProc;// And remember the old WndProc so we can put it back later
#endif
public:
__fastcall TSystemEvents(TForm *_pForm);
__fastcall ~TSystemEvents();
__property TForm *OwnerForm = {read=pOwnerForm};
#ifdef _Windows
LRESULT __stdcall MessageHandler(Winapi::Messages::TMessage &Message);
#endif // _Windows
__property TDeviceChangedMethod DeviceChangeHandler={read=pDeviceChangeHandler,write=pDeviceChangeHandler};
};
extern TSystemEvents *SystemEvents;
#endif
SystemEvents.cpp
#include <fmx.h>
#include <FMX.Platform.Win.hpp>
#pragma hdrstop
#include "SystemEvents.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#ifdef _Windows
LRESULT __stdcall WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (SystemEvents == NULL)
{
return 0;
}
// This routine can't be a closure because the winapi needs to call it as
// a LONGPTR. So from here I can pass it into the SystemEvents object.
Winapi::Messages::TMessage _Message;
_Message.Msg = msg;
_Message.WParam = wParam;
_Message.LParam = lParam;
return (LRESULT)SystemEvents->MessageHandler(_Message);
}
#endif //_Windows
__fastcall TSystemEvents::TSystemEvents(TForm *_pForm)
{
pOwnerForm = _pForm;
pDeviceChangeHandler = NULL;
#ifdef _Windows
// Owner form handle is in FMX framework, but we want a Hwnd handle:
Hwnd = FmxHandleToHWND(pOwnerForm->Handle);
// Save the original WindowProc address
OldWndProc = GetWindowLongPtr(Hwnd, GWL_WNDPROC);
// Redirect the messages to my own function
SetWindowLongPtr(Hwnd, GWL_WNDPROC, (LONG_PTR)&WindowProc);
#endif
}
__fastcall TSystemEvents::~TSystemEvents()
{
#ifdef _Windows
// Very important we undo our hack before the app finishes
SetWindowLongPtr(Hwnd, GWL_WNDPROC, OldWndProc);
#endif
}
LRESULT __stdcall TSystemEvents::MessageHandler(Winapi::Messages::TMessage &Message)
{
#ifdef _Windows
if (Message.Msg == WM_DEVICECHANGE)
{
if (DeviceChangeHandler != NULL)
{
DeviceChangeHandler(this, new TDeviceChangedMessage(TDeviceChangedMessage::ParamForWin32wParam(Message.WParam)));
return 1;
}
}
return CallWindowProc((WNDPROC)OldWndProc, Hwnd, Message.Msg, Message.WParam, Message.LParam);
#endif
return 0;
}
DeviceChanged.h
#ifndef DeviceChangedH
#define DeviceChangedH
//---------------------------------------------------------------------------
typedef enum {Unknown = 0, DeviceNodesChanged} DeviceChangedParam;
class TDeviceChangedMessage
{
private:
DeviceChangedParam eParam;
public:
TDeviceChangedMessage(DeviceChangedParam _eParam)
{
eParam = _eParam;
}
__property DeviceChangedParam Param={read=eParam};
static DeviceChangedParam __fastcall ParamForWin32wParam(WPARAM _wParam);
};
typedef void __fastcall (__closure *TDeviceChangedMethod)(System::TObject* Sender, TDeviceChangedMessage* M);
#endif
DeviceChanged.cpp
#include <fmx.h>
#include <Dbt.h>
#pragma hdrstop
#include "DeviceChanged.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
DeviceChangedParam __fastcall TDeviceChangedMessage::ParamForWin32wParam(WPARAM _wParam)
{
if (_wParam == DBT_DEVNODES_CHANGED)
return DeviceChangedParam::DeviceNodesChanged;
return DeviceChangedParam::Unknown;
}
To use it:
#include <SystemEvents.h>
TSystemEvents *SystemEvents;
// In Constructor:
{
SystemEvents = new TSystemEvents(this);
SystemEvents->DeviceChangeHandler = OnDeviceChanged;
}
// In Destructor:
{
deletenullify(SystemEvents);
}
// Handler:
void __fastcall TMainForm::OnDeviceChanged(System::TObject* Sender, TDeviceChangedMessage *M)
{
if (M->Param == DeviceChangedParam::DeviceNodesChanged)
{
OnUSBDeviceChanged();
}
}
Works for me. :)
Related
I made a simple application that creates a window and then uses preview handlers to display a preview of a file passed as an argument to the application, see code below.
This works perfectly fine when just drag'n'dropping the file to preview onto the .exe file (or alternatively just hard coding the file path instead and then launching the .exe directly from explorer), but fails for almost all preview handlers when invoking it from a terminal (powershell, cmd, whatever) with e.g. ./main.exe testfile.docx. It also fails when trying to invoke it from other processes.
The issue i described happens (among others) for the following preview handlers:
MS Office preview handler for excel files, CLSID {00020827-0000-0000-C000-000000000046}
MS Office preview handler for word files, CLSID {84F66100-FF7C-4fb4-B0C0-02CD7FB668FE}
Edge preview handler for pdf files, CLSID {3A84F9C2-6164-485C-A7D9-4B27F8AC009E}
MS Office preview handler for power point files, CLSID {65235197-874B-4A07-BDC5-E65EA825B718}. For the power point preview handler, the initialize call to the IInitializeWithFile interface fails hresult 0x86420003
It works fine for the following preview handlers:
Windows provided preview handler for text files, CLSID {1531d583-8375-4d3f-b5fb-d23bbd169f22}
Windows provided preview handler for html files, CLSID {f8b8412b-dea3-4130-b36c-5e8be73106ac}
When i say it "fails", i mean that it just doesn't display a preview, none of the function calls fail. See images:
test.xlsx excel file dragged onto .exe file, same result when hardcoding path to test.xlsx and just starting the application from explorer:
Path to test.xlsx passed as command line argument (./main.exe ./test.xlsx in powershell):
I am not sure, what would cause this or where to even start looking or what to search for, so any input on this would be appreciated.
Note: I asked a similar question a few days ago, but that was a seperate issue.
Compile with cl /std:c++20 /EHsc main.cpp, expects path to a file to preview as first command line parameter. Since i used a bit of winrt, this requires windows 10.
#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "WindowsApp.lib")
#pragma comment(lib, "Ole32.lib")
#include <iostream>
#include <string>
#include <string_view>
#include <array>
#include <filesystem>
#include <Windows.h>
#include <shlwapi.h>
#include <winrt/base.h>
#include <ShObjIdl.h>
class Preview{
winrt::com_ptr<IPreviewHandler> mPreviewHandler;
bool mIsActive = false;
bool mIsInitialized = false;
std::filesystem::path mFilePath;
HWND mHwnd;
RECT mRect;
public:
Preview(const std::filesystem::path& filePath, const HWND hwnd, const RECT& rect):
mFilePath(filePath), mHwnd(hwnd), mRect(rect){
createPreviewHandler();
initialize();
};
void setRect(const RECT& rect){
mPreviewHandler->SetRect(&rect);
mRect = rect;
}
void setWindow(const HWND hwnd, const RECT& rect){
mPreviewHandler->SetWindow(hwnd, &rect);
mHwnd = hwnd;
setRect(rect);
}
void startPreview(){
if(!mIsActive){
if(!mIsInitialized){
initialize();
}
mPreviewHandler->DoPreview();
setRect(mRect);
mIsActive = true;
}
}
void stopPreview(){
if(mIsActive){
mPreviewHandler->Unload();
mIsActive = false;
mIsInitialized = false;
}
}
private:
void createPreviewHandler(){
CLSID previewHandlerId = getShellExClsidForType(mFilePath.extension());
mPreviewHandler.capture(CoCreateInstance, previewHandlerId, nullptr, CLSCTX_LOCAL_SERVER);
}
CLSID getShellExClsidForType(const std::wstring& extension){
winrt::hresult res;
DWORD size;
std::array<wchar_t, 39> interfaceIdWstr;
size = StringFromGUID2(IID_IPreviewHandler, interfaceIdWstr.data(), interfaceIdWstr.size());
if(!size){
winrt::throw_hresult(HRESULT_FROM_WIN32(GetLastError()));
}
std::array<wchar_t, 39> exIdWstr;
res = AssocQueryStringW(ASSOCF_INIT_DEFAULTTOSTAR,
ASSOCSTR_SHELLEXTENSION,
extension.c_str(),
interfaceIdWstr.data(),
exIdWstr.data(),
&size);
winrt::check_hresult(res);
CLSID exId;
res = IIDFromString(exIdWstr.data(), &exId);
winrt::check_hresult(res);
return(exId);
}
void initialize(){
initializeFromPath(mFilePath);
setWindow(mHwnd, mRect);
mIsInitialized = true;
}
void initializeFromPath(const std::filesystem::path& filePath){
if(!initWithFile(filePath) && !initWithStream(filePath)){
winrt::throw_hresult(E_NOINTERFACE);
}
}
bool initWithFile(const std::filesystem::path& filePath){
if(!mPreviewHandler.try_as<IInitializeWithFile>()){
return(false);
}
winrt::check_hresult(mPreviewHandler.as<IInitializeWithFile>()->Initialize(filePath.c_str(), STGM_READ));
return(true);
}
bool initWithStream(const std::filesystem::path& filePath){
if(!mPreviewHandler.try_as<IInitializeWithStream>()){
return(false);
}
winrt::com_ptr<IStream> stream;
stream.capture([](LPCWSTR pszFile, DWORD grfMode, REFIID riid, void **ppstm)
{return(SHCreateStreamOnFileEx(pszFile, grfMode, 0, false, nullptr, reinterpret_cast<IStream**>(ppstm)));},
filePath.c_str(), STGM_READ | STGM_SHARE_DENY_WRITE | STGM_FAILIFTHERE);
winrt::check_hresult(mPreviewHandler.as<IInitializeWithStream>()->Initialize(stream.get(), STGM_READ));
return(true);
}
};
HMODULE getCurrentModule(){
HMODULE hModule;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(LPCTSTR)getCurrentModule,
&hModule);
return(hModule);
}
LRESULT windowProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam){
LPARAM ret = 0;
switch(msg){
case WM_CLOSE:
{
PostQuitMessage(0);
ret = 0;
}break;
default:
{
ret = DefWindowProc(hwnd, msg, wParam, lParam);
}break;
}
return(ret);
}
HWND createTestWindow(){
WNDCLASSW wndClass;
wndClass.style = 0;
wndClass.lpfnWndProc = windowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hInstance = getCurrentModule();
wndClass.hIcon = nullptr;
wndClass.hCursor = nullptr;
wndClass.hbrBackground = nullptr;
wndClass.lpszMenuName = nullptr;
wndClass.lpszClassName = L"test";
ATOM wndAtom = RegisterClassW(&wndClass);
if(!wndAtom){
winrt::throw_hresult(HRESULT_FROM_WIN32(GetLastError()));
}
HWND window = CreateWindowExW(0,
L"Test",
L"",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
wndClass.hInstance,
nullptr);
if(!window){
winrt::throw_hresult(HRESULT_FROM_WIN32(GetLastError()));
}
ShowWindow(window, SW_NORMAL);
return(window);
}
int main(int argc, char *argv[]){
try{
winrt::check_hresult(CoInitialize(nullptr));
if(!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE)){
winrt::throw_hresult(E_FAIL);
}
if(argc != 2){
return(1);
}
std::filesystem::path filePath(argv[1]);
HWND window = createTestWindow();
RECT rect;
GetClientRect(window, &rect);
Preview preview(filePath, window, rect);
preview.startPreview();
MSG msg;
while(GetMessageW(&msg, nullptr, 0, 0) != 0){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}catch(winrt::hresult_error err){
std::wcout << "0x" << std::hex << err.code() << " " << static_cast<std::wstring_view>(err.message());
}catch(...){}
}
Call GetFullPathName on argv[1]. The shell functions can't parse a relative path into a pidl.
The main goal is to block maximalize web browser window using subclassing and dll.
I have 2 apps: injector and the dll.
In injector app I load that dll, find window by title, get functions from dll and execute that functions ( their names are hook and unhook ) from dll. So this is standard injector. Of course I check is something NULL and I don't get any errors.
In dll I have 5 functions:
dllMain (here I only set global hInstance variable, which is in shared memory ):
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
{
if (hInstance == NULL)
{
hInstance = hinstDLL;
}
break;
}
...
}
return TRUE;
}
Hook ( HandleofTarget is the HWND, which I get from FindWindow ; I use this function in injector ):
extern "C" __declspec(dllexport) bool hook( HWND HandleofTarget)
{
hTarget=HandleofTarget;
hhook=SetWindowsHookEx(WH_CBT,cbtHookProc,hInstance, GetWindowThreadProcessId(hTarget,NULL));
if(hhook==NULL)
return 0;
else
return 1;
}
Unhook ( here I unhook hooks - I use this function in injector):
extern "C" __declspec(dllexport) void unhook(void)
{
if(hhook != NULL)
UnhookWindowsHookEx( hhook );
}
cbtHookProc ( hook callback, where I change window procedure ):
LRESULT CALLBACK cbtHookProc( int code, WPARAM wParam, LPARAM lParam )
{
if( code < 0 ) return CallNextHookEx( 0, code, wParam, lParam );
if (code == HCBT_ACTIVATE)
{
if((HWND)(wParam)==hTarget)
{
if(done == FALSE)
{
g_OldWndProc =(WNDPROC)(SetWindowLongPtr ( (HWND)(wParam), GWLP_WNDPROC,reinterpret_cast<LONG_PTR>( NewWndProc )));
done = TRUE;
}
}
}
return CallNextHookEx( 0, code, wParam, lParam );
}
NewWndProc ( new Window procedure, where I would like to block maximalize ):
LRESULT CALLBACK NewWndProc( HWND hwnd, UINT mesg, WPARAM wParam, LPARAM lParam )
{
switch( mesg )
{
case WM_SYSCOMMAND:
{
if(wParam == SC_MAXIMIZE)
{
return 1;
}
}
break;
}
return CallWindowProc( g_OldWndProc, hwnd, mesg, wParam, lParam );
}
When I test this dll with other apps - it works. When I use this dll with web browser like Internet Edge and Google Chrome - it doesn't works. That web browser, which I try injected works slower, but I can still maximalize that window. When I debuq dll, in web browser after SetWindowsHookEx I see that hook is not NULL, but my code doesn't go to cbtHookProc. What is going on with web browser?
UPDATE:
One more time - thank you Strive Sun - MSFT for helping me.
I change the lines in cbtHookProc, but it still doesn't work. My cbtHookProc is don't called by webBrowser - that is problem.
When I looked at your gif I see something what I don't have and I think that is the problem. My injector app looks like this:
hDll = LoadLibrary( L"dllka10" );
hHookedWindow=FindWindow(TEXT("Chrome_WidgetWin_1"),TEXT("Nowa karta - Google Chrome"));
if( hDll && hHookedWindow)
{
qDebug()<<"hDll and hHookedWindow are not NULL!";
funHook =( MYPROC2 ) GetProcAddress( hDll, (LPCSTR) "hook" );
funUnhook = ( MYPROC ) GetProcAddress( hDll, (LPCSTR) "unhook" );
if( funHook )
{
qDebug()<<funHook(hHookedWindow);
}
}
I don't use CreateThread(). Is it important here?
UPDATED 2
LRESULT CALLBACK cbtHookProc( int code, WPARAM wParam, LPARAM lParam )
{
if( code < 0 ) return CallNextHookEx( 0, code, wParam, lParam );
std::fstream file;
file.open("C:\\Users\\tom\\Desktop\\logs.txt",std::ios::out|std::ios::app);
file<<"In cbtHook function!"<<std::endl;
file.close();
if (code == HCBT_MINMAX)
{
if (LOWORD(lParam) == SW_SHOWMAXIMIZED)
{
return 1;
}
}
return CallNextHookEx( 0, code, wParam, lParam );
}
When I run chrome application - my logs.txt is empty. When I run other app - I have logs.
UPDATED 3
In my dll I have:
#ifdef __GNUC__
HWND hTarget __attribute__((section (".shared"), shared)) =NULL;
HWND hApp __attribute__((section (".shared"), shared)) = NULL;
bool done __attribute__((section (".shared"), shared)) =FALSE;
HINSTANCE hInstance __attribute__((section (".shared"), shared)) =NULL;
HHOOK hhook __attribute__((section (".shared"), shared)) = NULL;
WNDPROC g_OldWndProc __attribute__((section (".shared"), shared)) = NULL;
#endif
#ifdef _MSC_VER
#pragma data_seg(".shared")
HWND hTarget=NULL;
HWND hApp = NULL;
bool done=FALSE;
HINSTANCE hInstance=NULL;
HHOOK hhook = NULL;
WNDPROC g_OldWndProc = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:.shared,RWS")
#endif
in my injector I don't have any pragma - I have only ( QT ):
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <iostream>
#include "string.h"
#include "windows.h"
I can't reproduce your problem, but you can try another easier method.
HCBT_MINMAX : Specifies, in the low-order word, a show-window value
(SW_) specifying the operation. For a list of show-window values, see
the ShowWindow. The high-order word is undefined.
if (code == HCBT_MINMAX)
{
if (LOWORD(lParam) == SW_SHOWMAXIMIZED)
{
return 1;
}
}
No need to use HCBT_ACTIVATE to obtain window handles and compare their handles.
Updated:
Code works fine.
My code sample:
DLL:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include "stdio.h"
#include <windows.h>
HHOOK hCBT = NULL;
HWND hTarget;
HMODULE thisModule;
LRESULT CALLBACK _cbtProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code < 0) return CallNextHookEx(0, code, wParam, lParam);
if (code == HCBT_MINMAX)
{
if (LOWORD(lParam) == SW_SHOWMAXIMIZED)
{
return 1;
}
}
return CallNextHookEx(0, code, wParam, lParam);
}
#ifdef __cplusplus //If used by C++ code.
extern "C" { //we need to export the C interface
#endif
__declspec(dllexport) BOOL WINAPI InstallHooks(HWND HandleofTarget)
{
hTarget = HandleofTarget;
DWORD tid = GetWindowThreadProcessId(hTarget, NULL);
hCBT = SetWindowsHookEx(WH_CBT, _cbtProc, thisModule, tid);
return (hCBT) ? TRUE : FALSE;
}
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus //If used by C++ code.
extern "C" { //we need to export the C interface
#endif
__declspec(dllexport) void WINAPI RemoveHooks()
{
UnhookWindowsHookEx(hCBT);
MessageBox(NULL, L"unhook", L" ", MB_OK);
hCBT = NULL;
}
#ifdef __cplusplus
}
#endif
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
thisModule = hModule;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
main.cpp
#include <Windows.h>
#include <stdio.h>
#include <psapi.h>
#include <shlwapi.h>
#include <tchar.h>
#pragma comment(lib,"Kernel32.lib")
#pragma comment(lib,"shlwapi.lib")
#pragma comment(linker, "/SECTION:.shared,RWS")
using namespace std;
HINSTANCE hinstDLL;
typedef void (*RemoveHooks)();
DWORD __stdcall CreateThreadFunc(LPVOID)
{
while (1)
{
if (GetAsyncKeyState(0x50) & 0x0001)
{
RemoveHooks removeHooks = (RemoveHooks)GetProcAddress(hinstDLL, "RemoveHooks");
removeHooks();
}
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_DESTROY) {
PostQuitMessage(0);
}
return DefWindowProc(hwnd, message, wParam, lParam);
};
int main()
{
HWND hwnd = FindWindow(L"Chrome_WidgetWin_1", L"Google Translate - Google Chrome");
CreateThread(NULL, 0, CreateThreadFunc, 0, 0, 0);
hinstDLL = LoadLibrary(TEXT("D:\\Start from 11.2\\WM_CBT_DLL\\x64\\Debug\\WM_CBT_DLL.dll"));
BOOL(*InstallHooks)(HWND);
InstallHooks = (BOOL(*)(HWND)) GetProcAddress(hinstDLL, "InstallHooks");
BOOL l = InstallHooks(hwnd);
int err = GetLastError();
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
I want my application to run some code when the screen gets locked (Win 7 and 10). My application may be in the background too when user locks screen.
Anyone point me in right direction?
Thank you,
relayman
Use WTSRegisterSessionNotification() to register an HWND to receive WTS_SESSION_(UN)LOCK notifications via the WM_WTSSESSION_CHANGE window message.
You can use FMX's FormToHWND() function to obtain your Form's HWND. However, FireMonkey controls, including Forms, do not have an overridable WndProc() method to process window messages, like VCL does, so you will have to use the Win32 API's SetWindowLongPtr() or SetWindowSubclass() function (see Subclassing Controls on MSDN) to receive the WM_WTSSESSION_CHANGE window message:
class TMyForm : public TForm
{
...
#ifdef _Windows
private:
bool MonitoringWTS;
static LRESULT CALLBACK SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
protected:
virtual void __fastcall CreateHandle();
#endif
...
};
#ifdef _Windows
#include <FMX.Platform.Win.hpp>
#include <commctrl.h>
#include <wtsapi32.h>
void __fastcall TMyForm::CreateHandle()
{
MonitoringWTS = false;
TForm::CreateHandle();
// depending on which version of C++Builder you are using...
HWND hWnd = FormToHWND(this);
//HWND hWnd = WindowHandleToPlatform(Handle)->Wnd;
//HWND hWnd = FmxHandleToHWND(Handle);
if (!SetWindowSubclass(hWnd, &SubclassWndProc, 1, reinterpret_cast<DWORD_PTR>(this)))
throw Exception(_D("Could not subclass window"));
MonitoringWTS = WTSRegisterSessionNotification(hWnd, NOTIFY_FOR_THIS_SESSION);
if (!MonitoringWTS)
RaiseLastOSError();
}
LRESULT CALLBACK TMyForm::SubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_NCDESTROY:
{
WTSUnRegisterSessionNotification(hWnd);
reinterpret_cast<TMyForm*>(dwRefData)->MonitoringWTS = false;
RemoveWindowSubclass(hWnd, &SubclassWndProc, uIdSubclass);
break;
}
case WM_WTSSESSION_CHANGE:
{
TMyForm *pThis = reinterpret_cast<TMyForm*>(dwRefData);
switch (wParam)
{
case WTS_SESSION_LOCK:
// do something ...
break;
case WTS_SESSION_UNLOCK:
// do something ...
break;
}
return 0;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
#endif
Alternatively, you can use the RTL's AllocateHWnd() function to create a hidden HWND and provide it with a custom WndProc() method:
class TMyForm : public TForm
{
...
#ifdef _Windows
private:
HWND WndForWTS;
bool MonitoringWTS;
void __fastcall WndProcForWTS(TMessage &Message);
public:
__fastcall TMyForm(TComponent *Owner);
__fastcall ~TMyForm();
#endif
...
};
#ifdef _Windows
#include <wtsapi32.h>
__fastcall TMyForm::TMyForm(TComponent *Owner)
: TForm(Owner)
{
WndForWTS = AllocateHWnd(&WndProcForWTS);
MonitoringWTS = WTSRegisterSessionNotification(WndForWTS, NOTIFY_FOR_THIS_SESSION);
if (!MonitoringWTS)
{
int err = GetLastError();
DeallocateHWnd(WndForWTS);
RaiseLastOSError(err);
}
}
__fastcall ~TMyForm();
{
DeallocateHWnd(WndForWTS);
}
void __fastcall TMyForm::WndProcForWTS(TMessage &Message)
{
switch (Message.Msg)
{
case WM_NCDESTROY:
{
if (MonitoringWTS)
{
WTSUnRegisterSessionNotification(WndForWTS);
MonitoringWTS = false;
}
WndForWTS = NULL;
break;
}
case WM_WTSSESSION_CHANGE:
{
switch (Message.WParam)
{
case WTS_SESSION_LOCK:
// do something ...
break;
case WTS_SESSION_UNLOCK:
// do something ...
break;
}
return;
}
}
Message.Result = DefWindowProc(WndForWTS, Message.Msg, Message.WParam, Message.LParam);
}
#endif
The code of keyhook.cpp in dll:
// KeyHook.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include "stdio.h"
#include "Windows.h"
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
#define DEF_PROCESS_NAME "notepad.exe"
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;
#pragma data_seg()
BOOL APIENTRY DllMain(HINSTANCE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
printf("main function in dll\n");
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hInstance = hModule;
printf("max=%d\n", MAX_PATH);
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//OutputDebugString("what the hell\n");
char szPath[MAX_PATH] = { 0, };
char *p = NULL;
//printf("the callback function");
int shift = nCode;
if (shift = 0)
{
if (!(lParam & 0x80000000))
{
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\');
//printf("szPath=%s\n", szPath);
OutputDebugString("what the hell\n");
if (!_stricmp(p + 1, DEF_PROCESS_NAME))
return 1;
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
#ifdef __cplusplus
extern "C" {
#endif // DEBUG
__declspec(dllexport) void HookStart()
{
printf("hookstart function\n");
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
if (g_hHook == NULL)
printf("failed to install the keyboard hook");
else
{
printf("Succeed in installing the keyboard hook");
}
}
__declspec(dllexport) void HookStop()
{
if (g_hHook)
{
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
printf("hookstop function\n");
}
#ifdef __cplusplus
}
#endif // DEBUG
The calling application code:
// hkeybi.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include<stdio.h>
#include<conio.h>
#include<Windows.h>
#define DEF_DLL_NAME "KeyHook.dll"
#define DEF_HOOKSTART "HookStart"
#define DEF_HOOKSTOP "HookStop"
typedef void(*PFN_HOOKSTART)();
typedef void(*PFN_HOOKSTOP)();
int main()
{
int ch = -1;
HMODULE hDll = NULL;
PFN_HOOKSTART HookStart = NULL;
PFN_HOOKSTOP HookStop = NULL;
hDll = LoadLibraryA(DEF_DLL_NAME);
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
if ((HookStart != NULL) && (HookStop != NULL))
printf("hello start\n");
HookStart();
printf("press q to exit!\n");
while (_getch() != 'q');
HookStop();
FreeLibrary(hDll);
return 0;
}
When I run the app,after I input several words,it will go down. I spent long time in solving the problem.
There are several problems in your KeyboardProc function. The first one being that your shift variable is assigned instead of tested:
if (shift = 0)
The variable is assigned 0, the condition is therefore always false. Effectively, the only code executed after the test is the return CallNextHookEx(...). If the condition would be true, you may run into problems because the GetModuleFileNameA result is not tested. In case of an error, the following strrchr will likely fail and the p pointer will be NULL. This will result in a crash at the _stricmp. And why do you specifically use the ANSI version of GetModuleFileName, are you sure you're not using Unicode? Finally, returning the hook proc without calling CallNextHookEx is a bad idea. Here's what the documentation says:
If code is greater than or equal to zero, and the hook procedure did
not process the message, it is highly recommended that you call
CallNextHookEx and return the value it returns
how can i check whether a specific window is open or not. I only got part of the window's name. i thinking of using EnumWindows() in QT console app but i get a few errors stating "main.obj:-1: error: unresolved external symbol imp__GetWindowTextW#12 referenced in function "int __stdcall EnumWindowsProc(struct HWND *,long)" (?EnumWindowsProc##YGHPAUHWND__##J#Z)"
Below is my sample code
BOOL CALLBACK EnumWindowsProc(HWND hWnd, long lParam) {
char buff[255];
if (IsWindowVisible(hWnd)) {
GetWindowText(hWnd, (LPWSTR) buff, 254);
}
return TRUE;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
EnumWindows(EnumWindowsProc, 0);
return 0;
}
That's a linker error rather than a compile error.
You have correctly included windows.h but you also need to add the import libraries to your linker options. All three Win32 functions in your sample code require you to link user32.lib.
EnumWindowsProc is not from Qt, it's a windows API function, you need to include windows.h
I did not use Qt, and the code can pass complie, but the output seems NOT right. Anyway, here's my code
#include <conio.h>
#include <iostream>
#include <windows.h>
using namespace std;
char buff[255];
BOOL CALLBACK EnumWindowsProc(HWND hWnd, long lParam)
{
if (IsWindowVisible(hWnd))
{
GetWindowText(hWnd, (LPWSTR) buff, 254);
}
return TRUE;
}
int main()
{
EnumWindows(EnumWindowsProc, 0);
for(int i = 0; i != 254; ++i)
cout << buff[i];
getch();
return 0;
}
You can use:
Application.OpenForms["FormName"]
To check if the form is open or not.