2
* ChangeAutorun.c - Change autorun for a new and/or existing user
4
* Copyright (C) 2012 Ray Satiro on behalf of the Workrave project
7
* This program is free software: you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation, either version 3 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program. If not, see <http:*www.gnu.org/licenses/>.
23
* README, CHANGES REQUIRED
25
* This program, ChangeAutorun, is called by Active Setup to change the autorun settings for a new
26
* and/or existing user of your program.
28
* Your installer should have added to its Active Setup key a StubPath value with data that will run
29
* this program. For more information search the internet for how to configure Active Setup.
31
* "StubPath"="\"C:\\Program Files\\Workrave\\lib\\ChangeAutorun.exe\" -b"
33
* Typically you'd set your Active Setup StubPath to call ChangeAutorun with 'a' or 'b' or 'u'.
34
* Run ChangeAutorun /? for all options.
36
* Options 'v' and 'z' are for testing purposes and must NOT be options in your ActiveSetup StubPath.
37
* Either of those options will routinely show MessageBoxes with information only a developer could
38
* care about and require user input to click 'OK' to the messages, stalling explorer initialization.
41
* If this is not the Workrave project you MUST change everything below (until the /END block), and
42
* not use the GUID 180B0AC5-6FDA-438B-9466-C9894322B6BA. Generate your own GUID.
45
/* This is the Active Setup GUID subkey generated by your installer to store its Active Setup. */
46
#define ACTIVE_SETUP_GUID "{180B0AC5-6FDA-438B-9466-C9894322B6BA}"
48
/* This is the autorun value stored in the key HKCU_AUTORUN_KEY. The value must be unique.
49
This program will add a value of AUTORUN_VALUE to the key with data pointing to the target, eg:
50
"Workrave"="\"C:\\Program Files\\Workrave\\lib\\workrave.exe\""
52
#define AUTORUN_VALUE "Workrave"
54
/* This is the name of your project. It is the name that appears in this program's verbose output.
56
#define PROJECT "Workrave"
58
/* This is an HKCU key that has been set by your program after it is run by the user. It should not
59
be a key that is set by your installer. If ChangeAutorun is run with the -b option it needs to
60
determine whether or not the target program has previously been run by the user. It checks for the
61
existence of this key to determine that.
63
#define HKCU_PROJECT_KEY "AppEvents\\Schemes\\Apps\\Workrave"
65
/* This is your target program's executable name, with extension, and without path. It must be in
66
the same directory as this program.
68
#define TARGET "workrave.exe"
70
/* These are the options to pass to the target program.
72
"--somearg \"abc\" -asdf"
75
#define TARGET_OPTIONS ""
78
* /END REQUIRED CHANGES
79
* You shouldn't need to change anything below here.
86
* I wrote this to create an autorun entry for an x86 target program (TARGET). When compiled x86,
87
* and run on x86 or x64 platform it will access the 32-bit ACTIVE_SETUP_GUID, HKCU_PROJECT_KEY, etc.
89
* You could probably use this program with an x64 TARGET if you compile it x64, and your installer
90
* writes to the 64-bit ACTIVE_SETUP_GUID. In that case this program should access the 64-bit keys.
91
* I haven't tested that. If you do test it let me know what happens.
93
* cl /O2 /W4 /DUNICODE ChangeAutorun.c
94
* gcc -O2 -Wall -Wextra -Wno-unknown-pragmas -Wno-unused-parameter -DUNICODE -mwindows -o ChangeAutorun.exe ChangeAutorun.c -lpsapi
98
/* Disable cl unused parameter warnings */
99
#pragma warning( disable : 4100 )
101
#pragma comment( lib, "psapi.lib" )
102
#pragma comment( lib, "user32.lib" )
103
#pragma comment( lib, "advapi32.lib" )
105
#pragma comment( linker, "/SUBSYSTEM:WINDOWS" )
108
/* Define both UNICODEs before any includes.
109
http://blogs.msdn.com/b/oldnewthing/archive/2004/02/12/71851.aspx#73016
110
http://en.wikibooks.org/wiki/Windows_Programming/Unicode#Unicode_Environment
112
#if defined UNICODE && !defined _UNICODE
116
#if defined _UNICODE && !defined UNICODE
120
#if defined _UNICODE && defined _MBCS
121
#error Both _UNICODE and _MBCS are defined. Those defines are mutually exclusive.
124
/* Use the original GetModuleFilenameEx() location.
125
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683198.aspx
127
#define PSAPI_VERSION 1
129
#define _CRT_SECURE_NO_WARNINGS
130
#define _WIN32_WINNT 0x0501
141
/* MinGW has KEY_WOW64 defines only when >= 0x502 */
142
#ifndef KEY_WOW64_64KEY
143
#define KEY_WOW64_64KEY 0x0100
146
#ifndef KEY_WOW64_32KEY
147
#define KEY_WOW64_32KEY 0x0200
151
#define VERBOSE_ERR(msg) VERBOSE_ERR_DATA2(msg,NULL,NULL)
152
#define VERBOSE_ERR_DATA(msg,tstr1) VERBOSE_ERR_DATA2(msg,tstr1,NULL)
153
#define VERBOSE_ERR_DATA2(msg,tstr1,tstr2) \
155
show_message( __LINE__, __FUNCTION__, msg, _T("Error"), tstr1, tstr2 )
157
#define VERBOSE_MSG(msg) VERBOSE_MSG_DATA2(msg,NULL,NULL)
158
#define VERBOSE_MSG_DATA(msg,tstr1) VERBOSE_MSG_DATA2(msg,tstr1,NULL)
159
#define VERBOSE_MSG_DATA2(msg,tstr1,tstr2) \
161
show_message( __LINE__, __FUNCTION__, msg, _T("Message"), tstr1, tstr2 )
164
#define HKCU_AUTORUN_KEY "Software\\Microsoft\\Windows\\CurrentVersion\\Run"
167
/* if _UNICODE then the lengths of these global variables will be a count of wide characters in the
168
string. if not _UNICODE then the lengths will be a count of single byte characters. lengths here
169
will not be a count of multibyte characters even if _MBCS.
172
/* The target's full image name. Initialized at runtime. Example:
173
C:\Program Files\Workrave\lib\workrave.exe
175
TCHAR *target_fullname;
176
int length_target_fullname;
178
/* The target's directory. Initialized at runtime. Example:
179
C:\Program Files\Workrave\lib\
181
TCHAR *target_directory;
182
int length_target_directory;
184
/* The command line for the autorun entry. Initialized at runtime. Example:
185
"C:\Program Files\Workrave\lib\workrave.exe" -whatever "some arg with spaces" -abc
187
TCHAR *target_command;
188
int length_target_command;
191
BOOL g_verbose = FALSE;
196
IGNORE_THIS_TAG = -1, /* force gcc signed enum */
199
UPDATE_EXISTING_AUTORUN_ONLY,
205
/* Shows a MessageBox with information. Used by the VERBOSE_ macro functions. */
208
const char *function,
209
const TCHAR *message,
215
TCHAR *buffer = NULL;
216
int length_buffer = 0;
217
int max_length_buffer = 1024;
221
max_length_buffer += _tcslen( data );
224
max_length_buffer += _tcslen( data2 );
226
buffer = malloc( ( max_length_buffer + 1 ) * sizeof( TCHAR ) );
234
_T("%s - ChangeAutorun\n")
236
_T("Function: %hs()\n")
237
_T("Project: ") _T(PROJECT) _T("\n")
240
( ( !title || !*title ) ? _T("<no title>") : title ),
242
( ( !function || !*function ) ? "<no function>" : function ),
243
( ( !message || !*message ) ? _T("<no message>") : message ),
244
( ( data || data2 ) ? _T("\n\nRelated data:\n") : _T("") ),
245
( data ? data : _T("") ),
246
( data2 ? data2 : _T("") )
249
if( length_buffer < 0 )
250
length_buffer = max_length_buffer;
252
buffer[ length_buffer ] = _T('\0');
254
MessageBox( 0, buffer, ( ( !title || !*title ) ? _T("<no title>") : title ), 0 );
262
/* Check if a file exists. Returns TRUE on success. */
263
BOOL file_exists( const TCHAR *filename )
278
if( hFile == INVALID_HANDLE_VALUE )
281
CloseHandle( hFile );
287
/* Check if a registry key exists and can be opened for read. Returns TRUE on success. */
288
BOOL regkey_test_read( HKEY mainkey, const TCHAR *subkey )
297
STANDARD_RIGHTS_READ,
312
/* Frees and zeroes all global variables except g_verbose and any const. */
313
void free_global_variables()
315
if( target_directory )
317
free( target_directory );
318
target_directory = NULL;
320
length_target_directory = 0;
322
if( target_fullname )
324
free( target_fullname );
325
target_fullname = NULL;
327
length_target_fullname = 0;
331
free( target_command );
332
target_command = NULL;
334
length_target_command = 0;
341
/* init_global_variables()
343
Initialize all global variables except g_verbose and any const.
345
Returns TRUE if all variables were successfully initialized. If only some variables were
346
initialized, those that couldn't be initialized will be cleared and this function will return FALSE.
348
BOOL init_global_variables()
352
int length_target = 0;
353
int max_length_target_command = 0;
356
free_global_variables();
360
Get this program's path from GetModuleFileNameEx().
361
When using that function there's no sure way to know the exact size beforehand.
363
for( i = 512; i <= 65536; i *= 2, length_target_directory = 0 )
365
temp = realloc( target_directory, ( i * sizeof( TCHAR ) ) );
369
VERBOSE_ERR( _T("realloc() failed. Could not initialize target_directory.") );
373
target_directory = temp;
375
length_target_directory =
383
if( length_target_directory
384
&& ( ( length_target_directory + 1 ) < i )
389
if( !length_target_directory )
391
VERBOSE_ERR( _T("GetModuleFileNameEx() failed. Could not initialize target_directory.") );
395
target_directory[ length_target_directory ] = _T('\0');
397
if( !file_exists( target_directory ) )
400
_T("File not found. Could not initialize target_directory."),
404
length_target_directory = 0;
408
temp = _tcsrchr( target_directory, _T('\\') );
413
_T("Backslash not found in string. Could not initialize target_directory."),
417
length_target_directory = 0;
423
length_target_directory = temp - target_directory;
427
Append the executable name (TARGET) to target_directory.
429
length_target = _tcslen( _T(TARGET) );
430
length_target_fullname = length_target_directory + length_target;
432
target_fullname = malloc( ( length_target_fullname + 1 ) * sizeof( TCHAR ) );
433
if( !target_fullname )
435
VERBOSE_ERR( _T("malloc() failed. Could not initialize target_fullname.") );
437
length_target_fullname = 0;
441
temp = target_fullname;
443
memcpy( temp, target_directory, ( length_target_directory * sizeof( TCHAR ) ) );
444
temp += length_target_directory;
446
memcpy( temp, _T(TARGET), ( length_target * sizeof( TCHAR ) ) );
447
temp += length_target;
451
if( !file_exists( target_fullname ) )
454
_T("File not found. Could not initialize target_fullname."),
458
length_target_fullname = 0;
464
Make the target command to execute from the target_fullname and TARGET_OPTIONS
466
max_length_target_command = 1024 + length_target_fullname;
468
target_command = malloc( ( max_length_target_command + 1 ) * sizeof( TCHAR ) );
469
if( !target_command )
471
VERBOSE_ERR( _T("malloc() failed. Could not initialize target_command.") );
473
length_target_command = 0;
477
/* what follows target_fullname cannot be all trailing spaces.
478
"\"C:\whatever.exe\" "
479
the legacy run key will not handle that properly and would not start whatever.exe
480
it will not start a program with trailing spaces. if there are options then trailing spaces can
481
follow that, so this is ok:
482
"\"C:\whatever.exe\" --arf "
484
for( temp = _T(TARGET_OPTIONS); *temp == _T(' '); ++temp )
487
length_target_command =
490
max_length_target_command,
493
( *temp ? _T(" ") : _T("") ),
497
if( length_target_command < 0 )
500
_T("_sntprintf() failed. Could not initialize target_command."),
505
length_target_command = 0;
509
target_command[ length_target_command ] = _T('\0');
512
if( !length_target_directory )
514
free( target_directory );
515
target_directory = NULL;
518
if( !target_directory )
519
length_target_directory = 0;
521
if( !length_target_fullname )
523
free( target_fullname );
524
target_fullname = NULL;
527
if( !target_fullname )
528
length_target_fullname = 0;
530
if( !length_target_command )
532
free( target_command );
533
target_command = NULL;
536
if( !target_command )
537
length_target_command = 0;
539
return( target_directory && target_fullname && target_command );
544
/* add_autorun_entry()
546
Adds an autorun entry for your program. Returns TRUE on success.
548
if 'users' == NEW_USERS then add only if TARGET has never before been started by the user.
549
if 'users' == ALL_USERS then add always.
550
if 'users' == UPDATE_EXISTING_AUTORUN_ONLY then only update an existing entry.
552
regardless of the value of 'users', if the user already has an autorun entry it is updated with the
555
To determine if your TARGET program has ever before been run by the user, this function checks for
556
the existence of HKCU registry key HKCU_PROJECT_KEY.
558
BOOL add_autorun_entry( enum users users )
564
if( !target_command )
566
VERBOSE_ERR( _T("Parameter validation failed. target_command == NULL") );
570
if( ( users < 0 ) || ( users >= INVALID ) )
572
VERBOSE_ERR( _T("Parameter validation failed. users enumeration tag unrecognized.") );
577
ret = RegCreateKeyEx(
579
_T(HKCU_AUTORUN_KEY),
583
( KEY_READ | KEY_WRITE ),
592
_T("RegCreateKeyEx() failed. The HKCU autorun key couldn't be opened for writing."),
600
/* if the autorun value doesn't exist */
601
if( RegQueryValueEx( hkey, _T(AUTORUN_VALUE), NULL, NULL, NULL, NULL ) )
603
/* if setting an autorun entry for new users, and the user isn't new (project key exists) */
604
if( ( users == NEW_USERS )
605
&& regkey_test_read( HKEY_CURRENT_USER, _T(HKCU_PROJECT_KEY) )
609
_T("Existing project key and no existing autorun entry. No entry has been added."),
615
else if( users == UPDATE_EXISTING_AUTORUN_ONLY )
617
VERBOSE_ERR( _T("No existing autorun entry to update.") );
629
(const BYTE *)target_command,
630
( ( length_target_command + 1 ) * sizeof( TCHAR ) )
638
_T("RegSetValueEx() failed. The autorun value couldn't be written."),
646
VERBOSE_MSG_DATA( _T("The autorun data was successfully added."), target_command );
652
/* Returns TRUE if your project's HKCU autorun entry was deleted. */
653
BOOL remove_autorun_entry()
661
_T(HKCU_AUTORUN_KEY),
663
( KEY_READ | KEY_WRITE ),
670
_T("RegOpenKeyEx() failed. The HKCU autorun key doesn't exist or couldn't be read."),
677
ret = RegQueryValueEx( hkey, _T(AUTORUN_VALUE), NULL, NULL, NULL, NULL );
681
if( ( ret == ERROR_FILE_NOT_FOUND ) || ( ret == ERROR_PATH_NOT_FOUND ) )
684
_T("The autorun value does not exist. There is nothing to delete."),
691
_T("RegQueryValueEx() failed. The value could not be read."),
699
ret = RegDeleteValue( hkey, _T(AUTORUN_VALUE) );
706
_T("RegDeleteValue() failed. The autorun value doesn't exist or could not be read."),
713
VERBOSE_MSG( _T("Your project's autorun entry was successfully deleted.") );
719
/* Returns TRUE if your project's HKCU Active Setup key was deleted. */
720
BOOL remove_activesetup_entry()
722
BOOL deleted = FALSE;
723
LONG ( WINAPI *pRegDeleteKeyEx )( HKEY, LPCTSTR, REGSAM, DWORD ) = NULL;
725
TCHAR *subkey_default =
726
_T("SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\")
727
_T(ACTIVE_SETUP_GUID);
730
_T("SOFTWARE\\Wow6432Node\\Microsoft\\Active Setup\\Installed Components\\")
731
_T(ACTIVE_SETUP_GUID);
734
if( !RegDeleteKey( HKEY_CURRENT_USER, subkey_default ) )
737
/* This one probably won't work. */
738
if( !RegDeleteKey( HKEY_CURRENT_USER, subkey_wow ) )
742
pRegDeleteKeyEx = ( LONG ( WINAPI * )( HKEY, LPCTSTR, REGSAM, DWORD ) )
744
GetModuleHandleA( "Advapi32" ),
745
( ( sizeof( TCHAR ) == 1 ) ? "RegDeleteKeyExA" : "RegDeleteKeyExW" )
748
if( pRegDeleteKeyEx )
750
if( !pRegDeleteKeyEx( HKEY_CURRENT_USER, subkey_default, KEY_WOW64_32KEY, 0 ) )
753
if( !pRegDeleteKeyEx( HKEY_CURRENT_USER, subkey_default, KEY_WOW64_64KEY, 0 ) )
761
_T("Your project's Active Setup key either does not exist or could not be deleted."),
768
VERBOSE_MSG( _T("Your project's Active Setup key was successfully deleted.") );
774
/* Returns FALSE if tstring is NULL or is empty or contains only whitespace. */
775
BOOL param_check_tstring( const TCHAR *tstring )
780
while( ( *tstring == _T(' ') ) || ( *tstring == _T('\t') ) )
788
/* Returns FALSE if some string literal is empty that shouldn't be. */
789
BOOL check_global_literals()
793
#define CHECK_LITERAL(literal) \
794
if( !param_check_tstring( _T(literal) ) ) \
798
_T("Program to fail. tstring literal is empty or contains only whitespace."), \
803
CHECK_LITERAL( HKCU_AUTORUN_KEY );
804
CHECK_LITERAL( ACTIVE_SETUP_GUID );
805
CHECK_LITERAL( AUTORUN_VALUE );
806
CHECK_LITERAL( PROJECT );
807
CHECK_LITERAL( HKCU_PROJECT_KEY );
808
CHECK_LITERAL( TARGET );
809
/* TARGET_OPTIONS can be empty */
820
"This is a configuration tool added by this project's installer.\n"
821
"This program is invoked by Microsoft's undocumented Active Setup.\n"
822
"If you do not understand what this program does don't use it.\n"
824
"ACTIVE_SETUP_GUID: " ACTIVE_SETUP_GUID "\n"
825
"AUTORUN_VALUE: " AUTORUN_VALUE "\n"
826
"PROJECT: " PROJECT "\n"
827
"HKCU_PROJECT_KEY: " HKCU_PROJECT_KEY "\n"
828
"TARGET: " TARGET "\n"
829
"TARGET_OPTIONS: " TARGET_OPTIONS "\n"
832
"-a" "\t" "Add autorun always.\n"
833
"-b" "\t" "Add autorun only if user has never before used your project.\n"
834
"-d" "\t" "Remove the autorun entry.\n"
835
"-r" "\t" "Remove the Active Setup entry.\n"
836
"-u" "\t" "Update existing autorun entry.\n"
837
"-v" "\t" "Be verbose. If multiple arguments this must be the first.\n"
838
"-z" "\t" "Show each passed in argument for testing purposes.\n"
840
"The 'Add autorun' options are mutually exclusive. Using either \n"
841
"implies option 'u', which updates an existing autorun entry, if any.\n"
843
"Option 'r' removes the HKCU Active Setup entry, if any, and not the\n"
844
"HKLM Active Setup entry. What that means is Active Setup will be\n"
845
"reset for the user, and will run the next time the user logs on.\n",
846
"Help - ChangeAutorun",
855
void just_testing( int argc, char **argv )
859
for( i = 0; i < argc; ++i )
860
MessageBoxA( 0, argv[ i ], argv[ i ], 0 );
863
MessageBoxA( 0, "Final argv pointer is not NULL", "WARNING", 0 );
870
int APIENTRY WinMain(
872
HINSTANCE hPrevInstance,
883
( SEM_FAILCRITICALERRORS
884
| SEM_NOGPFAULTERRORBOX
885
| SEM_NOOPENFILEERRORBOX )
894
&& ( ( argv[ 1 ][ 0 ] == '/' )
895
|| ( argv[ 1 ][ 0 ] == '-' ) )
898
if( ( argv[ 1 ][ 1 ] == 'v' )
899
|| ( argv[ 1 ][ 1 ] == 'V' )
904
else if( ( argv[ 1 ][ 1 ] == '?' )
905
|| ( argv[ 1 ][ 1 ] == 'h' )
906
|| ( argv[ 1 ][ 1 ] == 'H' )
915
if( !check_global_literals() )
918
init_global_variables();
920
for( i = 1; i < argc; ++i )
922
if( argv[ i ][ 0 ] != '-' && argv[ i ][ 0 ] != '/' )
925
for( j = 1; argv[ i ][ j ]; ++j )
927
switch( argv[ i ][ j ] )
931
add_autorun_entry( ALL_USERS );
936
add_autorun_entry( NEW_USERS );
941
remove_autorun_entry();
946
remove_activesetup_entry();
951
add_autorun_entry( UPDATE_EXISTING_AUTORUN_ONLY );
956
just_testing( argc, argv );
962
free_global_variables();