1
// W32ActiveSetup.cc --- Active Setup for Workrave
3
// Copyright (C) 2012 Ray Satiro
4
// All rights reserved.
6
// This program is free software: you can redistribute it and/or modify
7
// it under the terms of the GNU General Public License as published by
8
// the Free Software Foundation, either version 3 of the License, or
9
// (at your option) any later version.
11
// This program is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
// GNU General Public License for more details.
16
// You should have received a copy of the GNU General Public License
17
// along with this program. If not, see <http://www.gnu.org/licenses/>.
20
/* Active Setup is post install data that's set by the Inno Setup installer.
22
After Active Setup data is set it will be run for each user the next time that user logs on.
23
However, in the case that the user hasn't yet logged off and then back on, any programs specified
24
by Active Setup have not yet ran.
26
In order to ensure the proper behavior of Workrave, Workrave's Active Setup key(s) should always be
27
checked before Workrave loads, and run if they haven't already. This module emulates the behavior
28
of Microsoft's Active Setup.
30
##############################################WARNING##############################################
31
Active Setup is undocumented and can be dangerous if not used properly. Its behavior is blocking
32
and there is no interaction available to the user in a typical case. In a typical case it will
33
block the explorer shell, with explorer running the Active Setup programs hidden from view and
34
waiting for them to complete before completing user initialization on logon. This emulation is very
35
similar. It was designed for the Workrave application to block before continuing execution until
36
Workrave's Active Setup programs have run and terminated. Therefore, it is extremely important that
37
any program put under the purview of Active Setup does not require any user interaction, does not
38
have any error message boxes, and has all its dependencies (so there are no message boxes about
39
failure to load libraries). The Workrave Active Setup program (I've only written one so far -- to
40
change a user's autorun settings) takes an extra step, to void user interaction during any
58
#include "W32ActiveSetup.hh"
64
/* using 32-bit registry view on 32-bit OS, 64-bit registry view on 64-bit OS
65
http://support.microsoft.com/kb/907660
67
const REGSAM W32ActiveSetup::registry_view = ( W32ActiveSetup::is_os_64() ? KEY_WOW64_64KEY : KEY_WOW64_32KEY );
69
/* The subkey path to HKLM/HKCU Active Setup GUIDs */
70
const wchar_t W32ActiveSetup::component_path[] = L"SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\";
72
/* Right now there is only one GUID key set by Inno and it's used by Workrave's 'ChangeAutorun'
73
program, which adds/updates each user's Workrave autorun setting.
75
const wchar_t W32ActiveSetup::guid_autorun[] = L"{180B0AC5-6FDA-438B-9466-C9894322B6BA}";
79
/* W32ActiveSetup::is_os_64()
81
returns true if the windows operating system is 64-bit
83
bool W32ActiveSetup::is_os_64()
85
SYSTEM_INFO si = { 0, };
87
GetNativeSystemInfo( &si );
89
return( ( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 )
90
|| ( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 )
96
/* W32ActiveSetup::get_user_profile_dir()
98
returns the path contained in environment variable %USERPROFILE%
100
const WCHAR *W32ActiveSetup::get_user_profile_dir()
102
static vector<WCHAR> buffer;
104
if( buffer.size() && *buffer.begin() )
107
WCHAR *env_var = L"USERPROFILE";
109
DWORD ret = GetEnvironmentVariableW( env_var, NULL, 0 );
110
if( !ret || ( ret > 32767 ) )
113
buffer.resize( ret );
115
ret = GetEnvironmentVariableW( env_var, &buffer[ 0 ], buffer.size() );
116
if( !ret || ( ret >= buffer.size() ) || !*buffer.begin() )
124
/* W32ActiveSetup::check_guid()
126
Check for the existence of an Active Setup GUID key (HKLM or HKCU).
128
returns true if the key exists
130
bool W32ActiveSetup::check_guid(
131
const enum reg reg, // HKLM or HKCU
135
HKEY root_hkey = NULL;
140
root_hkey = HKEY_LOCAL_MACHINE;
143
root_hkey = HKEY_CURRENT_USER;
149
wstring keyname( W32ActiveSetup::component_path );
153
LONG ret = RegOpenKeyExW(
157
( STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | W32ActiveSetup::registry_view ),
170
/* W32ActiveSetup::is_guid_enabled()
172
returns true if the HKLM Active Setup GUID key exists and the key value "IsInstalled" either does
173
not exist or does exist and does not have both a REG_DWORD type and data 0.
175
Those conditions are based on the my testing in Windows XP SP3 x86. In most cases for GUID keys
176
"IsInstalled" exists with a REG_DWORD type and data 1, in which case this function returns true.
178
bool W32ActiveSetup::is_guid_enabled(
185
wstring keyname( W32ActiveSetup::component_path );
189
LONG ret = RegOpenKeyExW(
193
( STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | W32ActiveSetup::registry_view ),
200
DWORD type = REG_NONE;
202
DWORD bytelen = sizeof( data );
203
ret = RegQueryValueExW(
215
&& ( type == REG_DWORD )
216
&& ( bytelen == sizeof( data ) )
226
/* W32ActiveSetup::read_from_registry_value()
228
Get the REG_SZ data from a registry value in an Active Setup GUID key (HKLM or HKCU).
230
Before this function does anything else 'data' is cleared. 'data' may be empty even on success.
232
returns true if the value exists in the registry. 'data' receives the value's data, if any.
234
bool W32ActiveSetup::read_from_registry_value(
235
const enum reg reg, // HKLM or HKCU
237
const wstring &value,
243
if( guid.empty() || value.empty() )
246
HKEY root_hkey = NULL;
251
root_hkey = HKEY_LOCAL_MACHINE;
254
root_hkey = HKEY_CURRENT_USER;
260
wstring keyname( W32ActiveSetup::component_path );
264
LONG ret = RegOpenKeyExW(
268
( STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | W32ActiveSetup::registry_view ),
275
DWORD type = REG_NONE, bytelen = 0;
276
ret = RegQueryValueExW(
285
if( ret || !bytelen || ( type != REG_SZ ) )
291
vector<BYTE> buffer( bytelen + sizeof( wchar_t ) );
293
ret = RegQueryValueExW(
304
if( ret || ( type != REG_SZ ) )
307
data = (wchar_t *)&buffer[ 0 ];
313
/* W32ActiveSetup::write_to_registry_value()
315
Set a value and its REG_SZ data in an Active Setup GUID key (HKCU only).
317
If the HKCU Active Setup GUID key does not exist this function will create it.
318
If 'value' is empty this function sets the GUID key's default value.
319
'data' can also be an empty string.
321
returns true if the value and its data (if any) were set in the registry.
323
bool W32ActiveSetup::write_to_registry_value(
325
const wstring &value,
332
wstring keyname( W32ActiveSetup::component_path );
337
LONG ret = RegCreateKeyExW(
343
( KEY_READ | KEY_WRITE | W32ActiveSetup::registry_view ),
352
ret = RegSetValueExW(
357
(const BYTE *)data.c_str(),
358
( ( data.length() + 1 ) * sizeof( wchar_t ) )
368
/* W32ActiveSetup::get_version()
369
Converts the REG_SZ data from value "Version" in an Active Setup GUID key (HKLM or HKCU) into a
372
Active Setup observations:
373
A valid "Version" value string of a GUID key has four parts or less, separated by comma. If a
374
number is greater than dword max the version is still valid, and that number is wrapped around.
376
"2012,05,10,023701" returns DWORDs 2012, 5, 10, 23701
377
"1" returns DWORDs 1, 0, 0, 0
378
"" or "0" returns DWORDs 0, 0, 0, 0
379
"1,2,3,4,5" (invalid) returns DWORDs 0, 0, 0, 0
380
"25,4294967299,77,4" returns DWORDs 25, 3, 77, 4
382
Before this function does anything else 'version' is cleared and then resized to 4 DWORDs.
384
returns true if the "Version" value exists in the registry and it's valid. 'version' receives the
385
version as a vector of 4 DWORDs.
387
bool W32ActiveSetup::get_version(
388
const enum reg reg, // HKLM or HKCU
390
vector<DWORD> &version // out
394
version.resize( 4, 0 );
397
if( !read_from_registry_value( reg, guid, L"Version", data ) )
400
/* testing shows anything other than these characters invalidates the version string.
401
Active Setup treats it the same as a version where all parts are zeroes
403
size_t pos = data.find_first_not_of( L"0123456789," );
404
if( pos != wstring::npos )
407
/* testing shows if there is more than 3 commas that invalidates the version string.
408
Active Setup treats it the same as a version where all parts are zeroes
410
for( unsigned i = 0, count = 0; i < data.length(); ++i )
412
if( data[ i ] == L',' )
420
wstringstream ss( data );
422
for( unsigned i = 0; i < 4; ++i )
426
if( !getline( ss, s, L',' ) )
429
for( unsigned j = 0; j < s.length(); ++j )
432
version[ i ] += s[ j ] - 48;
441
/* W32ActiveSetup::set_version()
443
Converts a vector of 4 DWORDs to a comma separated string of unsigned numbers and sets it as the
444
REG_SZ data for the "Version" value of an Active Setup GUID key.
446
returns true on success
448
bool W32ActiveSetup::set_version(
450
const vector<DWORD> &version
453
if( version.size() != 4 )
457
ss << version[ 0 ] << "," << version[ 1 ] << "," << version[ 2 ] << "," << version[ 3 ];
459
return write_to_registry_value( guid, L"Version", ss.str() );
464
/* W32ActiveSetup::update()
466
This function emulates Active Setup behavior. It should be called synchronously by the main thread
467
before Workrave is initialized.
469
The way Active Setup basically works is it checks the HKLM GUID key for a "Version" REG_SZ value
470
and compares it to the same HKCU GUID key's "Version" value. If the HKCU GUID key does not exist,
471
or its "Version" value does not exist, or its version is less than the HKLM version then Active
472
Setup will add/update the HKCU GUID key by copying any HKLM "Version" value to the HKCU "Version"
473
value, and then execute the string contained in HKLM GUID key's "StubPath" value, if any.
474
http://www.sepago.de/helge/2010/04/22/active-setup-explained/
476
returns true if the HKLM Active Setup GUID key needed to be added/updated in HKCU Active Setup.
478
bool W32ActiveSetup::update(
483
|| !check_guid( HKLM, guid )
484
|| !is_guid_enabled( guid )
488
vector<DWORD> hklm_version;
489
get_version( HKLM, guid, hklm_version );
491
if( check_guid( HKCU, guid ) )
493
vector<DWORD> hkcu_version;
494
get_version( HKCU, guid, hkcu_version );
496
if( hkcu_version >= hklm_version )
500
if( !set_version( guid, hklm_version ) )
503
// At this point HKCU Active Setup has been updated so any return should be true
507
if( read_from_registry_value( HKLM, guid, L"Locale", data ) )
508
write_to_registry_value( guid, L"Locale", data );
510
if( !read_from_registry_value( HKLM, guid, L"StubPath", data )
515
vector<WCHAR> buffer( data.begin(), data.end() );
516
buffer.push_back( L'\0' );
518
HANDLE thread = CreateThread( NULL, 0, create_process, &buffer[ 0 ], 0, NULL );
521
WaitForSingleObject( thread, INFINITE );
522
CloseHandle( thread );
526
/* Active Setup only attempts to run the StubPath, it doesn't record whether or not it was
527
successful in doing so.
535
/* W32ActiveSetup::update_all()
537
returns true if any of Workrave's HKLM Active Setup GUID keys needed to be installed in HKCU
540
bool W32ActiveSetup::update_all()
542
/* For now there's only one entry, for setting up Workrave's autorun for each user */
543
return update( W32ActiveSetup::guid_autorun );
548
/* W32ActiveSetup::create_process()
550
This is the starting function for a thread that creates a process and waits for it to terminate.
552
DWORD WINAPI W32ActiveSetup::create_process(
556
DWORD exit_code = ((DWORD) -1);
557
WCHAR *command = (WCHAR *)lpParam;
558
STARTUPINFOW si = { sizeof( si ), };
559
PROCESS_INFORMATION pi = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, };
561
if( !command || !*command )
565
void *OldValue = NULL;
566
bool wow64_disabled = false;
567
BOOL ( WINAPI *pWow64DisableWow64FsRedirection )( void ** ) = NULL;
568
BOOL ( WINAPI *pWow64RevertWow64FsRedirection )( void * ) = NULL;
570
if( W32ActiveSetup::is_os_64() )
572
pWow64DisableWow64FsRedirection = ( BOOL ( WINAPI * )( void ** ) )
573
GetProcAddress( GetModuleHandleA( "kernel32" ), "Wow64DisableWow64FsRedirection" );
575
pWow64RevertWow64FsRedirection = ( BOOL ( WINAPI * )( void * ) )
576
GetProcAddress( GetModuleHandleA( "kernel32" ), "Wow64RevertWow64FsRedirection" );
580
if( pWow64DisableWow64FsRedirection && pWow64RevertWow64FsRedirection )
581
wow64_disabled = !!pWow64DisableWow64FsRedirection( &OldValue );
583
BOOL ret = CreateProcessW(
591
get_user_profile_dir(),
596
if( wow64_disabled && pWow64DisableWow64FsRedirection && pWow64RevertWow64FsRedirection )
597
wow64_disabled = !pWow64RevertWow64FsRedirection( OldValue );
602
if( pi.hProcess != INVALID_HANDLE_VALUE )
604
WaitForSingleObject( pi.hProcess, INFINITE );
606
if( !GetExitCodeProcess( pi.hProcess, &exit_code ) )
607
exit_code = ((DWORD) -1);
609
CloseHandle( pi.hProcess );
612
if( pi.hThread != INVALID_HANDLE_VALUE )
613
CloseHandle( pi.hThread );