/*++ Copyright (c) 1991 Microsoft Corporation Module Name: Apitest.c Abstract: This module contains the function test for the Win32 Registry API. Author: David J. Gilman (davegi) 28-Dec-1991 Environment: Windows, Crt - User Mode Notes: This test can be compiled for Unicode by defining the compiler symbol UNICODE. Since this is a test program it relies on assertions for error checking rather than a more robust mechanism. --*/ #include #include #include #include #include "crtools.h" #define HKEY_ROOT HKEY_CURRENT_USER #define SAVE_RESTORE_FILE TEXT( "srkey.reg" ) #define KEY_PATH \ TEXT( "TestUser1\\TestUser1_1\\TestUser1_2" ) #define PREDEFINED_HANDLE HKEY_USERS #define PREDEFINED_HANDLE_STRING \ TEXT( "HKEY_USERS\\.Default\\TestUser1" ) #define KEY_NAME_1 TEXT( "TestUser1" ) #define KEY_NAME_1_TITLE_INDEX ( 0 ) #define KEY_NAME_1_CLASS TEXT( "Test User Class" ) #define KEY_NAME_1_CLASS_LENGTH LENGTH( KEY_NAME_1_CLASS ) #define KEY_NAME_1_1 TEXT( "TestUser1_1" ) #define KEY_NAME_1_1_LENGTH LENGTH( KEY_NAME_1_1 ) #define KEY_NAME_1_1_TITLE_INDEX ( 0 ) #define KEY_NAME_1_1_CLASS TEXT( "Test User Class" ) #define KEY_NAME_1_1_CLASS_LENGTH LENGTH( KEY_NAME_1_1_CLASS ) #define KEY_NAME_1_2 TEXT( "TestUser1_2" ) #define KEY_NAME_1_2_LENGTH LENGTH( KEY_NAME_1_2 ) #define KEY_NAME_1_2_TITLE_INDEX (0 ) #define KEY_NAME_1_2_CLASS TEXT( "Test User Class" ) #define KEY_NAME_1_2_CLASS_LENGTH LENGTH( KEY_NAME_1_2_CLASS ) #define VALUE_NAME_1 TEXT( "One" ) #define VALUE_NAME_1_LENGTH LENGTH( VALUE_NAME_1 ) #define VALUE_NAME_1_TITLE_INDEX 0 #define VALUE_DATA_1 "Number One" #define VALUE_DATA_1_LENGTH 11 #define VALUE_DATA_1_TYPE REG_SZ #define VALUE_NAME_2 TEXT( "Second" ) #define VALUE_NAME_2_LENGTH LENGTH( VALUE_NAME_2 ) #define VALUE_NAME_2_TITLE_INDEX ( 0 ) #define VALUE_DATA_2 ( 2 ) #define VALUE_DATA_2_LENGTH ( sizeof( VALUE_DATA_2 )) #define VALUE_DATA_2_TYPE REG_DWORD #define MAX_DATA_LENGTH ( 32 ) // // Root handle for apitest's nodes. // HKEY RootHandle; // // Error and informational messages. // PSTR UsageMessage = "Usage: apitest [-?] [-q] [\\machinename]\n"; PSTR HelpMessage = "\n where:\n" \ " -? - display this message.\n" \ " -q - quiet - suppresses all output\n" \ " machinename - remote machine.\n"; PSTR InvalidSwitchMessage = "Invalid switch - %s\n"; PSTR InvalidMachineNameMessage = "Invalid machine name - %s\n"; // // Event handle used for synchronization. // HANDLE _EventHandle; HANDLE _EventHandle1; HANDLE _EventHandle2; BOOL Quiet; VOID DeleteTree( IN HKEY KeyHandle ) { LONG Error; DWORD Index; HKEY ChildHandle; TSTR KeyName[ MAX_PATH ]; DWORD KeyNameLength; TSTR ClassName[ MAX_PATH ]; DWORD ClassNameLength; DWORD TitleIndex; DWORD NumberOfSubKeys; DWORD MaxSubKeyLength; DWORD MaxClassLength; DWORD NumberOfValues; DWORD MaxValueNameLength; DWORD MaxValueDataLength; DWORD SecurityDescriptorLength; FILETIME LastWriteTime; ClassNameLength = MAX_PATH; Error = RegQueryInfoKey( KeyHandle, ClassName, &ClassNameLength, NULL, &NumberOfSubKeys, &MaxSubKeyLength, &MaxClassLength, &NumberOfValues, &MaxValueNameLength, &MaxValueDataLength, &SecurityDescriptorLength, &LastWriteTime ); REG_API_SUCCESS( RegQueryInfoKey ); for( Index = 0; Index < NumberOfSubKeys; Index++ ) { KeyNameLength = MAX_PATH; Error = RegEnumKey( KeyHandle, 0, // Index, KeyName, KeyNameLength ); REG_API_SUCCESS( RegEnumKey ); Error = RegOpenKey( KeyHandle, KeyName, &ChildHandle ); REG_API_SUCCESS( RegOpenKey ); DeleteTree( ChildHandle ); Error = RegCloseKey( ChildHandle ); REG_API_SUCCESS( RegCloseKey ); Error = RegDeleteKey( KeyHandle, KeyName ); REG_API_SUCCESS( RegDeleteKey ); } } VOID DeleteTestTree( ) { LONG Error; HKEY KeyHandle; Error = RegOpenKey( RootHandle, KEY_NAME_1, &KeyHandle ); if( Error == ERROR_SUCCESS ) { DeleteTree( KeyHandle ); Error = RegCloseKey( KeyHandle ); REG_API_SUCCESS( RegCloseKey ); Error = RegDeleteKey( RootHandle, KEY_NAME_1 ); REG_API_SUCCESS( RegDeleteKey ); } } DWORD NotifyThread( LPVOID Parameters ) { LONG Error; BOOL ErrorFlag; HANDLE EventHandle; UNREFERENCED_PARAMETER( Parameters ); // // Create the notification event. // EventHandle = CreateEvent( NULL, FALSE, FALSE, NULL ); ASSERT( EventHandle != NULL ); // // Set-up an asynchronous notify. // Error = RegNotifyChangeKeyValue( RootHandle, FALSE, REG_LEGAL_CHANGE_FILTER, EventHandle, TRUE ); REG_API_SUCCESS( RegNotifyChangeKeyValue ); // // Release the main thread. // ErrorFlag = SetEvent( _EventHandle ); ASSERT( ErrorFlag == TRUE ); // // Wait for a notification. // Error = (LONG)WaitForSingleObject( EventHandle, (DWORD)-1 ); ASSERT( Error == 0 ); if( ! Quiet ) { printf( "First notification triggered\n" ); } CloseHandle( EventHandle ); EventHandle = CreateEvent( NULL, FALSE, FALSE, NULL ); ASSERT( EventHandle != NULL ); // // Set-up an asynchronous notify. // Error = RegNotifyChangeKeyValue( RootHandle, FALSE, REG_LEGAL_CHANGE_FILTER, EventHandle, TRUE ); REG_API_SUCCESS( RegNotifyChangeKeyValue ); // // Release the main thread. // ErrorFlag = SetEvent( _EventHandle1 ); ASSERT( ErrorFlag == TRUE ); // // Wait for a notification. // Error = (LONG)WaitForSingleObject( EventHandle, (DWORD)-1 ); ASSERT( Error == 0 ); if( ! Quiet ) { printf( "Second notification triggered\n" ); } CloseHandle( EventHandle ); ErrorFlag = SetEvent( _EventHandle2 ); ASSERT( ErrorFlag == TRUE ); #endif return ( DWORD ) TRUE; } VOID main( INT argc, PCHAR argv[ ] ) { LONG Error; BOOL ErrorFlag; DWORD Index; PTSTR MachineName; PKEY Key; TSTR NameString[ MAX_PATH ]; HANDLE NotifyThreadHandle; DWORD ThreadID; HKEY PredefinedHandle; HKEY Handle1; HKEY Handle1_1; HKEY Handle1_2; PSECURITY_DESCRIPTOR SecurityDescriptor; SECURITY_ATTRIBUTES SecurityAttributes; DWORD Disposition; TSTR KeyName[ MAX_PATH ]; DWORD KeyNameLength; TSTR ClassName[ MAX_PATH ]; DWORD ClassNameLength; DWORD NumberOfSubKeys; DWORD MaxSubKeyLength; DWORD MaxClassLength; DWORD NumberOfValues; DWORD MaxValueNameLength; DWORD MaxValueDataLength; DWORD SecurityDescriptorLength; FILETIME LastWriteTime; TSTR ValueName[ MAX_PATH ]; DWORD ValueNameLength; BYTE Data[ MAX_DATA_LENGTH ]; DWORD DataLength; BYTE Data_1[ ] = VALUE_DATA_1; DWORD Data_2 = VALUE_DATA_2; DWORD TitleIndex; DWORD Type; UNREFERENCED_PARAMETER( argc ); // // By default, be verbose and operate on the local machine. // Quiet = FALSE; MachineName = NULL; // // Initialize options based on the command line. // while( *++argv ) { // // If the command line argument is a switch character... // if( isswitch(( *argv )[ 0 ] )) { switch( tolower(( *argv )[ 1 ] )) { // // Display the detailed help message and quit. // case '?': DisplayMessage( FALSE, UsageMessage ); DisplayMessage( TRUE, HelpMessage ); break; // // Quiet - no output. // case 'q': Quiet = TRUE; break; // // Display invalid switch message and quit. // default: DisplayMessage( FALSE, InvalidSwitchMessage, *argv ); DisplayMessage( TRUE, UsageMessage ); } } else { MachineName = *argv; } } // // If a machine name was passed on the command line, connect to // the Registry on that machine else use the local Registry. // In either case construct a string representation of the // test's main key (i.e. \\machine\HKEY_USERS\.Default\TestUser1 or // HKEY_USERS\.Default\TestUser1. // if( MachineName ) { Error = RegConnectRegistry( MachineName, PREDEFINED_HANDLE, &PredefinedHandle ); REG_API_SUCCESS( RegConnectRegistry ); strcpy( NameString, MachineName ); strcat( NameString, "\\\\" ); strcat( NameString, PREDEFINED_HANDLE_STRING ); } else { PredefinedHandle = PREDEFINED_HANDLE; strcpy( NameString, PREDEFINED_HANDLE_STRING ); } // // Open ".Default" key as the root for the remainder of the test. // Error = RegOpenKeyEx( PredefinedHandle, ".Default", REG_OPTION_RESERVED, MAXIMUM_ALLOWED, &RootHandle ); REG_API_SUCCESS( RegOpenKeyEx ); // // Predefined handle is no longer needed. // Error = RegCloseKey( PredefinedHandle ); REG_API_SUCCESS( RegCloseKey ); // // Delete the save / restore file (in case it exists from a previous // run of the test) as RegSaveKey requires a new file. // DeleteFile( SAVE_RESTORE_FILE ); // // Remove any leftover keys from previous runs of this test. // DeleteTestTree( ); // // Use the Win 3.1 API (which calls the Win32 API) to create a path. // Error = RegCreateKey( RootHandle, KEY_PATH, &Handle1 ); REG_API_SUCCESS( RegCreateKey ); // // Close the key so the delete (DeleteTestTree) will work. // Error = RegCloseKey( Handle1 ); REG_API_SUCCESS( RegCloseKey ); // // Remove the path. // DeleteTestTree( ); // // Create the synchronization event. // _EventHandle = CreateEvent( NULL, FALSE, FALSE, NULL ); ASSERT( _EventHandle != NULL ); _EventHandle1 = CreateEvent( NULL, FALSE, FALSE, NULL ); ASSERT( _EventHandle1 != NULL ); _EventHandle2 = CreateEvent( NULL, FALSE, FALSE, NULL ); ASSERT( _EventHandle2 != NULL ); // // Create the notify thread. // NotifyThreadHandle = CreateThread( NULL, 0, NotifyThread, NULL, 0, &ThreadID ); ASSERT( NotifyThreadHandle != NULL ); // // Wait for the notify thread to create its event. // Error = (LONG)WaitForSingleObject( _EventHandle, (DWORD)-1 ); ASSERT( Error == 0 ); // // Use Win 3.1 compatible APIs to create/close, open/close and delete // the key TestUser1. // Error = RegCreateKey( RootHandle, KEY_NAME_1, &Handle1 ); REG_API_SUCCESS( RegCreateKey ); Error = RegCloseKey( Handle1 ); REG_API_SUCCESS( RegCloseKey ); // // Wait for the notify thread to create its event. // Error = (LONG)WaitForSingleObject( _EventHandle1, (DWORD)-1 ); ASSERT( Error == 0 ); Error = RegOpenKey( RootHandle, KEY_NAME_1, &Handle1 ); REG_API_SUCCESS( RegOpenKey ); Error = RegCloseKey( Handle1 ); REG_API_SUCCESS( RegCloseKey ); Error = RegDeleteKey( RootHandle, KEY_NAME_1 ); REG_API_SUCCESS( RegDeleteKey ); // // Use Win32 APIs to create/close, open/close and create (open) the // key TestUser1. // // // Allocate and initialize the SecurityDescriptor. // SecurityDescriptor = malloc( sizeof( SECURITY_DESCRIPTOR )); ASSERT( SecurityDescriptor != NULL ); ErrorFlag = InitializeSecurityDescriptor( SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); ASSERT( ErrorFlag == TRUE ); SecurityAttributes.nLength = sizeof( SECURITY_ATTRIBUTES ); SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor; SecurityAttributes.bInheritHandle = FALSE; Error = RegCreateKeyEx( RootHandle, KEY_NAME_1, 0, KEY_NAME_1_CLASS, REG_OPTION_RESERVED, KEY_ALL_ACCESS, &SecurityAttributes, &Handle1, &Disposition ); REG_API_SUCCESS( RegCreateKeyEx ); ASSERT( Disposition == REG_CREATED_NEW_KEY ); Error = RegCloseKey( Handle1 ); REG_API_SUCCESS( RegCloseKey ); // // Wait for the notify thread to create its event. // Error = RegOpenKeyEx( RootHandle, KEY_NAME_1, REG_OPTION_RESERVED, KEY_ALL_ACCESS, &Handle1 ); REG_API_SUCCESS( RegOpenKeyEx ); Error = RegCloseKey( Handle1 ); REG_API_SUCCESS( RegCloseKey ); Error = RegCreateKeyEx( RootHandle, KEY_NAME_1, 0, KEY_NAME_1_CLASS, REG_OPTION_RESERVED, KEY_ALL_ACCESS, NULL, &Handle1, &Disposition ); REG_API_SUCCESS( RegCreateKeyEx ); ASSERT( Disposition == REG_OPENED_EXISTING_KEY ); // // Get and set the key's SECURITY_DESCRIPTOR. Setting will trigger // a notification. // SecurityDescriptorLength = 0; // // Get the SECURITY_DESCRIPTOR's length. // Error = RegGetKeySecurity( Handle1, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, SecurityDescriptor, &SecurityDescriptorLength ); ASSERT( Error == ERROR_INSUFFICIENT_BUFFER ); SecurityDescriptor = realloc( SecurityDescriptor, SecurityDescriptorLength ); ASSERT( SecurityDescriptor != NULL ); ErrorFlag = InitializeSecurityDescriptor( SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); ASSERT( ErrorFlag == TRUE ); Error = RegSetKeySecurity( Handle1, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, SecurityDescriptor ); REG_API_SUCCESS( RegSetKeySecurity ); Error = (LONG)WaitForSingleObject( _EventHandle2, (DWORD)-1 ); ASSERT( Error == 0 ); // // Reinitialize after the realloc. // SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor; // // Create two sub-keys. // Error = RegCreateKeyEx( Handle1, KEY_NAME_1_1, 0, KEY_NAME_1_1_CLASS, REG_OPTION_RESERVED, KEY_ALL_ACCESS, &SecurityAttributes, &Handle1_1, &Disposition ); REG_API_SUCCESS( RegCreateKeyEx ); ASSERT( Disposition == REG_CREATED_NEW_KEY ); Error = RegCreateKeyEx( Handle1, KEY_NAME_1_2, 0, KEY_NAME_1_2_CLASS, 0, KEY_ALL_ACCESS, &SecurityAttributes, &Handle1_2, &Disposition ); REG_API_SUCCESS( RegCreateKeyEx ); ASSERT( Disposition == REG_CREATED_NEW_KEY ); // // Enumerate the two sub-keys using the Win 3.1 and the the Win32 // enumeration APIs. // KeyNameLength = MAX_PATH; Error = RegEnumKey( Handle1, 0, KeyName, KeyNameLength ); REG_API_SUCCESS( RegEnumKey ); ASSERT( Compare( KeyName, KEY_NAME_1_1, KEY_NAME_1_1_LENGTH )); KeyNameLength = MAX_PATH; ClassNameLength = MAX_PATH; Error = RegEnumKeyEx( Handle1, 1, KeyName, &KeyNameLength, NULL, ClassName, &ClassNameLength, &LastWriteTime ); REG_API_SUCCESS( RegEnumKeyEx ); ASSERT( Compare( KeyName, KEY_NAME_1_2, KEY_NAME_1_2_LENGTH )); ASSERT( KeyNameLength == KEY_NAME_1_2_LENGTH ); //ASSERT( TitleIndex == KEY_NAME_1_2_TITLE_INDEX ); ASSERT( Compare( ClassName, KEY_NAME_1_2_CLASS, KEY_NAME_1_2_CLASS_LENGTH )); ASSERT( ClassNameLength == KEY_NAME_1_2_CLASS_LENGTH ); // // If the Quiet command line option wasn't set, display the TestUser1 key. // if( ! Quiet ) { Key = ParseKey( NameString ); REG_API_SUCCESS( Key != NULL ); DisplayKeys( Key, TRUE, TRUE, TRUE ); FreeKey( Key ); } // // Close the two sub-keys. // Error = RegCloseKey( Handle1_1 ); REG_API_SUCCESS( RegCloseKey ); Error = RegCloseKey( Handle1_2 ); REG_API_SUCCESS( RegCloseKey ); Error = RegFlushKey( Handle1 ); REG_API_SUCCESS( RegFlushKey ); // // Save the TestUser1 tree to a file. // #if 0 Error = RegSaveKey( Handle1, SAVE_RESTORE_FILE, SecurityDescriptor ); REG_API_SUCCESS( RegSaveKey ); RegCloseKey( Handle1 ); // // Delete the TestUser1 tree. // DeleteTestTree( ); // // Load TestUser1 from the file // Error = RegLoadKey( RootHandle, KEY_NAME_1, SAVE_RESTORE_FILE ); REG_API_SUCCESS( RegLoadKey ); // // Unload TestUser1 // Error = RegUnLoadKey( RootHandle, KEY_NAME_1 ); REG_API_SUCCESS( RegUnLoadKey ); // // Restore the TestUser1 tree from a file. // Error = RegCreateKey( RootHandle, KEY_NAME_1, &Handle1 ); REG_API_SUCCESS( RegCreateKey ); Error = RegRestoreKey( Handle1, SAVE_RESTORE_FILE, 0 ); REG_API_SUCCESS( RegRestoreKey ); #endif // // Delete the two sub-keys. // Error = RegDeleteKey( Handle1, KEY_NAME_1_1 ); REG_API_SUCCESS( RegDeleteKey ); Error = RegDeleteKey( Handle1, KEY_NAME_1_2 ); REG_API_SUCCESS( RegDeleteKey ); // // Set a value in the TestUser1 key using the Win 3.1 compatible API. // Error = RegSetValue( RootHandle, KEY_NAME_1, VALUE_DATA_1_TYPE, Data_1, VALUE_DATA_1_LENGTH ); REG_API_SUCCESS( RegSetValue ); // // Set a value in the TestUser1 key using the Win32 API. // Error = RegSetValueEx( Handle1, VALUE_NAME_2, 0, VALUE_DATA_2_TYPE, ( PVOID ) &Data_2, VALUE_DATA_2_LENGTH ); REG_API_SUCCESS( RegSetValueEx ); // // Commit the Key to the Registry. // Error = RegFlushKey( Handle1 ); REG_API_SUCCESS( RegFlushKey ); // // If the Quiet command line option wasn't set, display the TestUser1 key. // if( ! Quiet ) { Key = ParseKey( NameString ); REG_API_SUCCESS( Key != NULL ); DisplayKeys( Key, TRUE, TRUE, TRUE ); FreeKey( Key ); } // // Query a value in the TestUser1 key using the Win 3.1 compatible API. // DataLength = MAX_DATA_LENGTH; Error = RegQueryValue( RootHandle, KEY_NAME_1, Data, &DataLength ); REG_API_SUCCESS( RegQueryValue ); ASSERT( Compare( Data, &Data_1, VALUE_DATA_1_LENGTH )); ASSERT( DataLength == VALUE_DATA_1_LENGTH ); // // Query a value in the TestUser1 key using the Win32 API. // DataLength = MAX_DATA_LENGTH; Error = RegQueryValueEx( Handle1, VALUE_NAME_2, NULL, &Type, Data, &DataLength ); REG_API_SUCCESS( RegQueryValueEx ); //ASSERT( TitleIndex == VALUE_NAME_2_TITLE_INDEX ); ASSERT( Type == VALUE_DATA_2_TYPE ); ASSERT(( DWORD ) Data[ 0 ] == Data_2 ); ASSERT( DataLength == VALUE_DATA_2_LENGTH ); // // Query information about the key. // ClassNameLength = MAX_PATH; Error = RegQueryInfoKey( Handle1, ClassName, &ClassNameLength, NULL, &NumberOfSubKeys, &MaxSubKeyLength, &MaxClassLength, &NumberOfValues, &MaxValueNameLength, &MaxValueDataLength, &SecurityDescriptorLength, &LastWriteTime ); REG_API_SUCCESS( RegQueryInfoKey ); ASSERT( Compare( ClassName, KEY_NAME_1_CLASS, KEY_NAME_1_CLASS_LENGTH )); ASSERT( ClassNameLength == KEY_NAME_1_CLASS_LENGTH ); //ASSERT( TitleIndex == KEY_NAME_1_TITLE_INDEX ); ASSERT( NumberOfSubKeys == 0 ); ASSERT( MaxSubKeyLength == 0 ); ASSERT( MaxClassLength == 0 ); ASSERT( NumberOfValues == 2 ); ASSERT( MaxValueNameLength == VALUE_NAME_2_LENGTH * sizeof(WCHAR) ); ASSERT( MaxValueDataLength == VALUE_DATA_1_LENGTH * sizeof(WCHAR) ); // // Enumerate the values. // for( Index = 0; Index < 2; Index++ ) { ValueNameLength = MAX_PATH; DataLength = MAX_DATA_LENGTH; Error = RegEnumValue( Handle1, Index, ValueName, &ValueNameLength, NULL, &Type, Data, &DataLength ); REG_API_SUCCESS( RegEnumValue ); // // Check specifics depending on the value being queried. // switch( Index ) { case 0: // // No name - win 3.1 compatible value. // ASSERT( ValueNameLength == 0 ); //ASSERT( TitleIndex == VALUE_NAME_1_TITLE_INDEX ); ASSERT( Type == VALUE_DATA_1_TYPE ); ASSERT( Compare( Data, Data_1, VALUE_DATA_1_LENGTH )); ASSERT( DataLength == VALUE_DATA_1_LENGTH ); break; case 1: ASSERT( Compare( ValueName, VALUE_NAME_2, VALUE_NAME_2_LENGTH )); ASSERT( ValueNameLength == VALUE_NAME_2_LENGTH ); //ASSERT( TitleIndex == VALUE_NAME_2_TITLE_INDEX ); ASSERT( Type == VALUE_DATA_2_TYPE ); ASSERT(( DWORD ) Data[ 0 ] == Data_2 ); ASSERT( DataLength == VALUE_DATA_2_LENGTH ); break; default: ASSERT_MESSAGE( FALSE, "Valid value enumeration index - " ); } } // // All done! Get rid of the key and close it. // Error = RegDeleteKey( RootHandle, KEY_NAME_1 ); REG_API_SUCCESS( RegDeleteKey ); Error = RegCloseKey( Handle1 ); REG_API_SUCCESS( RegCloseKey ); }