-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSuiteMain.cpp
479 lines (444 loc) · 22.8 KB
/
SuiteMain.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
#include "SuiteMain.h"
#include "TaskbarNotificationAreaIcon.h"
#include "SuiteExternalRelations.h"
#include "SuiteHotkeyFunctions.h"
#include "SuiteBindingDialog.h"
#include "SuiteAboutDialog.h"
#include "SuiteExterns.h"
#include "HotkeyEngine.h"
#include "SuiteVersion.h"
#include "SuiteCommon.h"
#include "Res.h"
#include <string>
#include <functional>
#include <initguid.h>
#include <wincodec.h>
extern pTaskDialog fnTaskDialog;
extern pWICConvertBitmapSource fnWICConvertBitmapSource;
extern pSHGetStockIconInfo fnSHGetStockIconInfo;
extern pCheckTokenMembership fnCheckTokenMembership;
bool IconMenuProc(HotkeyEngine* &hk_engine, SuiteSettings *settings, KeyTriplet *hk_triplet, bool elev_req, TskbrNtfAreaIcon *sender, WPARAM wParam, LPARAM lParam);
void CloseEventHandler(SuiteSettings *settings, TskbrNtfAreaIcon *sender);
void EndsessionTrueEventHandler(SuiteSettings *settings, TskbrNtfAreaIcon *sender, bool critical);
bool CheckIfElevationAvailable();
bool CheckIfAdmin();
HBITMAP GetUacShieldBitmap();
#ifdef __clang__
//Obscure clang++ bug - it reports "multiple definition" of std::operator+() when statically linking with libstdc++
//Observed on LLVM 3.6.2 with MinGW 4.7.2
//This is a fix for the bug
extern template std::wstring std::operator+(wchar_t const*, std::wstring const&); //caused by use of std::operator+(wchar_t const*, std::wstring const&)
#endif
int SuiteMain(SuiteSettings *settings)
{
TskbrNtfAreaIcon* SnkIcon=NULL;
HotkeyEngine* SnkHotkey=NULL;
std::wstring snk_path;
if (!settings->IsCustomShk()||!settings->IsCustomLhk()) {
//SnK path can be relative
snk_path=GetFullPathNameWrapper(settings->GetSnkPath());
if (!CheckIfFileExists(snk_path)) {
const wchar_t *wrn_msg=L"Path to SnK is not valid!\n\nPlease choose valid SnK path.";
if (fnTaskDialog) {
int btn_clicked;
fnTaskDialog(NULL, NULL, SNK_HS_TITLE, NULL, wrn_msg, TDCBF_OK_BUTTON, TD_WARNING_ICON, &btn_clicked);
} else {
MessageBox(NULL, wrn_msg, SNK_HS_TITLE, MB_ICONWARNING|MB_OK);
}
if (SuiteExtRel::LaunchSnkOpenDialog(snk_path)) {
settings->SetSnkPath(snk_path);
settings->SaveSettings();
} else {
ErrorMessage(L"Path to SnK is not valid!");
return ERR_SUITEMAIN+1;
}
}
snk_path=QuoteArgument(snk_path.c_str())+L" /sec /bpp +p /cmd=";
}
std::wstring snk_cmdline_s;
std::wstring snk_cmdline_l;
if (settings->IsCustomShk())
snk_cmdline_s=settings->GetCustomShk();
else
snk_cmdline_s=snk_path+QuoteArgument(settings->GetShkCfgPath().c_str());
if (settings->IsCustomLhk())
snk_cmdline_l=settings->GetCustomLhk();
else
snk_cmdline_l=snk_path+QuoteArgument(settings->GetLhkCfgPath().c_str());
//CreateProcessW requires lpCommandLine to be non-const string because it may edit it
//MSDN doesn't provide details on why it may happen, but actually it will occur when lpApplicationName is not provided
//In this case CreateProcessW will try to tokenize lpCommandLine with NULLs to try to find lpApplicationName in it
//Interesting thing is that it will revert all changes to lpCommandLine when finished
//So it just have to be writable and we can assume that contents will stay the same
//CreateProcessA doesn't have such remark because it's actually a wrapper to CreateProcessW and passes unicode copy of lpCommandLine to CreateProcessW and not the original string
//wstring::c_str() since C++11 returns pointer to continous NULL-terminated array
//While it's not advised to edit it (though it won't lead to memory violations as long as string length is considered, which is the case with CreateProcessW) it won't do any harm here
//Because CreateProcessW preserves it's contents and we won't do string manipulations with this buffer afterwards anyway
//So it's safe to drop const qualifier
KeyTriplet OnKeyTriplet(const_cast<wchar_t*>(snk_cmdline_s.c_str()), const_cast<wchar_t*>(snk_cmdline_l.c_str()));
//Check if elevated rights are available
bool elev_avail=CheckIfElevationAvailable();
bool is_admin=CheckIfAdmin();
//It's ok to pass reference to NULL HotkeyEngine to OnWmCommand - see IconMenuProc comments
//std::bind differs from lamda captures in that you can't pass references by normal means - object will be copied anyway
//To pass a reference you should wrap referenced object in std::ref
SnkIcon=TskbrNtfAreaIcon::MakeInstance(GetModuleHandle(NULL), WM_HSTNAICO, SNK_HS_TITLE L": Running", (elev_avail&&is_admin)?IDI_HSLTNAICO:IDI_HSTNAICO, L"SnK_HotkeySuite_IconClass", IDR_ICONMENU, IDM_STOP_START,
std::bind(IconMenuProc, std::ref(SnkHotkey), settings, &OnKeyTriplet, elev_avail&&is_admin, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
std::bind(CloseEventHandler, settings, std::placeholders::_1),
std::bind(EndsessionTrueEventHandler, settings, std::placeholders::_1, std::placeholders::_2));
if (!SnkIcon->IsValid()) {
ErrorMessage(L"Failed to create icon!");
return ERR_SUITEMAIN+2;
}
//At this point taskbar icon is already visible but unusable - it doesn't respond to any clicks and can't show popup menu
//So it's ok to customize menu here and initialize everything else
SnkHotkey=HotkeyEngine::MakeInstance();
//By default IDM_EDIT_LHK menu item is enabled and IDM_SET_EN_LHK is unchecked (see Res.rc)
if (settings->GetLongPress()) {
SnkIcon->CheckIconMenuItem(IDM_SET_EN_LHK, MF_BYCOMMAND|MF_CHECKED);
} else {
SnkIcon->EnableIconMenuItem(IDM_EDIT_LHK, MF_BYCOMMAND|MF_GRAYED);
}
//By default none of IDM_SET_CTRL_ALT, IDM_SET_CTRL_SHIFT and IDM_SET_SHIFT_ALT is checked (see Res.rc)
switch (settings->GetModKey()) {
case ModKeyType::CTRL_ALT:
SnkIcon->CheckIconMenuRadioItem(IDM_SET_CTRL_ALT, IDM_SET_CTRL_SHIFT, IDM_SET_CTRL_ALT, MF_BYCOMMAND);
break;
case ModKeyType::SHIFT_ALT:
SnkIcon->CheckIconMenuRadioItem(IDM_SET_CTRL_ALT, IDM_SET_CTRL_SHIFT, IDM_SET_SHIFT_ALT, MF_BYCOMMAND);
break;
case ModKeyType::CTRL_SHIFT:
SnkIcon->CheckIconMenuRadioItem(IDM_SET_CTRL_ALT, IDM_SET_CTRL_SHIFT, IDM_SET_CTRL_SHIFT, MF_BYCOMMAND);
break;
}
SnkIcon->ModifyIconMenu(IDM_SET_CUSTOM, MF_BYCOMMAND|MF_STRING|MF_UNCHECKED|MF_ENABLED, IDM_SET_CUSTOM, GetHotkeyString(settings->GetBindedKey(), L"Rebind ", L"...").c_str());
SnkIcon->ModifyIconMenu(POS_SETTINGS, MF_BYPOSITION|MF_STRING|MF_UNCHECKED|MF_ENABLED|MF_POPUP, (UINT_PTR)GetSubMenu(SnkIcon->GetIconMenu(), POS_SETTINGS), GetHotkeyString(settings->GetModKey(), settings->GetBindedKey()).c_str());
HBITMAP uac_bitmap=NULL;
if (elev_avail) {
if ((uac_bitmap=GetUacShieldBitmap())) {
MENUINFO mi={sizeof(MENUINFO), MIM_STYLE};
if (SnkIcon->GetIconMenuInfo(&mi)) {
mi.dwStyle|=MNS_CHECKORBMP;
SnkIcon->SetIconMenuInfo(&mi);
}
//ModifyMenu doesn't work here - it just replaces text with bitmap
MENUITEMINFO mii={sizeof(MENUITEMINFO)};
mii.fMask=MIIM_BITMAP;
mii.hbmpItem=uac_bitmap;
SnkIcon->SetIconMenuItemInfo(IDM_ELEVATE, FALSE, &mii);
}
} else
SnkIcon->RemoveIconMenu(IDM_ELEVATE, MF_BYCOMMAND);
if (!CheckIfFileExists(SearchPathWrapper(L"cmd.exe", NULL, NULL))) {
SnkIcon->EnableIconMenuItem(IDM_CMDPRMPT, MF_BYCOMMAND|MF_GRAYED);
}
//It is possible to set initial stack commit size for hotkey hook thread
//By default it is 0 and this means that stack commit size is the same as defined in PE header (that is 4 KB for MinGW/Clang)
//It was observed (on Win Server 2012 R2 x64 test machine for v1.1) that hotkey hook thread consumes around 2 KB of stack at it's peak usage when not triggered (i.e. no hotkey press actually happens)
//When hotkey press happens, stack peak usage jumps to around 9 KB (thanks to CreateProcess call)
//Because most of the time hook thread stays in it's untriggered state, we keep default initial commit size value from PE header (i.e. 4 KB)
//Commit size will grow automatically if more stack space is needed by the thread
if (!SnkHotkey->StartNew((LPARAM)&OnKeyTriplet, OnKeyTriplet.CreateEventHandler(settings))) {
ErrorMessage(L"Failed to set keyboard hook!");
return ERR_SUITEMAIN+3;
}
//Main thread's message loop
//GetMessage returns -1 if error (probably happens only with invalid input parameters) and 0 if WM_QUIT
//So continue on any non-zero result skipping errors
MSG msg;
BOOL res;
while ((res=GetMessage(&msg, NULL, 0, 0))) {
if (res>0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//Manually uninitializing some components to make sure right unintialization order
SnkHotkey->Stop(); //This way HotkeyEngine is deinitialized right after TskbrNtfAreaIcon
if (uac_bitmap) DeleteObject(uac_bitmap); //If UAC shield bitmap was created - free it
return msg.wParam;
}
bool IconMenuProc(HotkeyEngine* &hk_engine, SuiteSettings *settings, KeyTriplet *hk_triplet, bool elev_req, TskbrNtfAreaIcon *sender, WPARAM wParam, LPARAM lParam)
{
//Not checking if hk_engine is NULL in menu handlers - handlers won't be called until message loop is fired which happens after creation of hk_engine
bool hk_was_running=false;
switch (LOWORD(wParam)) {
case IDM_EXIT:
case IDM_ELEVATE:
//We can just use PostQuitMessage() and wait for TskbrNtfAreaIcon destructor to destroy icon at the end of the program
//But in this case icon will be briefly present after the end of message loop till the end of WinMain, though being unresponsive
//It will be better to destroy the icon right away and then exit message loop
//And after that do all other uninitialization without icon being visible for unknown purpose
sender->CloseAndQuit(LOWORD(wParam)==IDM_ELEVATE?ERR_ELEVATE:0); //Set WM_QUIT's wParam to 0 if normal exit and ERR_ELEVATE if requesting elevated rights
return true;
case IDM_STOP_START:
if (hk_engine->IsRunning()) {
hk_engine->Stop();
sender->ChangeIconTooltip(SNK_HS_TITLE L": Stopped");
sender->ChangeIcon(elev_req?IDI_HSLSTOPICO:IDI_HSSTOPICO);
sender->ModifyIconMenu(IDM_STOP_START, MF_BYCOMMAND|MF_STRING|MF_UNCHECKED|MF_ENABLED, IDM_STOP_START, L"Start");
} else {
if (!hk_engine->Start()) break;
sender->ChangeIconTooltip(SNK_HS_TITLE L": Running");
sender->ChangeIcon(elev_req?IDI_HSLTNAICO:IDI_HSTNAICO);
sender->ModifyIconMenu(IDM_STOP_START, MF_BYCOMMAND|MF_STRING|MF_UNCHECKED|MF_ENABLED, IDM_STOP_START, L"Stop");
}
return true;
case IDM_EDIT_SHK:
case IDM_EDIT_LHK:
{
if (LOWORD(wParam)==IDM_EDIT_SHK?settings->IsCustomShk():settings->IsCustomLhk()) {
bool create_file=false;
const wchar_t* MSG_TEXT=L"Custom action is set in config.\n\nPlease edit config file instead.";
if (fnTaskDialog) {
int btn_clicked;
fnTaskDialog(NULL, NULL, SNK_HS_TITLE, NULL, MSG_TEXT, TDCBF_OK_BUTTON, TD_INFORMATION_ICON, &btn_clicked);
} else {
MessageBox(NULL, MSG_TEXT, SNK_HS_TITLE, MB_ICONASTERISK|MB_OK);
}
return true;
}
std::wstring cfg_path=LOWORD(wParam)==IDM_EDIT_SHK?settings->GetShkCfgPath():settings->GetLhkCfgPath();
if (!CheckIfFileExists(cfg_path)) {
std::wstring msg_text=L"File \""+cfg_path+L"\" doesn't exist.\n\nDo you want it to be created?";
bool create_file=false;
if (fnTaskDialog) {
int btn_clicked;
fnTaskDialog(NULL, NULL, SNK_HS_TITLE, NULL, msg_text.c_str(), TDCBF_YES_BUTTON|TDCBF_NO_BUTTON, TD_INFORMATION_ICON, &btn_clicked);
create_file=btn_clicked==IDYES;
} else {
create_file=MessageBox(NULL, msg_text.c_str(), SNK_HS_TITLE, MB_ICONASTERISK|MB_YESNO|MB_DEFBUTTON1)==IDYES;
}
if (create_file&&CreateDirTreeForFile(cfg_path)) {
CloseHandle(CreateFile(cfg_path.c_str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL));
} else
return true;
}
ShellExecute(NULL, NULL, cfg_path.c_str(), NULL, NULL, SW_SHOWNORMAL);
return true;
}
case IDM_SET_EN_LHK:
hk_was_running=hk_engine->Stop();
if (settings->GetLongPress()) {
sender->CheckIconMenuItem(IDM_SET_EN_LHK, MF_BYCOMMAND|MF_UNCHECKED);
sender->EnableIconMenuItem(IDM_EDIT_LHK, MF_BYCOMMAND|MF_GRAYED);
settings->SetLongPress(false);
} else {
sender->CheckIconMenuItem(IDM_SET_EN_LHK, MF_BYCOMMAND|MF_CHECKED);
sender->EnableIconMenuItem(IDM_EDIT_LHK, MF_BYCOMMAND|MF_ENABLED);
settings->SetLongPress(true);
}
hk_engine->Set((LPARAM)hk_triplet, hk_triplet->CreateEventHandler(settings));
if (hk_was_running&&!hk_engine->Start()) break;
settings->SaveSettings();
return true;
case IDM_SET_CTRL_ALT:
hk_was_running=hk_engine->Stop();
sender->CheckIconMenuRadioItem(IDM_SET_CTRL_ALT, IDM_SET_CTRL_SHIFT, IDM_SET_CTRL_ALT, MF_BYCOMMAND);
settings->SetModKey(ModKeyType::CTRL_ALT);
sender->ModifyIconMenu(POS_SETTINGS, MF_BYPOSITION|MF_STRING|MF_UNCHECKED|MF_ENABLED|MF_POPUP, (UINT_PTR)GetSubMenu(sender->GetIconMenu(), POS_SETTINGS), GetHotkeyString(ModKeyType::CTRL_ALT, settings->GetBindedKey()).c_str());
hk_engine->Set((LPARAM)hk_triplet, hk_triplet->CreateEventHandler(settings));
if (hk_was_running&&!hk_engine->Start()) break;
settings->SaveSettings();
return true;
case IDM_SET_SHIFT_ALT:
hk_was_running=hk_engine->Stop();
sender->CheckIconMenuRadioItem(IDM_SET_CTRL_ALT, IDM_SET_CTRL_SHIFT, IDM_SET_SHIFT_ALT, MF_BYCOMMAND);
settings->SetModKey(ModKeyType::SHIFT_ALT);
sender->ModifyIconMenu(POS_SETTINGS, MF_BYPOSITION|MF_STRING|MF_UNCHECKED|MF_ENABLED|MF_POPUP, (UINT_PTR)GetSubMenu(sender->GetIconMenu(), POS_SETTINGS), GetHotkeyString(ModKeyType::SHIFT_ALT, settings->GetBindedKey()).c_str());
hk_engine->Set((LPARAM)hk_triplet, hk_triplet->CreateEventHandler(settings));
if (hk_was_running&&!hk_engine->Start()) break;
settings->SaveSettings();
return true;
case IDM_SET_CTRL_SHIFT:
hk_was_running=hk_engine->Stop();
sender->CheckIconMenuRadioItem(IDM_SET_CTRL_ALT, IDM_SET_CTRL_SHIFT, IDM_SET_CTRL_SHIFT, MF_BYCOMMAND);
settings->SetModKey(ModKeyType::CTRL_SHIFT);
sender->ModifyIconMenu(POS_SETTINGS, MF_BYPOSITION|MF_STRING|MF_UNCHECKED|MF_ENABLED|MF_POPUP, (UINT_PTR)GetSubMenu(sender->GetIconMenu(), POS_SETTINGS), GetHotkeyString(ModKeyType::CTRL_SHIFT, settings->GetBindedKey()).c_str());
hk_engine->Set((LPARAM)hk_triplet, hk_triplet->CreateEventHandler(settings));
if (hk_was_running&&!hk_engine->Start()) break;
settings->SaveSettings();
return true;
case IDM_SET_CUSTOM:
{
//The fun thing with DialogBoxParam is that it won't return until DLGPROC exits with EndDialog but in the same time it won't block thread execution
//It creates new message loop within current message loop, while latter remains blocked because current message dispatch hasn't ended (it waits for DialogBoxParam to return)
//But other windows still respond because this new message loop besides it's own DLGPROC happily handles all other thread's window processes
//When EndDialog is called within DLGPROC, it exits DialogBoxParam's message loop, finishes message dispatch that called DialogBoxParam in the first place and returns to thread's original message loop
//Q: What happens when DialogBoxParam called several times without exiting previous dialogs?
//A: We get several nested message loops and all dialog/window processes will be handled by the last created loop
//Q: What happens if I use PostQuitMessage with one or several dialogs running?
//A: All present message loops will sequentially exit (including thread's own, starting from the last created) and DialogBoxParam will return 0 every time it's message loop exits
//Q: What happens when I open several dialogs and exit the one that wasn't the last created?
//A: DialogBoxParam won't return and last created message loop will still run until it's own dialog exits
//A: In the end, randomly closing dialogs, we get proper nested message loop unwinding (i.e. DialogBoxParam will return sequentially starting from the last called)
//All in all, it's better disable icon completely so binding dialog won't be called second time and no other menu items can be clicked
sender->Disable();
hk_was_running=hk_engine->Stop();
BINDING_DLGPRC_PARAM bd_dlgprc_param={sender, hk_engine, settings, {0, 0, false}, NULL};
//Several words on InitCommonControls() and InitCommonControlsEx()
//"Common" name is somewhat misleading
//Even though controls like "static", "edit" and "button" are common (like in common sence) they are actually "standard" controls
//So there is no need to initialize them with InitCommonControls() or InitCommonControlsEx() functions
//Though ICC_STANDARD_CLASSES can be passed to InitCommonControlsEx, it actually does nothing - "standard" controls are really initialized by the system
//Also these functions has nothing to do with "Win XP"/"ComCtl32 v6+" style - just supply proper manifest to make "standard" controls use it
//IDD_BINDINGDLG uses only "standard" controls
switch (DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_BINDINGDLG), sender->GetIconWindow(), BindingDialog::DialogProc, (LPARAM)&bd_dlgprc_param)) {
case BD_DLGPRC_ERROR:
case DLGBX_FN_INV_PARAM:
case DLGBX_FN_FAILED:
break;
case BD_DLGPRC_OK:
settings->SetBindedKey(bd_dlgprc_param.binded_key);
sender->ModifyIconMenu(IDM_SET_CUSTOM, MF_BYCOMMAND|MF_STRING|MF_UNCHECKED|MF_ENABLED, IDM_SET_CUSTOM, GetHotkeyString(bd_dlgprc_param.binded_key, L"Rebind ", L"...").c_str());
sender->ModifyIconMenu(POS_SETTINGS, MF_BYPOSITION|MF_STRING|MF_UNCHECKED|MF_ENABLED|MF_POPUP, (UINT_PTR)GetSubMenu(sender->GetIconMenu(), POS_SETTINGS), GetHotkeyString(settings->GetModKey(), bd_dlgprc_param.binded_key).c_str());
case BD_DLGPRC_CANCEL:
hk_engine->Set((LPARAM)hk_triplet, hk_triplet->CreateEventHandler(settings));
if (hk_was_running&&!hk_engine->Start()) break;
settings->SaveSettings();
sender->SetModalWnd(NULL);
sender->Enable();
return true;
}
break;
}
#ifdef DEBUG
case IDM_DEBUG:
hk_was_running=hk_engine->Stop();
if (sender->GetIconMenuState(IDM_DEBUG, MF_BYCOMMAND)&MF_CHECKED) {
sender->CheckIconMenuItem(IDM_DEBUG, MF_BYCOMMAND|MF_UNCHECKED);
sender->EnableIconMenuItem(POS_SETTINGS, MF_BYPOSITION|MF_ENABLED);
hk_engine->Set((LPARAM)hk_triplet, hk_triplet->CreateEventHandler(settings));
} else {
sender->CheckIconMenuItem(IDM_DEBUG, MF_BYCOMMAND|MF_CHECKED);
sender->EnableIconMenuItem(POS_SETTINGS, MF_BYPOSITION|MF_GRAYED);
hk_engine->Set(0, DebugEventHandler);
}
if (hk_was_running&&!hk_engine->Start()) break;
return true;
#endif
case IDM_CMDPRMPT:
SuiteExtRel::LaunchCommandPrompt(settings);
return true;
case IDM_ABOUT:
{
//Blah blah blah... see comments on IDM_SET_CUSTOM
sender->Disable();
hk_was_running=hk_engine->Stop();
ABOUT_DLGPRC_PARAM ad_dlgprc_param={sender, settings};
switch (DialogBoxParam(NULL, MAKEINTRESOURCE(IDD_ABOUTDLG), sender->GetIconWindow(), AboutDialog::DialogProc, (LPARAM)&ad_dlgprc_param)) {
case DLGBX_FN_INV_PARAM:
case DLGBX_FN_FAILED:
break;
case AD_DLGPRC_OK:
if (hk_was_running&&!hk_engine->Start()) break;
sender->SetModalWnd(NULL);
sender->Enable();
return true;
case AD_DLGPRC_RESTART:
sender->SetModalWnd(NULL);
sender->CloseAndQuit(ERR_RESTART);
return true;
}
break;
}
default:
return false;
}
//We get there after break which happens instead of return in all cases where hk_engine should have restarted but failed
ErrorMessage(L"Failed to restart keyboard hook!");
sender->CloseAndQuit(ERR_SUITEMAIN+3); //Sets WM_QUIT's wParam to ERR_SUITEMAIN+3
return true;
}
void CloseEventHandler(SuiteSettings *settings, TskbrNtfAreaIcon *sender)
{
//Settings will be saved after message loop exits
sender->CloseAndQuit(); //Sets WM_QUIT's wParam to 0
}
void EndsessionTrueEventHandler(SuiteSettings *settings, TskbrNtfAreaIcon *sender, bool critical)
{}
bool CheckIfElevationAvailable()
{
HANDLE hOwnToken;
bool required=false;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hOwnToken)) {
TOKEN_ELEVATION elevation;
DWORD ret_len;
if (GetTokenInformation(hOwnToken, TokenElevation, &elevation, sizeof(elevation), &ret_len)) {
required=!elevation.TokenIsElevated;
}
CloseHandle(hOwnToken);
}
return required;
}
bool CheckIfAdmin()
{
//Doesn't work on Win 9x/ME and Win NT4
//But, for current purpose, we actually need it to work only on systems where UAC is present
BOOL ctm_res=FALSE;
if (fnCheckTokenMembership) {
PSID asid;
SID_IDENTIFIER_AUTHORITY sia_nt=SECURITY_NT_AUTHORITY;
if (AllocateAndInitializeSid(&sia_nt, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &asid)) {
HANDLE hOwnToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hOwnToken)) {
TOKEN_LINKED_TOKEN tlt;
DWORD ret_len;
if (GetTokenInformation(hOwnToken, TokenLinkedToken, &tlt, sizeof(tlt), &ret_len)) {
fnCheckTokenMembership(tlt.LinkedToken, asid, &ctm_res);
CloseHandle(tlt.LinkedToken);
}
CloseHandle(hOwnToken);
}
if (!ctm_res) fnCheckTokenMembership(NULL, asid, &ctm_res);
FreeSid(asid);
}
}
return ctm_res;
}
HBITMAP GetUacShieldBitmap()
{
HBITMAP uac_bitmap=NULL;
if (!fnWICConvertBitmapSource||!fnSHGetStockIconInfo)
return NULL;
CoInitialize(NULL);
IWICImagingFactory *pIWICIF;
if (SUCCEEDED(CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pIWICIF)))) {
IWICBitmap *pIWICB;
IWICBitmapSource *pIWICBS;
SHSTOCKICONINFO sii={sizeof(sii)};
//LoadIcon(IDI_SHIELD) doesn't work here because it loads large version of UAC shield icon, same as SHGetStockIconInfo(SHGSI_LARGEICON)
if (SUCCEEDED(fnSHGetStockIconInfo(SIID_SHIELD, SHGSI_ICON|SHGSI_SMALLICON, &sii))&&SUCCEEDED(pIWICIF->CreateBitmapFromHICON(sii.hIcon, &pIWICB))) {
if (SUCCEEDED(fnWICConvertBitmapSource(GUID_WICPixelFormat32bppPBGRA, pIWICB, &pIWICBS))) {
UINT cx, cy;
if (SUCCEEDED(pIWICBS->GetSize(&cx, &cy))) {
if (HDC hdc=GetDC(NULL)) {
void *dib_buf; //Memory area pointed to by this variable will be freed when HBITMAP, returned by CreateDIBSection(hSection=NULL), will be freed with DeleteObject
BITMAPINFO bmi={{
sizeof(BITMAPINFOHEADER), //BITMAPINFO.BITMAPINFOHEADER.biSize
(int)cx, //BITMAPINFO.BITMAPINFOHEADER.biWidth
-(int)cy, //BITMAPINFO.BITMAPINFOHEADER.biHeight
1, //BITMAPINFO.BITMAPINFOHEADER.biPlanes
32, //BITMAPINFO.BITMAPINFOHEADER.biBitCount
BI_RGB //BITMAPINFO.BITMAPINFOHEADER.biCompression (rest of BITMAPINFO.BITMAPINFOHEADER members are initialized with NULLs)
}}; //BITMAPINFO.RGBQUAD members are initialized with NULLs (this is required when biBitCount is 32 and biCompression is BI_RGB)
if ((uac_bitmap=CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &dib_buf, NULL, 0))) {
UINT stride=cx*sizeof(DWORD);
UINT buf_sz=cy*stride;
if (FAILED(pIWICBS->CopyPixels(NULL, stride, buf_sz, (BYTE*)dib_buf))) {
DeleteObject(uac_bitmap);
uac_bitmap=NULL;
}
}
ReleaseDC(NULL, hdc);
}
}
pIWICBS->Release();
}
pIWICB->Release();
}
pIWICIF->Release();
}
CoUninitialize();
return uac_bitmap;
}