summaryrefslogtreecommitdiffstats
path: root/private/sdktools/instaler
diff options
context:
space:
mode:
Diffstat (limited to 'private/sdktools/instaler')
-rw-r--r--private/sdktools/instaler/compinst.c899
-rw-r--r--private/sdktools/instaler/compinst.rc12
-rw-r--r--private/sdktools/instaler/debug.c481
-rw-r--r--private/sdktools/instaler/dir.bmpbin0 -> 246 bytes
-rw-r--r--private/sdktools/instaler/error.c202
-rw-r--r--private/sdktools/instaler/errormsg.mc219
-rw-r--r--private/sdktools/instaler/event.c76
-rw-r--r--private/sdktools/instaler/file.bmpbin0 -> 246 bytes
-rw-r--r--private/sdktools/instaler/file.c440
-rw-r--r--private/sdktools/instaler/fileman.c226
-rw-r--r--private/sdktools/instaler/handledb.c172
-rw-r--r--private/sdktools/instaler/handler.c2355
-rw-r--r--private/sdktools/instaler/hlist.c484
-rw-r--r--private/sdktools/instaler/hlist.h13
-rw-r--r--private/sdktools/instaler/i386/machine.c332
-rw-r--r--private/sdktools/instaler/i386/sources32
-rw-r--r--private/sdktools/instaler/iml.c481
-rw-r--r--private/sdktools/instaler/iml.h221
-rw-r--r--private/sdktools/instaler/ini.c382
-rw-r--r--private/sdktools/instaler/init.c770
-rw-r--r--private/sdktools/instaler/instaler.c159
-rw-r--r--private/sdktools/instaler/instaler.h1165
-rw-r--r--private/sdktools/instaler/instaler.rc12
-rw-r--r--private/sdktools/instaler/instutil.c365
-rw-r--r--private/sdktools/instaler/instutil.h114
-rw-r--r--private/sdktools/instaler/key.c524
-rw-r--r--private/sdktools/instaler/makefile6
-rw-r--r--private/sdktools/instaler/makefile.inc10
-rw-r--r--private/sdktools/instaler/namedb.c173
-rw-r--r--private/sdktools/instaler/process.c560
-rw-r--r--private/sdktools/instaler/res.h2
-rw-r--r--private/sdktools/instaler/showinst.c295
-rw-r--r--private/sdktools/instaler/showinst.rc12
-rw-r--r--private/sdktools/instaler/sources36
-rw-r--r--private/sdktools/instaler/test.c389
-rw-r--r--private/sdktools/instaler/testins1.c384
-rw-r--r--private/sdktools/instaler/testins1.rc12
-rw-r--r--private/sdktools/instaler/testins2.c387
-rw-r--r--private/sdktools/instaler/testins2.rc12
-rw-r--r--private/sdktools/instaler/undoinst.c831
-rw-r--r--private/sdktools/instaler/undoinst.rc12
41 files changed, 13257 insertions, 0 deletions
diff --git a/private/sdktools/instaler/compinst.c b/private/sdktools/instaler/compinst.c
new file mode 100644
index 000000000..9c6df1338
--- /dev/null
+++ b/private/sdktools/instaler/compinst.c
@@ -0,0 +1,899 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ showinst.c
+
+Abstract:
+
+ This program compares the actions described by two Installation Modification Log file
+ created by the INSTALER program
+
+Author:
+
+ Steve Wood (stevewo) 15-Jan-1996
+
+Revision History:
+
+--*/
+
+#include "instutil.h"
+#include "iml.h"
+
+BOOLEAN VerboseOutput;
+
+BOOLEAN
+CompareIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml1,
+ PINSTALLATION_MODIFICATION_LOGFILE pIml2
+ );
+
+int
+_CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ char *s;
+ PWSTR ImlPathAlt = NULL;
+ PINSTALLATION_MODIFICATION_LOGFILE pIml1 = NULL;
+ PINSTALLATION_MODIFICATION_LOGFILE pIml2 = NULL;
+
+ InitCommonCode( "COMPINST",
+ "InstallationName2 [-v]",
+ "-v verbose output\n"
+ );
+ VerboseOutput = FALSE;
+ while (--argc) {
+ s = *++argv;
+ if (*s == '-' || *s == '/') {
+ while (*++s) {
+ switch( tolower( *s ) ) {
+ case 'v':
+ VerboseOutput = TRUE;
+ break;
+ default:
+ CommonSwitchProcessing( &argc, &argv, *s );
+ break;
+ }
+ }
+ }
+ else
+ if (!CommonArgProcessing( &argc, &argv )) {
+ if (ImlPathAlt != NULL) {
+ Usage( "Too many installation names specified - '%s'", (ULONG)s );
+ }
+
+ ImlPathAlt = FormatImlPath( InstalerDirectory, GetArgAsUnicode( s ) );
+ }
+ }
+
+ if (ImlPath == NULL || ImlPathAlt == NULL) {
+ Usage( "Must specify two installation names to compare", 0 );
+ }
+
+ if (!SetCurrentDirectory( InstalerDirectory )) {
+ FatalError( "Unable to change to '%ws' directory (%u)",
+ (ULONG)InstalerDirectory,
+ GetLastError()
+ );
+ }
+
+ pIml1 = LoadIml( ImlPath );
+ if (pIml1 == NULL) {
+ FatalError( "Unable to load '%ws' (%u)",
+ (ULONG)ImlPath,
+ GetLastError()
+ );
+ }
+
+ pIml2 = LoadIml( ImlPathAlt );
+ if (pIml2 == NULL) {
+ FatalError( "Unable to load '%ws' (%u)",
+ (ULONG)ImlPathAlt,
+ GetLastError()
+ );
+ }
+
+ printf( "Displaying differences between:\n" );
+ printf( " Installation 1: %ws\n", ImlPath );
+ printf( " Installation 2: %ws\n", ImlPathAlt );
+ exit( CompareIml( pIml1, pIml2 ) == FALSE );
+ return 0;
+}
+
+typedef struct _IML_GENERIC_RECORD {
+ POFFSET Next; // IML_GENERIC_RECORD
+ ULONG Action;
+ POFFSET Name; // WCHAR
+ POFFSET Records; // IML_GENERIC_RECORD
+} IML_GENERIC_RECORD, *PIML_GENERIC_RECORD;
+
+
+typedef
+VOID
+(*PIML_PRINT_RECORD_ROUTINE)(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ PIML_GENERIC_RECORD pGeneric,
+ PWSTR Parents[],
+ ULONG Depth,
+ ULONG i
+ );
+
+typedef
+BOOLEAN
+(*PIML_COMPARE_CONTENTS_ROUTINE)(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml1,
+ PIML_GENERIC_RECORD pGeneric1,
+ PINSTALLATION_MODIFICATION_LOGFILE pIml2,
+ PIML_GENERIC_RECORD pGeneric2,
+ PWSTR Parents[]
+ );
+
+
+PINSTALLATION_MODIFICATION_LOGFILE pSortIml;
+
+int
+_CRTAPI1
+CompareGeneric(
+ const void *Reference1,
+ const void *Reference2
+ )
+{
+ PIML_GENERIC_RECORD p1 = *(PIML_GENERIC_RECORD *)Reference1;
+ PIML_GENERIC_RECORD p2 = *(PIML_GENERIC_RECORD *)Reference2;
+
+ if (p1->Name == 0) {
+ if (p2->Name == 0) {
+ return 0;
+ }
+ else {
+ return -1;
+ }
+ }
+ else
+ if (p2->Name == 0) {
+ return 1;
+ }
+
+ return _wcsicmp( MP( PWSTR, pSortIml, p1->Name ),
+ MP( PWSTR, pSortIml, p2->Name )
+ );
+}
+
+
+PIML_GENERIC_RECORD *
+GetSortedGenericListAsArray(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ PIML_GENERIC_RECORD pGeneric
+ )
+{
+ PIML_GENERIC_RECORD p, *pp;
+ ULONG n;
+
+ p = pGeneric;
+ n = 1;
+ while (p != NULL) {
+ n += 1;
+ p = MP( PIML_GENERIC_RECORD, pIml, p->Next );
+ }
+
+ pp = HeapAlloc( GetProcessHeap(), 0, n * sizeof( *pp ) );
+ p = pGeneric;
+ n = 0;
+ while (p != NULL) {
+ pp[ n++ ] = p;
+ p = MP( PIML_GENERIC_RECORD, pIml, p->Next );
+ }
+ pp[ n ] = NULL;
+
+ pSortIml = pIml;
+ qsort( (void *)pp, n, sizeof( *pp ), CompareGeneric );
+ pSortIml = NULL;
+ return pp;
+}
+
+BOOLEAN
+CompareGenericIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml1,
+ PIML_GENERIC_RECORD pGeneric1,
+ PINSTALLATION_MODIFICATION_LOGFILE pIml2,
+ PIML_GENERIC_RECORD pGeneric2,
+ PWSTR Parents[],
+ ULONG Depth,
+ PIML_PRINT_RECORD_ROUTINE PrintRecordRoutine,
+ PIML_COMPARE_CONTENTS_ROUTINE CompareContentsRoutine
+ )
+{
+ PVOID pBufferToFree1;
+ PVOID pBufferToFree2;
+ PIML_GENERIC_RECORD *ppGeneric1;
+ PIML_GENERIC_RECORD *ppGeneric2;
+ PIML_GENERIC_RECORD pShow1;
+ PIML_GENERIC_RECORD pShow2;
+ BOOLEAN Result = FALSE;
+ PWSTR s1, s2;
+ int cmpResult;
+
+ ppGeneric1 = GetSortedGenericListAsArray( pIml1, pGeneric1 );
+ if (ppGeneric1 == NULL) {
+ return FALSE;
+ }
+ pBufferToFree1 = ppGeneric1;
+
+ ppGeneric2 = GetSortedGenericListAsArray( pIml2, pGeneric2 );
+ if (ppGeneric2 == NULL) {
+ HeapFree( GetProcessHeap(), 0, pBufferToFree1 );
+ return FALSE;
+ }
+ pBufferToFree2 = ppGeneric2;
+
+ pGeneric1 = *ppGeneric1++;
+ pGeneric2 = *ppGeneric2++;
+
+ while (TRUE) {
+ pShow1 = NULL;
+ pShow2 = NULL;
+ if (pGeneric1 == NULL) {
+ if (pGeneric2 == NULL) {
+ break;
+ }
+
+ //
+ // pGeneric2 is new
+ //
+
+ pShow2 = pGeneric2;
+ pGeneric2 = *ppGeneric2++;
+ Result = FALSE;
+ }
+ else
+ if (pGeneric2 == NULL) {
+ //
+ // pGeneric1 is new
+ //
+ pShow1 = pGeneric1;
+ pGeneric1 = *ppGeneric1++;
+ Result = FALSE;
+ }
+ else {
+ s1 = MP( PWSTR, pIml1, pGeneric1->Name );
+ s2 = MP( PWSTR, pIml2, pGeneric2->Name );
+
+ if (s1 == NULL) {
+ if (s2 == NULL) {
+ cmpResult = 0;
+ }
+ else {
+ cmpResult = -1;
+ }
+ }
+ else
+ if (s2 == NULL) {
+ cmpResult = 1;
+ }
+ else {
+ cmpResult = _wcsicmp( s1, s2 );
+ }
+ if (cmpResult == 0) {
+ if (Depth > 1) {
+ Parents[ Depth - 1 ] = MP( PWSTR, pIml1, pGeneric1->Name );
+ Result = CompareGenericIml( pIml1,
+ MP( PIML_GENERIC_RECORD, pIml1, pGeneric1->Records ),
+ pIml2,
+ MP( PIML_GENERIC_RECORD, pIml2, pGeneric2->Records ),
+ Parents,
+ Depth - 1,
+ PrintRecordRoutine,
+ CompareContentsRoutine
+ );
+ }
+ else {
+ Result = (*CompareContentsRoutine)( pIml1, pGeneric1,
+ pIml2, pGeneric2,
+ Parents
+ );
+ }
+
+ pGeneric1 = *ppGeneric1++;
+ pGeneric2 = *ppGeneric2++;
+ }
+ else
+ if (cmpResult > 0) {
+ pShow2 = pGeneric2;
+ pGeneric2 = *ppGeneric2++;
+ }
+ else {
+ pShow1 = pGeneric1;
+ pGeneric1 = *ppGeneric1++;
+ }
+ }
+
+ if (pShow1) {
+ (*PrintRecordRoutine)( pIml1, pShow1, Parents, Depth, 1 );
+ }
+
+ if (pShow2) {
+ (*PrintRecordRoutine)( pIml2, pShow2, Parents, Depth, 2 );
+ }
+ }
+
+ HeapFree( GetProcessHeap(), 0, pBufferToFree1 );
+ HeapFree( GetProcessHeap(), 0, pBufferToFree2 );
+ return Result;
+}
+
+
+char *FileActionStrings[] = {
+ "CreateNewFile",
+ "ModifyOldFile",
+ "DeleteOldFile",
+ "RenameOldFile",
+ "ModifyFileDateTime",
+ "ModifyFileAttributes"
+};
+
+
+PWSTR
+FormatFileTime(
+ LPFILETIME LastWriteTime
+ )
+{
+ FILETIME LocalFileTime;
+ SYSTEMTIME DateTime;
+ static WCHAR DateTimeBuffer[ 128 ];
+
+ FileTimeToLocalFileTime( LastWriteTime, &LocalFileTime );
+ FileTimeToSystemTime( &LocalFileTime, &DateTime );
+
+ _snwprintf( DateTimeBuffer,
+ 128,
+ L"%02u/%02u/%04u %02u:%02u:%02u",
+ (ULONG)DateTime.wMonth,
+ (ULONG)DateTime.wDay,
+ (ULONG)DateTime.wYear,
+ (ULONG)DateTime.wHour,
+ (ULONG)DateTime.wMinute,
+ (ULONG)DateTime.wSecond
+ );
+
+ return DateTimeBuffer;
+}
+
+
+VOID
+PrintFileRecordIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ PIML_GENERIC_RECORD pGeneric,
+ PWSTR Parents[],
+ ULONG Depth,
+ ULONG i
+ )
+{
+ PIML_FILE_RECORD pFile = (PIML_FILE_RECORD)pGeneric;
+
+ printf( "File: %ws\n %u: %s\n",
+ MP( PWSTR, pIml, pFile->Name ),
+ i, FileActionStrings[ pFile->Action ]
+ );
+}
+
+BOOLEAN
+CompareFileContentsIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml1,
+ PIML_GENERIC_RECORD pGeneric1,
+ PINSTALLATION_MODIFICATION_LOGFILE pIml2,
+ PIML_GENERIC_RECORD pGeneric2,
+ PWSTR Parents[]
+ )
+{
+ PIML_FILE_RECORD pFile1 = (PIML_FILE_RECORD)pGeneric1;
+ PIML_FILE_RECORD pFile2 = (PIML_FILE_RECORD)pGeneric2;
+ PIML_FILE_RECORD_CONTENTS pFileContents1;
+ PIML_FILE_RECORD_CONTENTS pFileContents2;
+ BOOLEAN ActionsDiffer = FALSE;
+ BOOLEAN DatesDiffer = FALSE;
+ BOOLEAN AttributesDiffer = FALSE;
+ BOOLEAN SizesDiffer = FALSE;
+ BOOLEAN ContentsDiffer = FALSE;
+ BOOLEAN Result = TRUE;
+ PCHAR s1, s2;
+ ULONG n;
+
+ pFileContents1 = MP( PIML_FILE_RECORD_CONTENTS, pIml1, pFile1->NewFile );
+ pFileContents2 = MP( PIML_FILE_RECORD_CONTENTS, pIml2, pFile2->NewFile );
+
+ if (pFile1->Action != pFile2->Action) {
+ ActionsDiffer = TRUE;
+ Result = FALSE;
+ }
+ else
+ if (pFileContents1 != NULL && pFileContents2 != NULL) {
+ if (pFile1->Action != CreateNewFile &&
+ ((pFileContents1->LastWriteTime.dwHighDateTime !=
+ pFileContents2->LastWriteTime.dwHighDateTime
+ ) ||
+ (pFileContents1->LastWriteTime.dwLowDateTime !=
+ pFileContents2->LastWriteTime.dwLowDateTime
+ )
+ )
+ ) {
+ DatesDiffer = TRUE;
+ Result = FALSE;
+ }
+
+ if (pFileContents1->FileAttributes != pFileContents2->FileAttributes) {
+ AttributesDiffer = TRUE;
+ Result = FALSE;
+ }
+
+ if (pFileContents1->FileSize != pFileContents2->FileSize) {
+ SizesDiffer = TRUE;
+ Result = FALSE;
+ }
+ else
+ if (pFileContents1->Contents == 0 ||
+ pFileContents2->Contents == 0 ||
+ memcmp( MP( PVOID, pIml1, pFileContents1->Contents ),
+ MP( PVOID, pIml2, pFileContents2->Contents ),
+ pFileContents1->FileSize
+ ) != 0
+ ) {
+ s1 = MP( PVOID, pIml1, pFileContents1->Contents );
+ s2 = MP( PVOID, pIml2, pFileContents2->Contents );
+ if (s1 == NULL || s2 == NULL) {
+ n = 0;
+ }
+ else {
+ n = pFileContents1->FileSize;
+ }
+ while (n) {
+ if (*s1 != *s2) {
+ n = pFileContents1->FileSize - n;
+ break;
+ }
+
+ n -= 1;
+ s1 += 1;
+ s2 += 1;
+ }
+
+ ContentsDiffer = TRUE;
+ Result = FALSE;
+ }
+ }
+
+ if (!Result) {
+ printf( "File: %ws\n", MP( PWSTR, pIml1, pFile1->Name ) );
+ if (ActionsDiffer) {
+ printf( " 1: Action - %s\n", FileActionStrings[ pFile1->Action ] );
+ printf( " 2: Action - %s\n", FileActionStrings[ pFile2->Action ] );
+ }
+ if (DatesDiffer) {
+ printf( " 1: LastWriteTime - %ws\n",
+ FormatFileTime( &pFileContents1->LastWriteTime )
+ );
+ printf( " 2: LastWriteTime - %ws\n",
+ FormatFileTime( &pFileContents2->LastWriteTime )
+ );
+ }
+ if (AttributesDiffer) {
+ printf( " 1: Attributes - 0x%08x\n", pFileContents1->FileAttributes );
+ printf( " 2: Attributes - 0x%08x\n", pFileContents2->FileAttributes );
+ }
+ if (SizesDiffer) {
+ printf( " 1: File Size - 0x%08x\n", pFileContents1->FileSize );
+ printf( " 2: File Size - 0x%08x\n", pFileContents2->FileSize );
+ }
+ if (ContentsDiffer) {
+ printf( " 1: Contents Differs\n" );
+ printf( " 2: from each other at offset %08x\n", n );
+ }
+ }
+
+ return Result;
+}
+
+
+char *KeyActionStrings[] = {
+ "CreateNewKey",
+ "DeleteOldKey",
+ "ModifyKeyValues"
+};
+
+char *ValueActionStrings[] = {
+ "CreateNewValue",
+ "DeleteOldValue",
+ "ModifyOldValue"
+};
+
+
+char *ValueTypeStrings[] = {
+ "REG_NONE",
+ "REG_SZ",
+ "REG_EXPAND_SZ",
+ "REG_BINARY",
+ "REG_DWORD",
+ "REG_DWORD_BIG_ENDIAN",
+ "REG_LINK",
+ "REG_MULTI_SZ",
+ "REG_RESOURCE_LIST",
+ "REG_FULL_RESOURCE_DESCRIPTOR",
+ "REG_RESOURCE_REQUIREMENTS_LIST"
+};
+
+VOID
+PrintKeyValueRecordIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ PIML_GENERIC_RECORD pGeneric,
+ PWSTR Parents[],
+ ULONG Depth,
+ ULONG i
+ )
+{
+ PIML_KEY_RECORD pKey = (PIML_KEY_RECORD)pGeneric;
+ PIML_VALUE_RECORD pValue = (PIML_VALUE_RECORD)pGeneric;
+
+ if (Depth == 2) {
+ printf( "Key: %ws\n %u: %s\n",
+ MP( PWSTR, pIml, pKey->Name ),
+ i, KeyActionStrings[ pKey->Action ]
+ );
+ }
+ else {
+ if (Parents[ 1 ] != NULL) {
+ printf( "Key: %ws\n", Parents[ 1 ] );
+ Parents[ 1 ] = NULL;
+ }
+
+ printf( " Value: %ws\n %u: %s\n",
+ MP( PWSTR, pIml, pValue->Name ),
+ i, ValueActionStrings[ pValue->Action ]
+ );
+ }
+}
+
+UCHAR BlanksForPadding[] =
+ " ";
+
+VOID
+PrintValueContents(
+ PCHAR PrefixString,
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ PIML_VALUE_RECORD_CONTENTS pValueContents
+ )
+{
+ ULONG ValueType;
+ ULONG ValueLength;
+ PVOID ValueData;
+ ULONG cbPrefix, cb, i, j;
+ PWSTR pw;
+ PULONG p;
+
+ ValueType = pValueContents->Type;
+ ValueLength = pValueContents->Length;
+ ValueData = MP( PVOID, pIml, pValueContents->Data );
+
+ cbPrefix = printf( "%s", PrefixString );
+ cb = cbPrefix + printf( "%s", ValueTypeStrings[ ValueType ] );
+
+ switch( ValueType ) {
+ case REG_SZ:
+ case REG_LINK:
+ case REG_EXPAND_SZ:
+ pw = (PWSTR)ValueData;
+ printf( " (%u) \"%.*ws\"\n", ValueLength, ValueLength/sizeof(WCHAR), pw );
+ break;
+
+ case REG_MULTI_SZ:
+ pw = (PWSTR)ValueData;
+ i = 0;
+ if (*pw)
+ while (i < (ValueLength - 1) / sizeof( WCHAR )) {
+ if (i > 0) {
+ printf( " \\\n%.*s", cbPrefix, BlanksForPadding );
+ }
+ printf( "\"%ws\" ", pw+i );
+ do {
+ ++i;
+ }
+ while (pw[i] != UNICODE_NULL);
+
+ ++i;
+ }
+ printf( "\n" );
+ break;
+
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN:
+ printf( " 0x%08x\n", *(PULONG)ValueData );
+ break;
+
+ case REG_RESOURCE_LIST:
+ case REG_FULL_RESOURCE_DESCRIPTOR:
+ case REG_RESOURCE_REQUIREMENTS_LIST:
+ case REG_BINARY:
+ case REG_NONE:
+ cb = printf( " [0x%08lx]", ValueLength );
+
+ if (ValueLength != 0) {
+ p = (PULONG)ValueData;
+ i = (ValueLength + 3) / sizeof( ULONG );
+ for (j=0; j<i; j++) {
+ if ((cbPrefix + cb + 11) > 78) {
+ printf( " \\\n%.*s", cbPrefix, BlanksForPadding );
+ cb = 0;
+ }
+ else {
+ cb += printf( " " );
+ }
+
+ cb += printf( "0x%08lx", *p++ );
+ }
+ }
+
+ printf( "\n" );
+ break;
+ }
+}
+
+BOOLEAN
+CompareKeyValueContentsIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml1,
+ PIML_GENERIC_RECORD pGeneric1,
+ PINSTALLATION_MODIFICATION_LOGFILE pIml2,
+ PIML_GENERIC_RECORD pGeneric2,
+ PWSTR Parents[]
+ )
+{
+ PIML_VALUE_RECORD pValue1 = (PIML_VALUE_RECORD)pGeneric1;
+ PIML_VALUE_RECORD pValue2 = (PIML_VALUE_RECORD)pGeneric2;
+ PIML_VALUE_RECORD_CONTENTS pValueContents1;
+ PIML_VALUE_RECORD_CONTENTS pValueContents2;
+ BOOLEAN ActionsDiffer = FALSE;
+ BOOLEAN TypesDiffer = FALSE;
+ BOOLEAN LengthsDiffer = FALSE;
+ BOOLEAN ContentsDiffer = FALSE;
+ BOOLEAN Result = TRUE;
+ PCHAR s1, s2;
+ ULONG n;
+
+ pValueContents1 = MP( PIML_VALUE_RECORD_CONTENTS, pIml1, pValue1->NewValue );
+ pValueContents2 = MP( PIML_VALUE_RECORD_CONTENTS, pIml2, pValue2->NewValue );
+
+ if (pValue1->Action != pValue2->Action) {
+ ActionsDiffer = TRUE;
+ Result = FALSE;
+ }
+ else
+ if (pValueContents1 != NULL && pValueContents2 != NULL) {
+ if (pValue1->Action != CreateNewValue &&
+ (pValueContents1->Type != pValueContents2->Type)
+ ) {
+ TypesDiffer = TRUE;
+ Result = FALSE;
+ }
+
+ if (pValueContents1->Length != pValueContents2->Length) {
+ LengthsDiffer = TRUE;
+ Result = FALSE;
+ }
+ else
+ if (pValueContents1->Data == 0 ||
+ pValueContents2->Data == 0 ||
+ memcmp( MP( PVOID, pIml1, pValueContents1->Data ),
+ MP( PVOID, pIml2, pValueContents2->Data ),
+ pValueContents1->Length
+ ) != 0
+ ) {
+ s1 = MP( PVOID, pIml1, pValueContents1->Data );
+ s2 = MP( PVOID, pIml2, pValueContents2->Data );
+ if (s1 == NULL || s2 == NULL) {
+ n = 0;
+ }
+ else {
+ n = pValueContents1->Length;
+ }
+ while (n) {
+ if (*s1 != *s2) {
+ n = pValueContents1->Length - n;
+ break;
+ }
+
+ n -= 1;
+ s1 += 1;
+ s2 += 1;
+ }
+
+ ContentsDiffer = TRUE;
+ Result = FALSE;
+ }
+ }
+
+ if (!Result) {
+ if (Parents[ 2 ] != NULL) {
+ printf( "Key: %ws\n", Parents[ 2 ] );
+ Parents[ 2 ] = NULL;
+ }
+
+ printf( " Value: %ws\n", MP( PWSTR, pIml1, pValue1->Name ) );
+ if (ActionsDiffer) {
+ printf( " 1: Action - %s\n", ValueActionStrings[ pValue1->Action ] );
+ printf( " 2: Action - %s\n", ValueActionStrings[ pValue2->Action ] );
+ }
+ if (TypesDiffer || LengthsDiffer || ContentsDiffer ) {
+ PrintValueContents( " 1: ", pIml1, pValueContents1 );
+ PrintValueContents( " 2: ", pIml2, pValueContents2 );
+ }
+ }
+
+ return Result;
+}
+
+
+char *IniActionStrings[] = {
+ "CreateNewIniFile",
+ "ModifyOldIniFile"
+};
+
+char *SectionActionStrings[] = {
+ "CreateNewSection",
+ "DeleteOldSection",
+ "ModifySectionVariables"
+};
+
+char *VariableActionStrings[] = {
+ "CreateNewVariable",
+ "DeleteOldVariable",
+ "ModifyOldVariable"
+};
+
+VOID
+PrintIniSectionVariableRecordIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ PIML_GENERIC_RECORD pGeneric,
+ PWSTR Parents[],
+ ULONG Depth,
+ ULONG i
+ )
+{
+ PIML_INI_RECORD pIni = (PIML_INI_RECORD)pGeneric;
+ PIML_INISECTION_RECORD pSection = (PIML_INISECTION_RECORD)pGeneric;
+ PIML_INIVARIABLE_RECORD pVariable = (PIML_INIVARIABLE_RECORD)pGeneric;
+
+ if (Depth == 3) {
+ printf( "Ini File: %ws\n %u: %s\n",
+ MP( PWSTR, pIml, pIni->Name ),
+ i, IniActionStrings[ pIni->Action ]
+ );
+ }
+ else
+ if (Depth == 2) {
+ if (Parents[ 2 ] != NULL) {
+ printf( "Ini File: %ws\n", Parents[ 2 ] );
+ Parents[ 2 ] = NULL;
+ }
+
+ printf( " Section: %ws\n %u: %s\n",
+ MP( PWSTR, pIml, pSection->Name ),
+ i, SectionActionStrings[ pSection->Action ]
+ );
+ }
+ else {
+ if (Parents[ 2 ] != NULL) {
+ printf( "Ini File: %ws\n", Parents[ 2 ] );
+ Parents[ 2 ] = NULL;
+ }
+
+ if (Parents[ 1 ] != NULL) {
+ printf( " Section: %ws\n", Parents[ 1 ] );
+ Parents[ 1 ] = NULL;
+ }
+
+ printf( " Variable: %ws\n %u: %s\n",
+ MP( PWSTR, pIml, pVariable->Name ),
+ i, VariableActionStrings[ pVariable->Action ]
+ );
+ }
+}
+
+BOOLEAN
+CompareIniSectionVariableContentsIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml1,
+ PIML_GENERIC_RECORD pGeneric1,
+ PINSTALLATION_MODIFICATION_LOGFILE pIml2,
+ PIML_GENERIC_RECORD pGeneric2,
+ PWSTR Parents[]
+ )
+{
+ PIML_INIVARIABLE_RECORD pVariable1 = (PIML_INIVARIABLE_RECORD)pGeneric1;
+ PIML_INIVARIABLE_RECORD pVariable2 = (PIML_INIVARIABLE_RECORD)pGeneric2;
+ PWSTR pVariableContents1;
+ PWSTR pVariableContents2;
+ BOOLEAN ActionsDiffer = FALSE;
+ BOOLEAN ContentsDiffer = FALSE;
+ BOOLEAN Result = TRUE;
+
+ pVariableContents1 = MP( PWSTR, pIml1, pVariable1->NewValue );
+ pVariableContents2 = MP( PWSTR, pIml2, pVariable2->NewValue );
+
+ if (pVariable1->Action != pVariable2->Action) {
+ ActionsDiffer = TRUE;
+ Result = FALSE;
+ }
+ else
+ if (pVariableContents1 != NULL && pVariableContents2 != NULL) {
+ if (wcscmp( pVariableContents1, pVariableContents2 ) != 0) {
+ ContentsDiffer = TRUE;
+ Result = FALSE;
+ }
+ }
+
+ if (!Result) {
+ if (Parents[ 2 ] != NULL) {
+ printf( "Ini File: %ws\n", Parents[ 2 ] );
+ Parents[ 2 ] = NULL;
+ }
+
+ if (Parents[ 1 ] != NULL) {
+ printf( " Section: %ws\n", Parents[ 1 ] );
+ Parents[ 1 ] = NULL;
+ }
+
+ printf( " Variable: %ws\n", MP( PWSTR, pIml1, pVariable1->Name ) );
+ if (ActionsDiffer) {
+ printf( " 1: Action - %s\n", VariableActionStrings[ pVariable1->Action ] );
+ printf( " 2: Action - %s\n", VariableActionStrings[ pVariable2->Action ] );
+ }
+ if (ContentsDiffer) {
+ printf( " 1: '%ws'\n", pVariableContents1 );
+ printf( " 2: '%ws'\n", pVariableContents2 );
+ }
+ }
+
+ return Result;
+}
+
+
+
+
+BOOLEAN
+CompareIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml1,
+ PINSTALLATION_MODIFICATION_LOGFILE pIml2
+ )
+{
+ BOOLEAN Result = TRUE;
+ PWSTR Parents[ 3 ];
+
+ Result &= CompareGenericIml( pIml1, MP( PIML_GENERIC_RECORD, pIml1, pIml1->FileRecords ),
+ pIml2, MP( PIML_GENERIC_RECORD, pIml2, pIml2->FileRecords ),
+ NULL,
+ 1,
+ PrintFileRecordIml,
+ CompareFileContentsIml
+ );
+
+ memset( Parents, 0, sizeof( Parents ) );
+ Result &= CompareGenericIml( pIml1, MP( PIML_GENERIC_RECORD, pIml1, pIml1->KeyRecords ),
+ pIml2, MP( PIML_GENERIC_RECORD, pIml2, pIml2->KeyRecords ),
+ Parents,
+ 2,
+ PrintKeyValueRecordIml,
+ CompareKeyValueContentsIml
+ );
+
+ memset( Parents, 0, sizeof( Parents ) );
+ Result &= CompareGenericIml( pIml1, MP( PIML_GENERIC_RECORD, pIml1, pIml1->IniRecords ),
+ pIml2, MP( PIML_GENERIC_RECORD, pIml2, pIml2->IniRecords ),
+ Parents,
+ 3,
+ PrintIniSectionVariableRecordIml,
+ CompareIniSectionVariableContentsIml
+ );
+
+ return Result;
+}
diff --git a/private/sdktools/instaler/compinst.rc b/private/sdktools/instaler/compinst.rc
new file mode 100644
index 000000000..a5f44b528
--- /dev/null
+++ b/private/sdktools/instaler/compinst.rc
@@ -0,0 +1,12 @@
+#include "instaler.h"
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Windows NT Instaler Compare Program"
+#define VER_INTERNALNAME_STR "COMPINST\0"
+#define VER_ORIGINALFILENAME_STR "COMPINST.EXE"
+
+#include "common.ver"
+
+RCINCLUDE errormsg.rc
diff --git a/private/sdktools/instaler/debug.c b/private/sdktools/instaler/debug.c
new file mode 100644
index 000000000..c20b9eed4
--- /dev/null
+++ b/private/sdktools/instaler/debug.c
@@ -0,0 +1,481 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ Main loop for INSTALER program
+
+Author:
+
+ Steve Wood (stevewo) 09-Aug-1994
+
+Revision History:
+
+--*/
+
+#include "instaler.h"
+
+DWORD
+DebugEventHandler(
+ LPDEBUG_EVENT DebugEvent
+ );
+
+VOID
+InstallBreakpointsForDLL(
+ PPROCESS_INFO Process,
+ LPVOID BaseOfDll
+ );
+
+VOID
+RemoveBreakpointsForDLL(
+ PPROCESS_INFO Process,
+ LPVOID BaseOfDll
+ );
+
+char *DebugEventNames[] = {
+ "Unknown debug event",
+ "EXCEPTION_DEBUG_EVENT",
+ "CREATE_THREAD_DEBUG_EVENT",
+ "CREATE_PROCESS_DEBUG_EVENT",
+ "EXIT_THREAD_DEBUG_EVENT",
+ "EXIT_PROCESS_DEBUG_EVENT",
+ "LOAD_DLL_DEBUG_EVENT",
+ "UNLOAD_DLL_DEBUG_EVENT",
+ "OUTPUT_DEBUG_STRING_EVENT",
+ "RIP_EVENT",
+ "Unknown debug event",
+ "Unknown debug event",
+ "Unknown debug event",
+ "Unknown debug event",
+ "Unknown debug event",
+ "Unknown debug event",
+ "Unknown debug event",
+ "Unknown debug event",
+ "Unknown debug event",
+ "Unknown debug event",
+ NULL
+};
+
+
+VOID
+DebugEventLoop( VOID )
+{
+ DEBUG_EVENT DebugEvent;
+ DWORD ContinueStatus;
+ DWORD OldPriority;
+
+ //
+ // We want to process debug events quickly
+ //
+
+ OldPriority = GetPriorityClass( GetCurrentProcess() );
+ SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS );
+
+ do {
+ if (!WaitForDebugEvent( &DebugEvent, INFINITE )) {
+ DeclareError( INSTALER_WAITDEBUGEVENT_FAILED, GetLastError() );
+ ExitProcess( 1 );
+ }
+
+ if (DebugEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
+ if (DebugEvent.u.Exception.ExceptionRecord.ExceptionCode != STATUS_BREAKPOINT &&
+ DebugEvent.u.Exception.ExceptionRecord.ExceptionCode != STATUS_SINGLE_STEP
+ ) {
+ DbgEvent( DBGEVENT, ( "Debug exception event - Code: %x Address: %x Info: [%u] %x %x %x %x\n",
+ DebugEvent.u.Exception.ExceptionRecord.ExceptionCode,
+ DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress,
+ DebugEvent.u.Exception.ExceptionRecord.NumberParameters,
+ DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 0 ],
+ DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 1 ],
+ DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 2 ],
+ DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 3 ]
+ )
+ );
+ }
+ }
+ else {
+ DbgEvent( DBGEVENT, ( "Debug %s (%x) event\n",
+ DebugEventNames[ DebugEvent.dwDebugEventCode ],
+ DebugEvent.dwDebugEventCode
+ )
+ );
+ }
+ ContinueStatus = DebugEventHandler( &DebugEvent );
+ if (!ContinueDebugEvent( DebugEvent.dwProcessId,
+ DebugEvent.dwThreadId,
+ ContinueStatus
+ )
+ ) {
+ DeclareError( INSTALER_CONTDEBUGEVENT_FAILED, GetLastError() );
+ ExitProcess( 1 );
+ }
+ }
+ while (!IsListEmpty( &ProcessListHead ));
+
+
+ //
+ // Drop back to old priority to interact with user.
+ //
+
+ SetPriorityClass( GetCurrentProcess(), OldPriority );
+}
+
+
+DWORD
+DebugEventHandler(
+ LPDEBUG_EVENT DebugEvent
+ )
+{
+ DWORD ContinueStatus;
+ PPROCESS_INFO Process;
+ PTHREAD_INFO Thread;
+ PBREAKPOINT_INFO Breakpoint;
+
+ ContinueStatus = (DWORD)DBG_CONTINUE;
+ if (FindProcessAndThreadForEvent( DebugEvent, &Process, &Thread )) {
+ switch (DebugEvent->dwDebugEventCode) {
+ case CREATE_PROCESS_DEBUG_EVENT:
+ //
+ // Create process event includes first thread of process
+ // as well. Remember process and thread in our process tree
+ //
+
+ if (AddProcess( DebugEvent, &Process )) {
+ InheritHandles( Process );
+ AddThread( DebugEvent, Process, &Thread );
+ }
+ break;
+
+ case EXIT_PROCESS_DEBUG_EVENT:
+ //
+ // Exit process event includes last thread of process
+ // as well. Remove process and thread from our process tree
+ //
+
+ if (DeleteThread( Process, Thread )) {
+ DeleteProcess( Process );
+ }
+ break;
+
+ case CREATE_THREAD_DEBUG_EVENT:
+ //
+ // Create thread. Remember thread in our process tree.
+ //
+
+ AddThread( DebugEvent, Process, &Thread );
+ break;
+
+ case EXIT_THREAD_DEBUG_EVENT:
+ //
+ // Exit thread. Remove thread from our process tree.
+ //
+
+ DeleteThread( Process, Thread );
+ break;
+
+ case LOAD_DLL_DEBUG_EVENT:
+ //
+ // DLL just mapped into process address space. See if it is one
+ // of the ones we care about and if so, install the necessary
+ // breakpoints.
+ //
+
+ InstallBreakpointsForDLL( Process, DebugEvent->u.LoadDll.lpBaseOfDll );
+ break;
+
+ case UNLOAD_DLL_DEBUG_EVENT:
+ //
+ // DLL just unmapped from process address space. See if it is one
+ // of the ones we care about and if so, remove the breakpoints we
+ // installed when it was mapped.
+ //
+
+ RemoveBreakpointsForDLL( Process, DebugEvent->u.UnloadDll.lpBaseOfDll );
+ break;
+
+ case OUTPUT_DEBUG_STRING_EVENT:
+ case RIP_EVENT:
+ //
+ // Ignore these
+ //
+ break;
+
+ case EXCEPTION_DEBUG_EVENT:
+ //
+ // Assume we wont handle this exception
+ //
+
+ ContinueStatus = (DWORD)DBG_EXCEPTION_NOT_HANDLED;
+ switch (DebugEvent->u.Exception.ExceptionRecord.ExceptionCode) {
+ //
+ // Breakpoint exception.
+ //
+
+ case STATUS_BREAKPOINT:
+ EnterCriticalSection( &BreakTable );
+ if (Thread->BreakpointToStepOver != NULL) {
+ //
+ // If this breakpoint was in place to step over an API entry
+ // point breakpoint, then deal with it by ending single
+ // step mode, resuming all threads in the process and
+ // reinstalling the API breakpoint we just stepped over.
+ // Finally return handled for this exception so the
+ // thread can proceed.
+ //
+
+ EndSingleStepBreakpoint( Process, Thread );
+ HandleThreadsForSingleStep( Process, Thread, FALSE );
+ InstallBreakpoint( Process, Thread->BreakpointToStepOver );
+ Thread->BreakpointToStepOver = NULL;
+ ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED;
+ }
+ else {
+ //
+ // Otherwise, see if this breakpoint is either an API entry
+ // point breakpoint for the process or a return address
+ // breakpoint for a thread in the process.
+ //
+ Breakpoint = FindBreakpoint( DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress,
+ Process,
+ Thread
+ );
+ if (Breakpoint != NULL) {
+ //
+ // This is one of our breakpoints, call the breakpoint
+ // handler.
+ //
+ if (HandleBreakpoint( Process, Thread, Breakpoint )) {
+ //
+ // Now single step over this breakpoint, by removing it and
+ // setting a breakpoint at the next instruction (or using
+ // single stepmode if the processor supports it). We also
+ // suspend all the threads in the process except this one so
+ // we know the next breakpoint/single step exception we see
+ // for this process will be for this one.
+ //
+
+ Thread->BreakpointToStepOver = Breakpoint;
+ RemoveBreakpoint( Process, Breakpoint );
+ HandleThreadsForSingleStep( Process, Thread, TRUE );
+ BeginSingleStepBreakpoint( Process, Thread );
+ }
+ else {
+ Thread->BreakpointToStepOver = NULL;
+ }
+
+ ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED;
+ }
+ else {
+ DbgEvent( DBGEVENT, ( "Skipping over hardcoded breakpoint at %x\n",
+ DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress
+ )
+ );
+
+ //
+ // If not one of our breakpoints, assume it is a hardcoded
+ // breakpoint and skip over it. This will get by the one
+ // breakpoint in LdrInit triggered by the process being
+ // invoked with DEBUG_PROCESS.
+ //
+
+ if (SkipOverHardcodedBreakpoint( Process,
+ Thread,
+ DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress
+ )
+ ) {
+ //
+ // If we successfully skipped over this hard-coded breakpoint
+ // then return handled for this exception.
+ //
+
+ ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED;
+ }
+ }
+ }
+ LeaveCriticalSection( &BreakTable );
+ break;
+
+ case STATUS_SINGLE_STEP:
+ //
+ // We should only see this exception on processors that
+ // support a single step mode.
+ //
+
+ EnterCriticalSection( &BreakTable );
+ if (Thread->BreakpointToStepOver != NULL) {
+ EndSingleStepBreakpoint( Process, Thread );
+ HandleThreadsForSingleStep( Process, Thread, FALSE );
+ InstallBreakpoint( Process, Thread->BreakpointToStepOver );
+ Thread->BreakpointToStepOver = NULL;
+ ContinueStatus = (DWORD)DBG_EXCEPTION_HANDLED;
+ }
+ LeaveCriticalSection( &BreakTable );
+ break;
+
+ case STATUS_VDM_EVENT:
+ //
+ // Returning NOT_HANDLED will cause the default behavior to
+ // occur.
+ //
+ break;
+
+ default:
+ DbgEvent( DBGEVENT, ( "Unknown exception: %08x at %08x\n",
+ DebugEvent->u.Exception.ExceptionRecord.ExceptionCode,
+ DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress
+ )
+ );
+ break;
+ }
+ break;
+
+ default:
+ DbgEvent( DBGEVENT, ( "Unknown debug event\n" ) );
+ break;
+ }
+ }
+ return( ContinueStatus );
+}
+
+
+VOID
+InstallBreakpointsForDLL(
+ PPROCESS_INFO Process,
+ LPVOID BaseOfDll
+ )
+{
+ UCHAR ModuleIndex, ApiIndex;
+ PBREAKPOINT_INFO Breakpoint;
+
+ //
+ // Loop over module table to see if the base address of this DLL matches
+ // one of the module handles we want to set breakpoints in. If not, ignore
+ // event.
+ //
+
+ for (ModuleIndex=0; ModuleIndex<MAXIMUM_MODULE_INDEX; ModuleIndex++) {
+ if (ModuleInfo[ ModuleIndex ].ModuleHandle == BaseOfDll) {
+ //
+ // Loop over the list of API entry points for this module and set
+ // a process specific breakpoint at the first instruction of each
+ // entrypoint.
+ //
+
+ for (ApiIndex=0; ApiIndex<MAXIMUM_API_INDEX; ApiIndex++) {
+ if (ModuleIndex == ApiInfo[ ApiIndex ].ModuleIndex) {
+ if (CreateBreakpoint( ApiInfo[ ApiIndex ].EntryPointAddress,
+ Process,
+ NULL, // process specific
+ ApiIndex,
+ NULL,
+ &Breakpoint
+ )
+ ) {
+ Breakpoint->ModuleName = ModuleInfo[ ApiInfo[ ApiIndex ].ModuleIndex ].ModuleName;
+ Breakpoint->ProcedureName = ApiInfo[ ApiIndex ].EntryPointName;
+ DbgEvent( DBGEVENT, ( "Installed breakpoint for %ws!%s at %08x\n",
+ Breakpoint->ModuleName,
+ Breakpoint->ProcedureName,
+ ApiInfo[ ApiIndex ].EntryPointAddress
+ )
+ );
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+VOID
+RemoveBreakpointsForDLL(
+ PPROCESS_INFO Process,
+ LPVOID BaseOfDll
+ )
+{
+ UCHAR ModuleIndex, ApiIndex;
+
+ //
+ // Loop over module index to see if the base address of this DLL matches
+ // one of the module handles we set breakpoints in above. If not, ignore
+ // event.
+ //
+
+ for (ModuleIndex=0; ModuleIndex<MAXIMUM_MODULE_INDEX; ModuleIndex++) {
+ if (ModuleInfo[ ModuleIndex ].ModuleHandle == BaseOfDll) {
+ //
+ // Loop over the list of API entry points for this module and remove
+ // each process specific breakpoint set above for each entrypoint.
+ //
+
+ for (ApiIndex=0; ApiIndex<MAXIMUM_API_INDEX; ApiIndex++) {
+ if (ModuleIndex == ApiInfo[ ApiIndex ].ModuleIndex) {
+ DestroyBreakpoint( ApiInfo[ ApiIndex ].EntryPointAddress,
+ Process,
+ NULL // process specific
+ );
+ }
+ }
+ break;
+ }
+ }
+}
+
+
+BOOLEAN
+InstallBreakpoint(
+ PPROCESS_INFO Process,
+ PBREAKPOINT_INFO Breakpoint
+ )
+{
+ if (!Breakpoint->SavedInstructionValid &&
+ !ReadMemory( Process,
+ Breakpoint->Address,
+ &Breakpoint->SavedInstruction,
+ SizeofBreakpointInstruction,
+ "save instruction"
+ )
+ ) {
+ return FALSE;
+ }
+ else
+ if (!WriteMemory( Process,
+ Breakpoint->Address,
+ BreakpointInstruction,
+ SizeofBreakpointInstruction,
+ "breakpoint instruction"
+ )
+ ) {
+ return FALSE;
+ }
+ else {
+ Breakpoint->SavedInstructionValid = TRUE;
+ return TRUE;
+ }
+}
+
+BOOLEAN
+RemoveBreakpoint(
+ PPROCESS_INFO Process,
+ PBREAKPOINT_INFO Breakpoint
+ )
+{
+ if (!Breakpoint->SavedInstructionValid ||
+ !WriteMemory( Process,
+ Breakpoint->Address,
+ &Breakpoint->SavedInstruction,
+ SizeofBreakpointInstruction,
+ "restore saved instruction"
+ )
+ ) {
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
diff --git a/private/sdktools/instaler/dir.bmp b/private/sdktools/instaler/dir.bmp
new file mode 100644
index 000000000..d8794617d
--- /dev/null
+++ b/private/sdktools/instaler/dir.bmp
Binary files differ
diff --git a/private/sdktools/instaler/error.c b/private/sdktools/instaler/error.c
new file mode 100644
index 000000000..2241d8642
--- /dev/null
+++ b/private/sdktools/instaler/error.c
@@ -0,0 +1,202 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ error.c
+
+Abstract:
+
+ Error handle module for the INSTALER program
+
+Author:
+
+ Steve Wood (stevewo) 09-Aug-1994
+
+Revision History:
+
+--*/
+
+#include "instaler.h"
+
+VOID
+TraceDisplay(
+ const char *FormatString,
+ ...
+ )
+{
+ va_list arglist;
+
+ va_start( arglist, FormatString );
+ vprintf( FormatString, arglist );
+ if (InstalerLogFile) {
+ vfprintf( InstalerLogFile, FormatString, arglist );
+ }
+ va_end( arglist );
+ fflush( stdout );
+ return;
+}
+
+VOID
+CDECL
+DeclareError(
+ UINT ErrorCode,
+ UINT SupplementalErrorCode,
+ ...
+ )
+{
+ va_list arglist;
+ HMODULE ModuleHandle;
+ DWORD Flags, Size;
+ WCHAR MessageBuffer[ 512 ];
+
+ va_start( arglist, SupplementalErrorCode );
+
+ if ((ErrorCode & 0x0FFF0000) >> 16 == FACILITY_APPLICATION) {
+ ModuleHandle = InstalerModuleHandle;
+ Flags = FORMAT_MESSAGE_FROM_HMODULE;
+ }
+ else
+ if ((ErrorCode & 0x0FFF0000) == FACILITY_NT) {
+ ErrorCode ^= FACILITY_NT;
+ ModuleHandle = ModuleInfo[ NTDLL_MODULE_INDEX ].ModuleHandle;
+ Flags = FORMAT_MESSAGE_FROM_HMODULE;
+ }
+ else {
+ ModuleHandle = NULL;
+ Flags = FORMAT_MESSAGE_FROM_SYSTEM;
+ }
+
+ Size = FormatMessage( Flags,
+ (LPCVOID)ModuleHandle,
+ ErrorCode,
+ 0,
+ MessageBuffer,
+ sizeof( MessageBuffer ) / sizeof( WCHAR ),
+ &arglist
+ );
+ va_end( arglist );
+
+ if (Size != 0) {
+ printf( "INSTALER: %ws", MessageBuffer );
+ }
+ else {
+ printf( "INSTALER: Unable to get message text for %08x\n", ErrorCode );
+ }
+
+ if (ModuleHandle == InstalerModuleHandle &&
+ SupplementalErrorCode != 0 &&
+ SupplementalErrorCode != ERROR_GEN_FAILURE &&
+ SupplementalErrorCode != STATUS_UNSUCCESSFUL
+ ) {
+ if ((SupplementalErrorCode & 0x0FFF0000) == FACILITY_NT) {
+ SupplementalErrorCode ^= FACILITY_NT;
+ ModuleHandle = ModuleInfo[ NTDLL_MODULE_INDEX ].ModuleHandle;
+ Flags = FORMAT_MESSAGE_FROM_HMODULE;
+ }
+ else {
+ ModuleHandle = NULL;
+ Flags = FORMAT_MESSAGE_FROM_SYSTEM;
+ }
+ Size = FormatMessage( Flags,
+ (LPCVOID)ModuleHandle,
+ SupplementalErrorCode,
+ 0,
+ MessageBuffer,
+ sizeof( MessageBuffer ) / sizeof( WCHAR ),
+ NULL
+ );
+ if (Size != 0) {
+ while (Size != 0 && MessageBuffer[ Size ] <= L' ') {
+ MessageBuffer[ Size ] = UNICODE_NULL;
+ Size -= 1;
+ }
+
+ printf( " '%ws'\n", MessageBuffer );
+ }
+ else {
+ printf( "INSTALER: Unable to get message text for %08x\n", SupplementalErrorCode );
+ }
+ }
+
+
+
+ return;
+}
+
+WCHAR MessageBoxTitle[ MAX_PATH ];
+
+
+UINT
+CDECL
+AskUser(
+ UINT MessageBoxFlags,
+ UINT MessageId,
+ UINT NumberOfArguments,
+ ...
+ )
+{
+ va_list arglist;
+ HMODULE ModuleHandle;
+ DWORD Flags, Size;
+ WCHAR MessageBuffer[ 512 ];
+ PWSTR s;
+ ULONG Args[ 24 ];
+ PULONG p;
+
+ if (MessageBoxTitle[ 0 ] == UNICODE_NULL) {
+ Args[ 0 ] = (ULONG)InstallationName;
+ Args[ 1 ] = 0;
+ Size = FormatMessageW( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ (LPCVOID)InstalerModuleHandle,
+ INSTALER_ASKUSER_TITLE,
+ 0,
+ MessageBoxTitle,
+ sizeof( MessageBoxTitle ) / sizeof( WCHAR ),
+ (va_list *)Args
+ );
+ if (Size == 0) {
+ _snwprintf( MessageBoxTitle,
+ sizeof( MessageBoxTitle ) / sizeof( WCHAR ),
+ L"Application Installation Monitor Program - %ws",
+ InstallationName
+ );
+ }
+ else {
+ if ((s = wcschr( MessageBoxTitle, L'\r' )) ||
+ (s = wcschr( MessageBoxTitle, L'\n' ))
+ ) {
+ *s = UNICODE_NULL;
+ }
+ }
+ }
+
+ va_start( arglist, NumberOfArguments );
+ p = Args;
+ while (NumberOfArguments--) {
+ *p++ = va_arg( arglist, ULONG );
+ }
+ *p++ = 0;
+ va_end( arglist );
+
+ Size = FormatMessageW( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ (LPCVOID)InstalerModuleHandle,
+ MessageId,
+ 0,
+ MessageBuffer,
+ sizeof( MessageBuffer ) / sizeof( WCHAR ),
+ (va_list *)Args
+ );
+
+ if (Size != 0) {
+ return MessageBox( NULL,
+ MessageBuffer,
+ MessageBoxTitle,
+ MB_SETFOREGROUND | MessageBoxFlags
+ );
+ }
+ else {
+ return IDOK;
+ }
+}
diff --git a/private/sdktools/instaler/errormsg.mc b/private/sdktools/instaler/errormsg.mc
new file mode 100644
index 000000000..4c91402ef
--- /dev/null
+++ b/private/sdktools/instaler/errormsg.mc
@@ -0,0 +1,219 @@
+;/*++ BUILD Version: 0001 // Increment this if a change has global effects
+;
+;Copyright (c) 1991-1992 Microsoft Corporation
+;
+;Module Name:
+;
+; errormsg.h
+;
+;Abstract:
+;
+; This file contains the error code definitions and message for the
+; INSTALER program.
+;
+;Author:
+;
+; Steve Wood (stevewo) 09-Aug-1994
+;
+;--*/
+;
+;#ifndef _INSTALER_ERRORMSG_
+;#define _INSTALER_ERRORMSG_
+;
+;
+
+SeverityNames=(Success=0x0:APP_SEVERITY_SUCCESS
+ Informational=0x1:APP_SEVERITY_INFORMATIONAL
+ Warning=0x2:APP_SEVERITY_WARNING
+ Error=0x3:APP_SEVERITY_ERROR
+ )
+
+FacilityNames=(Application=0x100:FACILITY_APPLICATION)
+
+;#define FACILITY_NT 0x0FFF0000
+
+MessageId=0001 Severity=Error Facility=Application SymbolicName=INSTALER_MISSING_MODULE
+Language=English
+Unable to find %1 dynamic link library.
+.
+
+MessageId= Severity=Error Facility=Application SymbolicName=INSTALER_MISSING_ENTRYPOINT
+Language=English
+Unable to find %1 entry point in %2 dynamic link library.
+.
+
+MessageId= Severity=Error Facility=Application SymbolicName=INSTALER_CANT_DEBUG_PROGRAM
+Language=English
+Unable to debug '%1'
+.
+
+MessageId= Severity=Error Facility=Application SymbolicName=INSTALER_WAITDEBUGEVENT_FAILED
+Language=English
+Unable to wait for debug event.
+.
+
+MessageId= Severity=Error Facility=Application SymbolicName=INSTALER_CONTDEBUGEVENT_FAILED
+Language=English
+Unable to continue debug event.
+.
+
+MessageId= Severity=Error Facility=Application SymbolicName=INSTALER_DUPLICATE_PROCESS_ID
+Language=English
+Duplicate Process Id (%1!x!).
+.
+
+MessageId= Severity=Error Facility=Application SymbolicName=INSTALER_MISSING_PROCESS_ID
+Language=English
+Missing Process Id (%1!x!).
+.
+
+MessageId= Severity=Error Facility=Application SymbolicName=INSTALER_DUPLICATE_THREAD_ID
+Language=English
+Duplicate Thread Id (%1!x!) for Process Id (%2!x!)
+.
+
+MessageId= Severity=Error Facility=Application SymbolicName=INSTALER_MISSING_THREAD_ID
+Language=English
+Missing Thread Id (%1!x!) for Process Id (%2!x!)
+.
+
+MessageId= Severity=Error Facility=Application SymbolicName=INSTALER_CANT_ACCESS_FILE
+Language=English
+Unable to access file (%1!ws!) for comparison. Assuming different.
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_ASKUSER_TITLE
+Language=English
+Application Installation Monitor - %1!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_ASKUSER_ROOTSCAN
+Language=English
+The application installation program is about to scan root directory of %1!ws!
+Press cancel if you don't want the program to do that.
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_ASKUSER_GETVERSION
+Language=English
+The application installation program is about to ask for the version of
+the operating system. Press OK if you want to tell the truth. Press
+cancel if you want to lie to the program and tell it that it is running
+on Windows 95
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_ASKUSER_REGCONNECT
+Language=English
+The application installation program is about to connect to the registry of a
+remote machine (%1!ws!) The INSTALER program is unable to track changes made to
+a remote registry. Press OK if you want to proceed anyway. Press cancel if you
+want to fail the program's attempt to connect to the registry of the remote machine.
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_SET_DIRECTORY
+Language=English
+%2!05u! Current Directory now: %1!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_INI_CREATE
+Language=English
+%5!05u! Created: %1!ws! [%2!ws!] %3!ws! = '%4!ws!'
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_INI_DELETE
+Language=English
+%5!05u! Deleted: %1!ws! [%2!ws!] %3!ws! = '%4!ws!'
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_INI_CHANGE
+Language=English
+%6!05u! Changed: %1!ws! [%2!ws!] %3!ws! = '%4!ws!' (was '%5!ws!')
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_INI_DELETE_SECTION
+Language=English
+%3!05u! Deleted: %1!ws! [%2!ws!]
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_SCAN_DIRECTORY
+Language=English
+%4!05u! Scanned %3!u! entries from directory: %1!ws!\%2!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_CREATE_FILE
+Language=English
+%3!05u! Created %1!ws!: %2!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_WRITE_FILE
+Language=English
+%3!05u! Writing %1!ws!: %2!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_READ_FILE
+Language=English
+%3!05u! Reading %1!ws!: %2!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_DELETE_FILE
+Language=English
+%3!05u! Deleted %1!ws!: %2!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_DELETE_TEMP_FILE
+Language=English
+%3!05u! Deleted %1!ws!: %2!ws! (temporary)
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_RENAME_FILE
+Language=English
+%4!05u! Renamed %1!ws!: %2!ws!
+ to: %3!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_RENAME_TEMP_FILE
+Language=English
+%4!05u! Renamed %1!ws!: %2!ws! (temporary)
+ to: %3!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_WRITE_KEY
+Language=English
+%2!05u! Opened key for write: %1!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_READ_KEY
+Language=English
+%2!05u! Opened key for read: %1!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_DELETE_KEY
+Language=English
+%2!05u! Deleted key: %1!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_DELETE_TEMP_KEY
+Language=English
+%2!05u! Deleted key: %1!ws! (temporary)
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_SET_KEY_VALUE
+Language=English
+%3!05u! Set value (%1!ws!) for key: %2!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_DELETE_KEY_VALUE
+Language=English
+%3!05u! Deleted value (%1!ws!) for key: %2!ws!
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_DELETE_KEY_TEMP_VALUE
+Language=English
+%3!05u! Deleted value (%1!ws!) for key: %2!ws! (temporary)
+.
+
+MessageId= Severity=Informational Facility=Application SymbolicName=INSTALER_EVENT_GETVERSION
+Language=English
+%2!05u! GetVersion will return %1!ws!
+.
+
+;#endif // _INSTALER_ERRORMSG_
diff --git a/private/sdktools/instaler/event.c b/private/sdktools/instaler/event.c
new file mode 100644
index 000000000..492504f63
--- /dev/null
+++ b/private/sdktools/instaler/event.c
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ event.c
+
+Abstract:
+
+ Log formatted events to a file and possibly the console too
+
+Author:
+
+ Steve Wood (stevewo) 09-Aug-1994
+
+Revision History:
+
+--*/
+
+#include "instaler.h"
+
+VOID
+CDECL
+LogEvent(
+ UINT MessageId,
+ UINT NumberOfArguments,
+ ...
+ )
+{
+ va_list arglist;
+ HMODULE ModuleHandle;
+ DWORD Flags, Size;
+ WCHAR MessageBuffer[ 512 ];
+ PWSTR s;
+ ULONG Args[ 24 ];
+ PULONG p;
+
+ va_start( arglist, NumberOfArguments );
+ p = Args;
+ while (NumberOfArguments--) {
+ *p++ = va_arg( arglist, ULONG );
+ }
+ *p++ = ((GetTickCount() - StartProcessTickCount) / 1000); // Seconds since the start
+ *p++ = 0;
+ va_end( arglist );
+
+ Size = FormatMessageW( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ (LPCVOID)InstalerModuleHandle,
+ MessageId,
+ 0,
+ MessageBuffer,
+ sizeof( MessageBuffer ) / sizeof( WCHAR ),
+ (va_list *)Args
+ );
+ if (Size != 0) {
+ s = MessageBuffer;
+ while (s = wcschr( s, L'\r' )) {
+ if (s[1] == '\n') {
+ wcscpy( s, s+1 );
+ }
+ else {
+ s += 1;
+ }
+ }
+ printf( "%ws", MessageBuffer );
+ if (InstalerLogFile) {
+ fprintf( InstalerLogFile, "%ws", MessageBuffer );
+ }
+ }
+ else {
+ printf( "INSTALER: Unable to get message text for %08x\n", MessageId );
+ }
+
+ return;
+}
diff --git a/private/sdktools/instaler/file.bmp b/private/sdktools/instaler/file.bmp
new file mode 100644
index 000000000..24824e130
--- /dev/null
+++ b/private/sdktools/instaler/file.bmp
Binary files differ
diff --git a/private/sdktools/instaler/file.c b/private/sdktools/instaler/file.c
new file mode 100644
index 000000000..ea310c54c
--- /dev/null
+++ b/private/sdktools/instaler/file.c
@@ -0,0 +1,440 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ file.c
+
+Abstract:
+
+ This module implements the functions to save references to files for the
+ INSTALER program. Part of each reference is a handle to a backup copy
+ of a file if the reference is a write/delete/rename.
+
+Author:
+
+ Steve Wood (stevewo) 22-Aug-1994
+
+Revision History:
+
+--*/
+
+#include "instaler.h"
+
+
+BOOLEAN
+CreateFileReference(
+ PWSTR Name,
+ BOOLEAN WriteAccess,
+ PFILE_REFERENCE *ReturnedReference
+ )
+{
+ PFILE_REFERENCE p;
+ PWSTR BackupFileName;
+ WIN32_FIND_DATAW FindFileData;
+ HANDLE FindFileHandle;
+
+ *ReturnedReference = NULL;
+ p = FindFileReference( Name );
+ if (p != NULL) {
+ if (p->WriteAccess) {
+ *ReturnedReference = p;
+ return TRUE;
+ }
+ }
+ else {
+ p = AllocMem( sizeof( *p ) );
+ if (p == NULL) {
+ return FALSE;
+ }
+
+ InsertTailList( &FileReferenceListHead, &p->Entry );
+ NumberOfFileReferences += 1;
+ p->Name = Name;
+ }
+
+ BackupFileName = L"";
+ FindFileHandle = NULL;
+ if ((p->WriteAccess = WriteAccess) &&
+ (FindFileHandle = FindFirstFileW( p->Name, &FindFileData )) != INVALID_HANDLE_VALUE
+ ) {
+ if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ p->DirectoryFile = TRUE;
+ }
+ else {
+ p->BackupFileAttributes = FindFileData.dwFileAttributes;
+ p->BackupLastWriteTime = FindFileData.ftLastWriteTime;
+ p->BackupFileSize.LowPart = FindFileData.nFileSizeLow;
+ p->BackupFileSize.HighPart = FindFileData.nFileSizeHigh;
+ BackupFileName = CreateBackupFileName( &p->BackupFileUniqueId );
+ if (BackupFileName == NULL ||
+ !CopyFileW( Name, BackupFileName, TRUE )
+ ) {
+ p->BackupFileUniqueId = 0xFFFF; // Backup failed.
+ DeclareError( INSTALER_CANT_ACCESS_FILE, GetLastError(), Name );
+ }
+ }
+ }
+ else {
+ p->Created = p->WriteAccess;
+ }
+
+ if (FindFileHandle != NULL) {
+ FindClose( FindFileHandle );
+ }
+
+ *ReturnedReference = p;
+ return TRUE;
+}
+
+
+BOOLEAN
+CompleteFileReference(
+ PFILE_REFERENCE p,
+ BOOLEAN CallSuccessful,
+ BOOLEAN Deleted,
+ PFILE_REFERENCE RenameReference
+ )
+{
+ DWORD dwFileAttributes;
+
+ if (!CallSuccessful) {
+ DestroyFileReference( p );
+ return FALSE;
+ }
+
+ if (RenameReference) {
+ if (RenameReference->Created) {
+ LogEvent( INSTALER_EVENT_RENAME_TEMP_FILE,
+ 3,
+ RenameReference->DirectoryFile ? L"directory" : L"file",
+ RenameReference->Name,
+ p->Name
+ );
+
+ RenameReference->Name = p->Name;
+ DestroyFileReference( p );
+ return FALSE;
+ }
+
+ RenameReference->Deleted = TRUE;
+
+ LogEvent( INSTALER_EVENT_RENAME_FILE,
+ 3,
+ RenameReference->DirectoryFile ? L"directory" : L"file",
+ RenameReference->Name,
+ p->Name
+ );
+ }
+ else
+ if (Deleted && p->Created) {
+ LogEvent( INSTALER_EVENT_DELETE_TEMP_FILE,
+ 2,
+ p->DirectoryFile ? L"directory" : L"file",
+ p->Name
+ );
+ DestroyFileReference( p );
+ return FALSE;
+ }
+ else {
+ if (wcschr( p->Name, '\\' ) == NULL) {
+ //
+ // If no path separator, must be volume open. Treat
+ // as directory and dont touch file
+ //
+ p->DirectoryFile = TRUE;
+ }
+ else
+ if ((dwFileAttributes = GetFileAttributes( p->Name )) != 0xFFFFFFFF) {
+ p->DirectoryFile = (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ }
+
+ if (Deleted) {
+ LogEvent( INSTALER_EVENT_DELETE_FILE,
+ 2,
+ p->DirectoryFile ? L"directory" : L"file",
+ p->Name
+ );
+ }
+ else
+ if (p->WriteAccess) {
+ if (p->Created) {
+ LogEvent( INSTALER_EVENT_CREATE_FILE,
+ 2,
+ p->DirectoryFile ? L"directory" : L"file",
+ p->Name
+ );
+ }
+ else {
+ LogEvent( INSTALER_EVENT_WRITE_FILE,
+ 2,
+ p->DirectoryFile ? L"directory" : L"file",
+ p->Name
+ );
+ }
+ }
+ else {
+ LogEvent( INSTALER_EVENT_READ_FILE,
+ 2,
+ p->DirectoryFile ? L"directory" : L"file",
+ p->Name
+ );
+ }
+ }
+
+ p->Deleted = Deleted;
+ return TRUE;
+}
+
+
+BOOLEAN
+DestroyFileReference(
+ PFILE_REFERENCE p
+ )
+{
+ PWSTR BackupFileName;
+
+ if (!p->Created &&
+ (BackupFileName = FormatTempFileName( NULL, &p->BackupFileUniqueId ))
+ ) {
+ DeleteFileW( BackupFileName );
+ }
+
+ if (p->DateModified || p->AttributesModified) {
+ return FALSE;
+ }
+
+ RemoveEntryList( &p->Entry );
+ NumberOfFileReferences -= 1;
+ FreeMem( &p );
+ return TRUE;
+}
+
+
+
+PVOID
+MapFileForRead(
+ PWSTR FileName,
+ PULONG FileSize
+ )
+{
+ HANDLE FileHandle, MappingHandle;
+ PVOID FileData;
+ ULONG HighFileSize;
+
+ FileData = NULL;
+ FileHandle = CreateFileW( FileName,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+ if (FileHandle != INVALID_HANDLE_VALUE) {
+ *FileSize = GetFileSize( FileHandle, &HighFileSize );
+ if (*FileSize == 0) {
+ CloseHandle( FileHandle );
+ return (PVOID)0xFFFFFFFF;
+ }
+
+ MappingHandle = CreateFileMappingW( FileHandle,
+ NULL,
+ PAGE_READONLY,
+ 0,
+ *FileSize,
+ NULL
+ );
+ CloseHandle( FileHandle );
+ if (MappingHandle != NULL) {
+ FileData = MapViewOfFile( MappingHandle,
+ FILE_MAP_READ,
+ 0,
+ 0,
+ *FileSize
+ );
+ CloseHandle( MappingHandle );
+ }
+ }
+
+ return FileData;
+}
+
+
+BOOLEAN
+AreFileContentsEqual(
+ PWSTR FileName1,
+ PWSTR FileName2
+ )
+{
+ PVOID FileData1, FileData2;
+ ULONG FileSize1, FileSize2;
+ BOOLEAN Result;
+
+ Result = FALSE;
+ if (FileData1 = MapFileForRead( FileName1, &FileSize1 )) {
+ if (FileData2 = MapFileForRead( FileName2, &FileSize2 )) {
+ if (FileSize1 == FileSize2 &&
+ RtlCompareMemory( FileData1, FileData2, FileSize1 ) == FileSize1
+ ) {
+ Result = TRUE;
+ }
+ if (FileData2 != (PVOID)0xFFFFFFFF) {
+ UnmapViewOfFile( FileData2 );
+ }
+ }
+ else {
+ DeclareError( INSTALER_CANT_ACCESS_FILE, GetLastError(), FileName2 );
+ }
+
+ if (FileData1 != (PVOID)0xFFFFFFFF) {
+ UnmapViewOfFile( FileData1 );
+ }
+ }
+ else {
+ DeclareError( INSTALER_CANT_ACCESS_FILE, GetLastError(), FileName1 );
+ }
+
+ return Result;
+}
+
+BOOLEAN
+IsNewFileSameAsBackup(
+ PFILE_REFERENCE p
+ )
+{
+ WIN32_FIND_DATAW FindFileData;
+ HANDLE FindFileHandle;
+ PWSTR BackupFileName;
+
+ if ((FindFileHandle = FindFirstFileW( p->Name, &FindFileData )) != INVALID_HANDLE_VALUE) {
+ FindClose( FindFileHandle );
+ if (p->BackupFileAttributes != FindFileData.dwFileAttributes) {
+ p->AttributesModified = TRUE;
+ }
+
+ if (p->BackupLastWriteTime.dwLowDateTime != FindFileData.ftLastWriteTime.dwLowDateTime ||
+ p->BackupLastWriteTime.dwHighDateTime != FindFileData.ftLastWriteTime.dwHighDateTime
+ ) {
+ p->DateModified = TRUE;
+ }
+
+ if (BackupFileName = FormatTempFileName( NULL, &p->BackupFileUniqueId )) {
+ if (p->BackupFileSize.LowPart != FindFileData.nFileSizeLow ||
+ p->BackupFileSize.HighPart != FindFileData.nFileSizeHigh ||
+ !AreFileContentsEqual( BackupFileName, p->Name )
+ ) {
+ p->ContentsModified = TRUE;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+PFILE_REFERENCE
+FindFileReference(
+ PWSTR Name
+ )
+{
+ PFILE_REFERENCE p;
+ PLIST_ENTRY Head, Next;
+
+ Head = &FileReferenceListHead;
+ Next = Head->Flink;
+ while (Head != Next) {
+ p = CONTAINING_RECORD( Next, FILE_REFERENCE, Entry );
+ if (p->Name == Name) {
+ return p;
+ }
+
+ Next = Next->Flink;
+ }
+
+ return NULL;
+}
+
+
+VOID
+DumpFileReferenceList(
+ FILE *LogFile
+ )
+{
+ PFILE_REFERENCE p;
+ PLIST_ENTRY Head, Next;
+
+ Head = &FileReferenceListHead;
+ Next = Head->Flink;
+ while (Head != Next) {
+ p = CONTAINING_RECORD( Next, FILE_REFERENCE, Entry );
+ if (p->WriteAccess) {
+ if (!p->Deleted) {
+ if (p->BackupFileUniqueId == 0) {
+ if (p->Created) {
+ ImlAddFileRecord( pImlNew,
+ CreateNewFile,
+ p->Name,
+ NULL,
+ NULL,
+ 0
+ );
+ }
+ }
+ else {
+ if (p->ContentsModified) {
+ if (ImlAddFileRecord( pImlNew,
+ ModifyOldFile,
+ p->Name,
+ FormatTempFileName( NULL, &p->BackupFileUniqueId ),
+ NULL,
+ 0
+ )
+ ) {
+ DeleteFile( FormatTempFileName( NULL, &p->BackupFileUniqueId ) );
+ }
+ }
+ else
+ if (p->DateModified) {
+ ImlAddFileRecord( pImlNew,
+ ModifyFileDateTime,
+ p->Name,
+ NULL,
+ &p->BackupLastWriteTime,
+ p->BackupFileAttributes
+ );
+ }
+ else {
+ ImlAddFileRecord( pImlNew,
+ ModifyFileAttributes,
+ p->Name,
+ NULL,
+ &p->BackupLastWriteTime,
+ p->BackupFileAttributes
+ );
+ }
+ }
+ }
+ else {
+ if (ImlAddFileRecord( pImlNew,
+ DeleteOldFile,
+ p->Name,
+ FormatTempFileName( NULL, &p->BackupFileUniqueId ),
+ NULL,
+ 0
+ )
+ ) {
+ DeleteFile( FormatTempFileName( NULL, &p->BackupFileUniqueId ) );
+ }
+ }
+ }
+
+ Next = Next->Flink;
+ }
+
+ return;
+}
diff --git a/private/sdktools/instaler/fileman.c b/private/sdktools/instaler/fileman.c
new file mode 100644
index 000000000..7ebcd0f2f
--- /dev/null
+++ b/private/sdktools/instaler/fileman.c
@@ -0,0 +1,226 @@
+#include <windows.h>
+#include <stdlib.h>
+#include <string.h>
+#include "resource.h"
+#include "hlist.h"
+
+
+#define TYPE_FILE 1
+#define TYPE_DIR 2
+
+
+LRESULT WndProc(HWND,UINT,WPARAM,LPARAM);
+
+HINSTANCE hControlLib;
+HINSTANCE hInst;
+HBITMAP hFileBmp;
+HBITMAP hDirBmp;
+HWND hwndList;
+
+
+BOOL HListInitialize(HMODULE);
+
+
+
+int _cdecl
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ WNDCLASS wndclass;
+ HWND hwnd;
+ MSG msg;
+
+
+ hInst = GetModuleHandle( NULL );
+
+ HListInitialize( hInst );
+
+ hFileBmp = LoadBitmap( hInst, MAKEINTRESOURCE(FILEBMP) );
+ hDirBmp = LoadBitmap( hInst, MAKEINTRESOURCE(DIRBMP) );
+
+ wndclass.style = CS_HREDRAW | CS_VREDRAW;
+ wndclass.lpfnWndProc = WndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = hInst;
+ wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
+ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wndclass.hbrBackground = GetStockObject (WHITE_BRUSH);
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = "test";
+ RegisterClass (&wndclass);
+
+ hwnd = CreateWindow(
+ "test",
+ "Test Custon Control App",
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ NULL,
+ NULL,
+ GetModuleHandle(NULL),
+ NULL );
+
+ ShowWindow( hwnd, SW_SHOW );
+ UpdateWindow( hwnd );
+
+ while (GetMessage( &msg, NULL, 0, 0 )) {
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+
+ return 0;
+}
+
+DWORD CALLBACK
+ExpansionCallback(
+ LPDWORD type,
+ LPSTR *str,
+ LPSTR ref,
+ DWORD level,
+ DWORD nchild
+ )
+{
+ static WIN32_FIND_DATA fd = {0};
+ static HANDLE hFind = NULL;
+ static CHAR Dir[MAX_PATH*3];
+ CHAR NewDir[MAX_PATH*3];
+ LPSTR p;
+
+
+ if ((!hFind) && (*type == TYPE_FILE)) {
+ MessageBeep( 0 );
+ return HLB_END;
+ }
+
+ if (!hFind) {
+ if (nchild) {
+ //
+ // this node needs to be collapsed
+ //
+ return HLB_COLLAPSE;
+ }
+
+ if (!GetCurrentDirectory( sizeof(Dir), Dir )) {
+ return HLB_IGNORE;
+ }
+ p = ref;
+ NewDir[0] = 0;
+ while (p && *p) {
+ p += (strlen(p) + 1);
+ }
+ while (p != ref) {
+ p -= 2;
+ while (*p && p != ref) {
+ p--;
+ }
+ if (!*p) {
+ p++;
+ }
+ strcat( NewDir, p );
+ strcat( NewDir, "\\" );
+ }
+ if (!SetCurrentDirectory( NewDir )) {
+ return HLB_END;
+ }
+ hFind = FindFirstFile( "*.*", &fd );
+ if (hFind == INVALID_HANDLE_VALUE) {
+ hFind = NULL;
+ MessageBeep( 0 );
+ return HLB_END;
+ }
+ } else {
+ if (!FindNextFile( hFind, &fd )) {
+ FindClose( hFind );
+ SetCurrentDirectory( Dir );
+ hFind = NULL;
+ return HLB_END;
+ }
+ }
+
+
+ *str = fd.cFileName;
+
+ if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ *type = TYPE_DIR;
+ } else {
+ *type = TYPE_FILE;
+ }
+
+ if (level && fd.cFileName[0] == '.') {
+ return HLB_IGNORE;
+ }
+
+ return HLB_EXPAND;
+}
+
+VOID
+FillListBox(
+ VOID
+ )
+{
+ WIN32_FIND_DATA fd;
+ HANDLE hFind;
+ DWORD type;
+
+
+ hFind = FindFirstFile( "*.*", &fd );
+ if (hFind == INVALID_HANDLE_VALUE) {
+ return;
+ }
+
+ do {
+ if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ type = TYPE_DIR;
+ } else {
+ type = TYPE_FILE;
+ }
+ SendMessage( hwndList, HLB_ADDSTRING, type, (LPARAM) fd.cFileName );
+ } while(FindNextFile( hFind, &fd ));
+
+ FindClose( hFind );
+}
+
+LRESULT
+WndProc(
+ HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+{
+ RECT cRect;
+
+
+ switch (message) {
+ case WM_CREATE:
+ GetClientRect( hwnd, &cRect );
+ hwndList = CreateWindow(
+ "HList",
+ NULL,
+ WS_CHILD | WS_VISIBLE,
+ cRect.left,
+ cRect.top,
+ cRect.right - cRect.left,
+ cRect.bottom - cRect.top,
+ hwnd,
+ NULL,
+ GetModuleHandle(NULL),
+ NULL );
+ SendMessage( hwndList, HLB_REGISTER_CALLBACK, 0, (LPARAM)ExpansionCallback );
+ SendMessage( hwndList, HLB_REGISTER_TYPE, TYPE_FILE, (LPARAM)hFileBmp );
+ SendMessage( hwndList, HLB_REGISTER_TYPE, TYPE_DIR, (LPARAM)hDirBmp );
+ FillListBox();
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage( 0 );
+ return 0;
+ }
+
+ return DefWindowProc( hwnd, message, wParam, lParam );
+}
diff --git a/private/sdktools/instaler/handledb.c b/private/sdktools/instaler/handledb.c
new file mode 100644
index 000000000..f3ade0c56
--- /dev/null
+++ b/private/sdktools/instaler/handledb.c
@@ -0,0 +1,172 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ handledb.c
+
+Abstract:
+
+ This module contains the code to maintain an open handle data base for the
+ INSTALER program. This is used to track path names associated with open
+ handles so we can construct full paths from relative opens.
+
+Author:
+
+ Steve Wood (stevewo) 11-Aug-1994
+
+Revision History:
+
+--*/
+
+#include "instaler.h"
+
+char *HandleTypes[] = {
+ "File/Key",
+ "File",
+ "Key"
+};
+
+POPENHANDLE_INFO
+FindOpenHandle(
+ PPROCESS_INFO Process,
+ HANDLE Handle,
+ ULONG Type
+ )
+{
+ PLIST_ENTRY Next, Head;
+ POPENHANDLE_INFO p;
+
+ Head = &Process->OpenHandleListHead;
+ Next = Head->Flink;
+ while (Next != Head) {
+ p = CONTAINING_RECORD( Next, OPENHANDLE_INFO, Entry );
+ if (p->Handle == Handle && (Type == (ULONG)-1 || p->Type == Type)) {
+ return p;
+ }
+
+ Next = Next->Flink;
+ }
+
+ return NULL;
+}
+
+BOOLEAN
+AddOpenHandle(
+ PPROCESS_INFO Process,
+ HANDLE Handle,
+ ULONG Type,
+ PWSTR Name,
+ BOOLEAN InheritHandle
+ )
+{
+ POPENHANDLE_INFO p;
+
+ if (Type == (ULONG)-1) {
+ return FALSE;
+ }
+
+ if (FindOpenHandle( Process, Handle, Type ) != NULL) {
+ return FALSE;
+ }
+
+ p = AllocMem( sizeof( *p ) );
+ if (p == NULL) {
+ return FALSE;
+ }
+
+ p->Handle = Handle;
+ p->Type = (USHORT)Type;
+ p->Inherit = InheritHandle;
+ p->Name = Name;
+ p->LengthOfName = (USHORT)(wcslen( Name ) * sizeof( WCHAR ));
+ if (Type == HANDLE_TYPE_FILE && p->LengthOfName == (3 * sizeof( WCHAR ))) {
+ p->RootDirectory = TRUE;
+ }
+
+ InsertHeadList( &Process->OpenHandleListHead, &p->Entry );
+
+ return TRUE;
+}
+
+
+BOOLEAN
+DeleteOpenHandle(
+ PPROCESS_INFO Process,
+ HANDLE Handle,
+ ULONG Type
+ )
+{
+ POPENHANDLE_INFO p;
+
+ p = FindOpenHandle( Process, Handle, Type );
+ if (p == NULL) {
+ //
+ // We will see lots of close calls for handles we dont care
+ // about. Ignore them quietly.
+ //
+ return FALSE;
+ }
+
+ RemoveEntryList( &p->Entry );
+ FreeMem( &p->QueryName );
+ FreeMem( &p );
+ return TRUE;
+}
+
+
+static BOOLEAN IgnoredFirstProcess = FALSE;
+
+VOID
+InheritHandles(
+ PPROCESS_INFO Process
+ )
+{
+ PPROCESS_INFO ParentProcess;
+ PLIST_ENTRY Prev, Head;
+ POPENHANDLE_INFO p;
+
+ if (Process->ProcessInformation.InheritedFromUniqueProcessId == 0) {
+ return;
+ }
+
+ ParentProcess = FindProcessById( Process->ProcessInformation.InheritedFromUniqueProcessId );
+ if (ParentProcess == NULL) {
+ if (!IgnoredFirstProcess) {
+ IgnoredFirstProcess = TRUE;
+ }
+ else {
+ DbgEvent( INTERNALERROR, ("Unable to find parent process (%x)\n", Process->ProcessInformation.InheritedFromUniqueProcessId ) );
+ }
+
+ return;
+ }
+
+ Head = &ParentProcess->OpenHandleListHead;
+ Prev = Head->Blink;
+ while (Prev != Head) {
+ p = CONTAINING_RECORD( Prev, OPENHANDLE_INFO, Entry );
+ if (p->Inherit) {
+ if (!AddOpenHandle( Process,
+ p->Handle,
+ p->Type,
+ p->Name,
+ TRUE
+ )
+ ) {
+ DbgEvent( INTERNALERROR, ("Unable to inherit %s handle (%x) for '%ws' from process (%x)\n",
+ HandleTypes[ 1+p->Type ],
+ p->Handle,
+ p->Name,
+ Process->ProcessInformation.InheritedFromUniqueProcessId
+ )
+ );
+ }
+ }
+
+ Prev = Prev->Blink;
+ }
+
+ return;
+}
diff --git a/private/sdktools/instaler/handler.c b/private/sdktools/instaler/handler.c
new file mode 100644
index 000000000..f28754648
--- /dev/null
+++ b/private/sdktools/instaler/handler.c
@@ -0,0 +1,2355 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ handler.c
+
+Abstract:
+
+ This module contains the individual API handler routines for the INSTALER program
+
+Author:
+
+ Steve Wood (stevewo) 10-Aug-1994
+
+Revision History:
+
+--*/
+
+#include "instaler.h"
+#include <ntstatus.dbg>
+
+
+BOOLEAN
+HandleBreakpoint(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PBREAKPOINT_INFO Breakpoint
+ )
+{
+ BOOLEAN Result;
+ API_SAVED_PARAMETERS SavedParameters;
+ UCHAR ApiIndex;
+ PBREAKPOINT_INFO ReturnBreakpoint;
+
+ Result = FALSE;
+ ApiIndex = Breakpoint->ApiIndex;
+ if (ApiInfo[ ApiIndex ].ModuleIndex < Thread->ModuleIndexCurrentlyIn) {
+ return TRUE;
+ }
+
+ DbgEvent( DBGEVENT, ( "Processing Breakpoint at %ws!%s\n",
+ Breakpoint->ModuleName,
+ Breakpoint->ProcedureName
+ )
+ );
+
+ if (!Breakpoint->SavedParametersValid) {
+ memset( &SavedParameters, 0, sizeof( SavedParameters ) );
+
+ Result = TRUE;
+ if (ExtractProcedureParameters( Process,
+ Thread,
+ (PVOID)&SavedParameters.ReturnAddress,
+ ApiInfo[ ApiIndex ].SizeOfParameters,
+ (PULONG)&SavedParameters.InputParameters
+ )
+ ) {
+ if (ApiInfo[ ApiIndex ].EntryPointHandler == NULL ||
+ (ApiInfo[ ApiIndex ].EntryPointHandler)( Process,
+ Thread,
+ &SavedParameters
+ )
+ ) {
+ if (SavedParameters.ReturnAddress == NULL) {
+ Result = FALSE;
+ }
+ else
+ if (CreateBreakpoint( SavedParameters.ReturnAddress,
+ Process,
+ Thread, // thread specific
+ ApiIndex,
+ &SavedParameters,
+ &ReturnBreakpoint
+ )
+ ) {
+ ReturnBreakpoint->ModuleName = L"Return from";
+ ReturnBreakpoint->ProcedureName = Breakpoint->ProcedureName;
+ Thread->ModuleIndexCurrentlyIn = ApiInfo[ ApiIndex ].ModuleIndex;
+ }
+ }
+ }
+
+#if DBG
+ TrimTemporaryBuffer();
+#endif
+ }
+ else {
+ if (UndoReturnAddressBreakpoint( Process, Thread ) &&
+ ExtractProcedureReturnValue( Process,
+ Thread,
+ &Breakpoint->SavedParameters.ReturnValue,
+ ApiInfo[ ApiIndex ].SizeOfReturnValue
+ )
+ ) {
+ Breakpoint->SavedParameters.ReturnValueValid = TRUE;
+ if (ApiInfo[ ApiIndex ].EntryPointHandler != NULL) {
+ (ApiInfo[ ApiIndex ].EntryPointHandler)( Process,
+ Thread,
+ &Breakpoint->SavedParameters
+ );
+ }
+ }
+
+ Thread->ModuleIndexCurrentlyIn = 0;
+ FreeSavedCallState( &Breakpoint->SavedParameters.SavedCallState );
+ DestroyBreakpoint( Breakpoint->Address, Process, Thread );
+#if DBG
+ TrimTemporaryBuffer();
+#endif
+ Result = FALSE;
+ }
+
+ return Result;
+}
+
+
+BOOLEAN
+CreateSavedCallState(
+ PPROCESS_INFO Process,
+ PAPI_SAVED_CALL_STATE SavedCallState,
+ API_ACTION Action,
+ ULONG Type,
+ PWSTR FullName,
+ ...
+ )
+{
+ va_list arglist;
+
+ va_start( arglist, FullName );
+ SavedCallState->Action = Action;
+ SavedCallState->Type = Type;
+ SavedCallState->FullName = FullName;
+ switch (Action) {
+ case OpenPath:
+ SavedCallState->PathOpen.InheritHandle = va_arg( arglist, BOOLEAN );
+ SavedCallState->PathOpen.WriteAccessRequested = va_arg( arglist, BOOLEAN );
+ SavedCallState->PathOpen.ResultHandleAddress = va_arg( arglist, PHANDLE );
+ break;
+
+ case RenamePath:
+ SavedCallState->PathRename.NewName = va_arg( arglist, PWSTR );
+ SavedCallState->PathRename.ReplaceIfExists = va_arg( arglist, BOOLEAN );
+ break;
+
+ case DeletePath:
+ case QueryPath:
+ case SetValue:
+ case DeleteValue:
+ break;
+
+ case WriteIniValue:
+ SavedCallState->SetIniValue.SectionName = va_arg( arglist, PWSTR );
+ SavedCallState->SetIniValue.VariableName = va_arg( arglist, PWSTR );
+ SavedCallState->SetIniValue.VariableValue = va_arg( arglist, PWSTR );
+ break;
+
+ case WriteIniSection:
+ SavedCallState->SetIniSection.SectionName = va_arg( arglist, PWSTR );
+ SavedCallState->SetIniSection.SectionValue = va_arg( arglist, PWSTR );
+ break;
+
+ default:
+ return FALSE;
+ }
+ va_end( arglist );
+
+ return TRUE;
+}
+
+VOID
+FreeSavedCallState(
+ PAPI_SAVED_CALL_STATE CallState
+ )
+{
+ switch (CallState->Action) {
+ case WriteIniValue:
+ FreeMem( &CallState->SetIniValue.VariableValue );
+ break;
+
+ case WriteIniSection:
+ FreeMem( &CallState->SetIniSection.SectionValue );
+ break;
+ }
+
+ return;
+}
+
+
+
+BOOLEAN
+CaptureObjectAttributes(
+ PPROCESS_INFO Process,
+ POBJECT_ATTRIBUTES ObjectAttributesAddress,
+ POBJECT_ATTRIBUTES ObjectAttributes,
+ PUNICODE_STRING ObjectName
+ )
+{
+ if (ObjectAttributesAddress != NULL &&
+ ReadMemory( Process,
+ ObjectAttributesAddress,
+ ObjectAttributes,
+ sizeof( *ObjectAttributes ),
+ "object attributes"
+ ) &&
+ ObjectAttributes->ObjectName != NULL &&
+ ReadMemory( Process,
+ ObjectAttributes->ObjectName,
+ ObjectName,
+ sizeof( *ObjectName ),
+ "object name string"
+ )
+ ) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+BOOLEAN
+CaptureFullName(
+ PPROCESS_INFO Process,
+ ULONG Type,
+ HANDLE RootDirectory,
+ PWSTR Name,
+ ULONG Length,
+ PWSTR *ReturnedFullName
+ )
+{
+ POPENHANDLE_INFO HandleInfo;
+ UNICODE_STRING FullName;
+ ULONG LengthOfName;
+ PWSTR s;
+ BOOLEAN Result;
+
+ *ReturnedFullName = NULL;
+ if (RootDirectory != NULL) {
+ HandleInfo = FindOpenHandle( Process,
+ RootDirectory,
+ Type
+ );
+ if (HandleInfo == NULL) {
+ //
+ // If handle not found then we dont care about this open
+ // as it is relative to a device that is not local.
+ //
+ return FALSE;
+ }
+ }
+ else {
+ HandleInfo = NULL;
+ }
+
+ LengthOfName = Length + sizeof( UNICODE_NULL );
+ if (HandleInfo != NULL) {
+ LengthOfName += HandleInfo->LengthOfName +
+ sizeof( OBJ_NAME_PATH_SEPARATOR );
+ }
+
+ FullName.Buffer = AllocMem( LengthOfName );
+ if (FullName.Buffer == NULL) {
+ return FALSE;
+ }
+
+ s = FullName.Buffer;
+ if (HandleInfo != NULL) {
+ RtlMoveMemory( s,
+ HandleInfo->Name,
+ HandleInfo->LengthOfName
+ );
+ s = &FullName.Buffer[ HandleInfo->LengthOfName / sizeof( WCHAR ) ];
+ if (s[-1] != OBJ_NAME_PATH_SEPARATOR) {
+ *s++ = OBJ_NAME_PATH_SEPARATOR;
+ }
+ }
+
+ if (!ReadMemory( Process,
+ Name,
+ s,
+ Length,
+ "object name buffer"
+ )
+ ) {
+ FreeMem( &FullName.Buffer );
+ return FALSE;
+ }
+
+ s = &s[ Length / sizeof( WCHAR ) ];
+ *s = UNICODE_NULL;
+ FullName.Length = (PCHAR)s - (PCHAR)FullName.Buffer;
+ FullName.MaximumLength = (USHORT)(FullName.Length + sizeof( UNICODE_NULL ));
+ if (HandleInfo == NULL && Type == HANDLE_TYPE_FILE) {
+ Result = IsDriveLetterPath( &FullName );
+ }
+ else {
+ Result = TRUE;
+ }
+ *ReturnedFullName = AddName( &FullName );
+ FreeMem( &FullName.Buffer );
+ return Result;
+}
+
+
+BOOLEAN
+CaptureOpenState(
+ PPROCESS_INFO Process,
+ PAPI_SAVED_PARAMETERS Parameters,
+ POBJECT_ATTRIBUTES ObjectAttributesAddress,
+ BOOLEAN WriteAccess,
+ PHANDLE ResultHandleAddress,
+ ULONG Type
+ )
+{
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING ObjectName;
+ PWSTR FullName;
+ BOOLEAN Result;
+
+ Result = FALSE;
+ if (CaptureObjectAttributes( Process,
+ ObjectAttributesAddress,
+ &ObjectAttributes,
+ &ObjectName
+ ) &&
+ CaptureFullName( Process,
+ Type,
+ ObjectAttributes.RootDirectory,
+ ObjectName.Buffer,
+ ObjectName.Length,
+ &FullName
+ ) &&
+ CreateSavedCallState( Process,
+ &Parameters->SavedCallState,
+ OpenPath,
+ Type,
+ FullName,
+ (BOOLEAN)((ObjectAttributes.Attributes & OBJ_INHERIT) != 0),
+ WriteAccess,
+ ResultHandleAddress
+ )
+ ) {
+ if (Type == HANDLE_TYPE_FILE) {
+ Result = CreateFileReference( FullName,
+ Parameters->SavedCallState.PathOpen.WriteAccessRequested,
+ (PFILE_REFERENCE *)&Parameters->SavedCallState.Reference
+ );
+ }
+ else {
+ Result = CreateKeyReference( FullName,
+ Parameters->SavedCallState.PathOpen.WriteAccessRequested,
+ (PKEY_REFERENCE *)&Parameters->SavedCallState.Reference
+ );
+ }
+ }
+
+ return Result;
+}
+
+
+BOOLEAN
+CompleteOpenState(
+ PPROCESS_INFO Process,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PAPI_SAVED_CALL_STATE p = &Parameters->SavedCallState;
+ HANDLE Handle;
+ BOOLEAN CallSuccessful;
+ PFILE_REFERENCE FileReference;
+ PKEY_REFERENCE KeyReference;
+
+ if (NT_SUCCESS( Parameters->ReturnValue.ReturnedLong ) &&
+ ReadMemory( Process,
+ p->PathOpen.ResultHandleAddress,
+ &Handle,
+ sizeof( Handle ),
+ "result handle"
+ ) &&
+ AddOpenHandle( Process,
+ Handle,
+ p->Type,
+ p->FullName,
+ p->PathOpen.InheritHandle
+ )
+ ) {
+ CallSuccessful = TRUE;
+ }
+ else {
+ CallSuccessful = FALSE;
+ }
+ IncrementOpenCount( p->FullName, CallSuccessful );
+
+ if (p->Type == HANDLE_TYPE_FILE) {
+ FileReference = (PFILE_REFERENCE)p->Reference;
+ CompleteFileReference( FileReference,
+ CallSuccessful,
+ FALSE,
+ NULL
+ );
+ }
+ else
+ if (p->Type == HANDLE_TYPE_KEY) {
+ KeyReference = (PKEY_REFERENCE)p->Reference;
+ CompleteKeyReference( (PKEY_REFERENCE)p->Reference,
+ CallSuccessful,
+ FALSE
+ );
+ }
+
+ return TRUE;
+}
+
+#if TRACE_ENABLED
+
+
+ENUM_TYPE_NAMES FileAccessNames[] = {
+ FILE_READ_DATA, "FILE_READ_DATA",
+ FILE_READ_DATA, "FILE_READ_DATA",
+ FILE_WRITE_DATA, "FILE_WRITE_DATA",
+ FILE_APPEND_DATA, "FILE_APPEND_DATA",
+ FILE_READ_EA, "FILE_READ_EA",
+ FILE_WRITE_EA, "FILE_WRITE_EA",
+ FILE_EXECUTE, "FILE_EXECUTE",
+ FILE_DELETE_CHILD, "FILE_DELETE_CHILD",
+ FILE_READ_ATTRIBUTES, "FILE_READ_ATTRIBUTES",
+ FILE_WRITE_ATTRIBUTES, "FILE_WRITE_ATTRIBUTES",
+ DELETE, "DELETE",
+ READ_CONTROL, "READ_CONTROL",
+ WRITE_DAC, "WRITE_DAC",
+ WRITE_OWNER, "WRITE_OWNER",
+ SYNCHRONIZE, "SYNCHRONIZE",
+ GENERIC_READ, "GENERIC_READ",
+ GENERIC_WRITE, "GENERIC_WRITE",
+ GENERIC_EXECUTE, "GENERIC_EXECUTE",
+ GENERIC_ALL, "GENERIC_ALL",
+ 0xFFFFFFFF, "%x"
+};
+
+
+ENUM_TYPE_NAMES FileShareNames[] = {
+ FILE_SHARE_READ, "FILE_SHARE_READ",
+ FILE_SHARE_WRITE, "FILE_SHARE_WRITE",
+ FILE_SHARE_DELETE, "FILE_SHARE_DELETE",
+ 0xFFFFFFFF, "FILE_SHARE_NONE"
+};
+
+ENUM_TYPE_NAMES FileCreateDispositionNames[] = {
+ FILE_SUPERSEDE, "FILE_SUPERSEDE",
+ FILE_OPEN, "FILE_OPEN",
+ FILE_CREATE, "FILE_CREATE",
+ FILE_OPEN_IF, "FILE_OPEN_IF",
+ FILE_OVERWRITE, "FILE_OVERWRITE",
+ FILE_OVERWRITE_IF, "FILE_OVERWRITE_IF",
+ 0xFFFFFFFF, "%x"
+};
+
+ENUM_TYPE_NAMES FileCreateOptionNames[] = {
+ FILE_DIRECTORY_FILE, "FILE_DIRECTORY_FILE",
+ FILE_WRITE_THROUGH, "FILE_WRITE_THROUGH",
+ FILE_SEQUENTIAL_ONLY, "FILE_SEQUENTIAL_ONLY",
+ FILE_NO_INTERMEDIATE_BUFFERING, "FILE_NO_INTERMEDIATE_BUFFERING",
+ FILE_SYNCHRONOUS_IO_ALERT, "FILE_SYNCHRONOUS_IO_ALERT",
+ FILE_SYNCHRONOUS_IO_NONALERT, "FILE_SYNCHRONOUS_IO_NONALERT",
+ FILE_NON_DIRECTORY_FILE, "FILE_NON_DIRECTORY_FILE",
+ FILE_CREATE_TREE_CONNECTION, "FILE_CREATE_TREE_CONNECTION",
+ FILE_COMPLETE_IF_OPLOCKED, "FILE_COMPLETE_IF_OPLOCKED",
+ FILE_NO_EA_KNOWLEDGE, "FILE_NO_EA_KNOWLEDGE",
+ FILE_RANDOM_ACCESS, "FILE_RANDOM_ACCESS",
+ FILE_DELETE_ON_CLOSE, "FILE_DELETE_ON_CLOSE",
+ FILE_OPEN_BY_FILE_ID, "FILE_OPEN_BY_FILE_ID",
+ FILE_OPEN_FOR_BACKUP_INTENT, "FILE_OPEN_FOR_BACKUP_INTENT",
+ FILE_NO_COMPRESSION, "FILE_NO_COMPRESSION",
+ 0xFFFFFFFF, "%x"
+};
+
+
+ENUM_TYPE_NAMES FileIoStatusInfoNames[] = {
+ FILE_SUPERSEDED, "FILE_SUPERSEDED",
+ FILE_OPENED, "FILE_OPENED",
+ FILE_CREATED, "FILE_CREATED",
+ FILE_OVERWRITTEN, "FILE_OVERWRITTEN",
+ FILE_EXISTS, "FILE_EXISTS",
+ FILE_DOES_NOT_EXIST, "FILE_DOES_NOT_EXIST",
+ 0xFFFFFFFF, "%x"
+};
+
+
+ENUM_TYPE_NAMES SetFileInfoClassNames[] = {
+ FileDirectoryInformation, "FileDirectoryInformation",
+ FileFullDirectoryInformation, "FileFullDirectoryInformation",
+ FileBothDirectoryInformation, "FileBothDirectoryInformation",
+ FileBasicInformation, "FileBasicInformation",
+ FileStandardInformation, "FileStandardInformation",
+ FileInternalInformation, "FileInternalInformation",
+ FileEaInformation, "FileEaInformation",
+ FileAccessInformation, "FileAccessInformation",
+ FileNameInformation, "FileNameInformation",
+ FileRenameInformation, "FileRenameInformation",
+ FileLinkInformation, "FileLinkInformation",
+ FileNamesInformation, "FileNamesInformation",
+ FileDispositionInformation, "FileDispositionInformation",
+ FilePositionInformation, "FilePositionInformation",
+ FileFullEaInformation, "FileFullEaInformation",
+ FileModeInformation, "FileModeInformation",
+ FileAlignmentInformation, "FileAlignmentInformation",
+ FileAllInformation, "FileAllInformation",
+ FileAllocationInformation, "FileAllocationInformation",
+ FileEndOfFileInformation, "FileEndOfFileInformation",
+ FileAlternateNameInformation, "FileAlternateNameInformation",
+ FileStreamInformation, "FileStreamInformation",
+ FilePipeInformation, "FilePipeInformation",
+ FilePipeLocalInformation, "FilePipeLocalInformation",
+ FilePipeRemoteInformation, "FilePipeRemoteInformation",
+ FileMailslotQueryInformation, "FileMailslotQueryInformation",
+ FileMailslotSetInformation, "FileMailslotSetInformation",
+ FileCompressionInformation, "FileCompressionInformation",
+ FileCopyOnWriteInformation, "FileCopyOnWriteInformation",
+ FileCompletionInformation, "FileCompletionInformation",
+ FileMoveClusterInformation, "FileMoveClusterInformation",
+ 0xFFFFFFFF, "%x"
+};
+
+ENUM_TYPE_NAMES KeyCreateOptionNames[] = {
+ REG_OPTION_VOLATILE, "REG_OPTION_VOLATILE",
+ REG_OPTION_CREATE_LINK, "REG_OPTION_CREATE_LINK",
+ REG_OPTION_BACKUP_RESTORE, "REG_OPTION_BACKUP_RESTORE",
+ 0xFFFFFFFF, "%x"
+};
+
+ENUM_TYPE_NAMES KeyDispositionNames[] = {
+ REG_CREATED_NEW_KEY, "REG_CREATED_NEW_KEY",
+ REG_OPENED_EXISTING_KEY, "REG_OPENED_EXISTING_KEY",
+ 0xFFFFFFFF, "%x"
+};
+
+
+#endif // TRACE_ENABLED
+
+BOOLEAN
+IsFileWriteAccessRequested(
+ ACCESS_MASK DesiredAccess,
+ ULONG CreateDisposition
+ )
+{
+ if (DesiredAccess & (GENERIC_WRITE |
+ GENERIC_ALL |
+ DELETE |
+ FILE_WRITE_DATA |
+ FILE_APPEND_DATA
+ )
+ ) {
+ return TRUE;
+ }
+
+ if (CreateDisposition != FILE_OPEN && CreateDisposition != FILE_OPEN_IF) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOLEAN
+NtCreateFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PCREATEFILE_PARAMETERS p = &Parameters->InputParameters.CreateFile;
+ BOOLEAN Result;
+
+ if (!Parameters->ReturnValueValid) {
+ Result = CaptureOpenState( Process,
+ Parameters,
+ p->ObjectAttributes,
+ IsFileWriteAccessRequested( p->DesiredAccess, p->CreateDisposition ),
+ p->FileHandle,
+ HANDLE_TYPE_FILE
+ );
+
+
+ if (Result
+ // && Parameters->SavedCallState.PathOpen.WriteAccessRequested
+ ) {
+ DbgEvent( CREATEEVENT, ( "NtCreateFile called:\n"
+ " Name: %ws\n"
+ " Access: %s\n"
+ " Share: %s\n"
+ " Create: %s\n"
+ " Options:%s\n",
+ Parameters->SavedCallState.FullName,
+ FormatEnumType( 0,
+ FileAccessNames,
+ p->DesiredAccess,
+ TRUE
+ ),
+ FormatEnumType( 1,
+ FileShareNames,
+ p->ShareAccess,
+ TRUE
+ ),
+ FormatEnumType( 2,
+ FileCreateDispositionNames,
+ p->CreateDisposition,
+ FALSE
+ ),
+ FormatEnumType( 3,
+ FileCreateOptionNames,
+ p->CreateOptions,
+ TRUE
+ )
+ )
+ );
+ }
+ }
+ else {
+ Result = CompleteOpenState( Process,
+ Parameters
+ );
+ if (Result
+ // && Parameters->SavedCallState.PathOpen.WriteAccessRequested
+ ) {
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ ReadMemory( Process,
+ p->IoStatusBlock,
+ &IoStatusBlock,
+ sizeof( IoStatusBlock ),
+ "IoStatusBlock"
+ );
+ DbgEvent( CREATEEVENT, ( "*** Returned %s [%s %s]\n",
+ FormatEnumType( 0,
+ (PENUM_TYPE_NAMES)ntstatusSymbolicNames,
+ Parameters->ReturnValue.ReturnedLong,
+ FALSE
+ ),
+ FormatEnumType( 1,
+ (PENUM_TYPE_NAMES)ntstatusSymbolicNames,
+ IoStatusBlock.Status,
+ FALSE
+ ),
+ FormatEnumType( 2,
+ FileIoStatusInfoNames,
+ IoStatusBlock.Information,
+ FALSE
+ )
+ )
+ );
+ }
+ }
+
+ return Result;
+}
+
+BOOLEAN
+NtOpenFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ POPENFILE_PARAMETERS p = &Parameters->InputParameters.OpenFile;
+ BOOLEAN Result;
+
+ if (!Parameters->ReturnValueValid) {
+ Result = CaptureOpenState( Process,
+ Parameters,
+ p->ObjectAttributes,
+ IsFileWriteAccessRequested( p->DesiredAccess, FILE_OPEN ),
+ p->FileHandle,
+ HANDLE_TYPE_FILE
+ );
+ if (Result
+ // && Parameters->SavedCallState.PathOpen.WriteAccessRequested
+ ) {
+ DbgEvent( CREATEEVENT, ( "NtOpenFile called:\n"
+ " Name: %ws\n"
+ " Access: %s\n"
+ " Share: %s\n"
+ " Options:%s\n",
+ Parameters->SavedCallState.FullName,
+ FormatEnumType( 0,
+ FileAccessNames,
+ p->DesiredAccess,
+ TRUE
+ ),
+ FormatEnumType( 1,
+ FileShareNames,
+ p->ShareAccess,
+ TRUE
+ ),
+ FormatEnumType( 2,
+ FileCreateOptionNames,
+ p->OpenOptions,
+ TRUE
+ )
+ )
+ );
+ }
+ }
+ else {
+ Result = CompleteOpenState( Process,
+ Parameters
+ );
+ if (Result
+ // && Parameters->SavedCallState.PathOpen.WriteAccessRequested
+ ) {
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ ReadMemory( Process,
+ p->IoStatusBlock,
+ &IoStatusBlock,
+ sizeof( IoStatusBlock ),
+ "IoStatusBlock"
+ );
+ DbgEvent( CREATEEVENT, ( "*** Returned %s [%s %s]\n",
+ FormatEnumType( 0,
+ (PENUM_TYPE_NAMES)ntstatusSymbolicNames,
+ Parameters->ReturnValue.ReturnedLong,
+ FALSE
+ ),
+ FormatEnumType( 1,
+ (PENUM_TYPE_NAMES)ntstatusSymbolicNames,
+ IoStatusBlock.Status,
+ FALSE
+ ),
+ FormatEnumType( 2,
+ FileIoStatusInfoNames,
+ IoStatusBlock.Information,
+ FALSE
+ )
+ )
+ );
+ }
+ }
+
+ return Result;
+}
+
+BOOLEAN
+NtDeleteFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PDELETEFILE_PARAMETERS p = &Parameters->InputParameters.DeleteFile;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING ObjectName;
+ PWSTR FullName;
+ BOOLEAN Result, CallSuccessful;
+ PFILE_REFERENCE FileReference;
+
+ if (!Parameters->ReturnValueValid) {
+ if (CaptureObjectAttributes( Process,
+ p->ObjectAttributes,
+ &ObjectAttributes,
+ &ObjectName
+ ) &&
+ CaptureFullName( Process,
+ HANDLE_TYPE_FILE,
+ ObjectAttributes.RootDirectory,
+ ObjectName.Buffer,
+ ObjectName.Length,
+ &FullName
+ ) &&
+ CreateSavedCallState( Process,
+ &Parameters->SavedCallState,
+ DeletePath,
+ HANDLE_TYPE_FILE,
+ FullName
+ )
+ ) {
+ Result = CreateFileReference( FullName,
+ TRUE,
+ (PFILE_REFERENCE *)&Parameters->SavedCallState.Reference
+ );
+ }
+ else {
+ Result = FALSE;
+ }
+
+ return Result;
+ }
+ else {
+ if (NT_SUCCESS( Parameters->ReturnValue.ReturnedLong )) {
+ CallSuccessful = TRUE;
+ }
+ else {
+ CallSuccessful = FALSE;
+ }
+
+ FileReference = (PFILE_REFERENCE)Parameters->SavedCallState.Reference;
+ CompleteFileReference( FileReference,
+ CallSuccessful,
+ TRUE,
+ NULL
+ );
+
+ return TRUE;
+ }
+}
+
+#undef DeleteFile
+
+
+BOOLEAN
+NtSetInformationFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PSETINFORMATIONFILE_PARAMETERS p = &Parameters->InputParameters.SetInformationFile;
+ POPENHANDLE_INFO HandleInfo;
+ FILE_RENAME_INFORMATION RenameInformation;
+ FILE_DISPOSITION_INFORMATION DispositionInformation;
+ PWSTR NewName, OldName;
+ PFILE_REFERENCE OldFileReference;
+ PFILE_REFERENCE NewFileReference;
+ BOOLEAN Result, CallSuccessful;
+
+ HandleInfo = FindOpenHandle( Process,
+ p->FileHandle,
+ HANDLE_TYPE_FILE
+ );
+ if (HandleInfo == NULL) {
+ Result = FALSE;
+ }
+ else
+ if (p->FileInformationClass == FileRenameInformation) {
+ //
+ // Renaming an open file.
+ //
+ if (!Parameters->ReturnValueValid) {
+ if (ReadMemory( Process,
+ p->FileInformation,
+ &RenameInformation,
+ FIELD_OFFSET( FILE_RENAME_INFORMATION, FileName ),
+ "rename information"
+ ) &&
+ CaptureFullName( Process,
+ HANDLE_TYPE_FILE,
+ RenameInformation.RootDirectory,
+ &((PFILE_RENAME_INFORMATION)(p->FileInformation))->FileName[ 0 ],
+ RenameInformation.FileNameLength,
+ &NewName
+ ) &&
+ CreateSavedCallState( Process,
+ &Parameters->SavedCallState,
+ RenamePath,
+ HANDLE_TYPE_FILE,
+ HandleInfo->Name,
+ NewName,
+ RenameInformation.ReplaceIfExists
+ )
+ ) {
+ Result = CreateFileReference( NewName,
+ RenameInformation.ReplaceIfExists,
+ (PFILE_REFERENCE *)&Parameters->SavedCallState.Reference
+ );
+ }
+ else {
+ Result = FALSE;
+ }
+ }
+ else {
+ OldName = NULL;
+ if (NT_SUCCESS( Parameters->ReturnValue.ReturnedLong )) {
+ CallSuccessful = TRUE;
+ OldFileReference = FindFileReference( HandleInfo->Name );
+ if (OldFileReference) {
+ OldName = OldFileReference->Name;
+ }
+ }
+ else {
+ CallSuccessful = FALSE;
+ OldFileReference = NULL;
+ }
+
+ NewFileReference = (PFILE_REFERENCE)Parameters->SavedCallState.Reference;
+ NewName = NewFileReference->Name;
+ CompleteFileReference( NewFileReference,
+ CallSuccessful,
+ (BOOLEAN)(!NewFileReference->Created),
+ OldFileReference
+ );
+ Result = TRUE;
+ }
+ }
+ else
+ if (p->FileInformationClass == FileDispositionInformation) {
+ //
+ // Marking an open file for delete.
+ //
+ if (!Parameters->ReturnValueValid) {
+ if (ReadMemory( Process,
+ p->FileInformation,
+ &DispositionInformation,
+ sizeof( DispositionInformation ),
+ "disposition information"
+ ) &&
+ DispositionInformation.DeleteFile &&
+ CreateSavedCallState( Process,
+ &Parameters->SavedCallState,
+ DeletePath,
+ HANDLE_TYPE_FILE,
+ HandleInfo->Name
+ )
+ ) {
+ Result = TRUE;
+ }
+ else {
+ Result = FALSE;
+ }
+ }
+ else {
+ if (NT_SUCCESS( Parameters->ReturnValue.ReturnedLong )) {
+ if (OldFileReference = FindFileReference( HandleInfo->Name )) {
+ if (OldFileReference->Created) {
+ LogEvent( INSTALER_EVENT_DELETE_TEMP_FILE,
+ 2,
+ OldFileReference->DirectoryFile ? L"directory" : L"file",
+ OldFileReference->Name
+ );
+ DestroyFileReference( OldFileReference );
+ }
+ else {
+ OldFileReference->Deleted = TRUE;
+ LogEvent( INSTALER_EVENT_DELETE_FILE,
+ 2,
+ OldFileReference->DirectoryFile ? L"directory" : L"file",
+ OldFileReference->Name
+ );
+ }
+ }
+ }
+
+ Result = TRUE;
+ }
+ }
+ else {
+ Result = FALSE;
+ }
+
+ return Result;
+}
+
+
+
+
+BOOLEAN
+CaptureUnicodeString(
+ PPROCESS_INFO Process,
+ PUNICODE_STRING UnicodeStringAddress,
+ PWSTR *ReturnedString
+ )
+{
+ UNICODE_STRING UnicodeString;
+ PWSTR s;
+
+ s = NULL;
+ if (ReadMemory( Process,
+ UnicodeStringAddress,
+ &UnicodeString,
+ sizeof( UnicodeString ),
+ "unicode string"
+ ) &&
+ ((s = AllocMem( UnicodeString.Length + sizeof( UNICODE_NULL ) )) != NULL) &&
+ ReadMemory( Process,
+ UnicodeString.Buffer,
+ s,
+ UnicodeString.Length,
+ "unicode string buffer"
+ )
+ ) {
+ *ReturnedString = s;
+ return TRUE;
+ }
+
+ FreeMem( &s );
+ return FALSE;
+}
+
+
+BOOLEAN
+NtQueryAttributesFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PQUERYATTRIBUTESFILE_PARAMETERS p = &Parameters->InputParameters.QueryAttributesFile;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING ObjectName;
+ PWSTR FullName;
+ NTSTATUS Status;
+ BOOLEAN Result;
+ CHAR Buffer[ MAX_PATH ];
+
+ if (!Parameters->ReturnValueValid) {
+ FullName = NULL;
+ Result = FALSE;
+ if (CaptureObjectAttributes( Process,
+ p->ObjectAttributes,
+ &ObjectAttributes,
+ &ObjectName
+ ) &&
+ CaptureFullName( Process,
+ HANDLE_TYPE_FILE,
+ ObjectAttributes.RootDirectory,
+ ObjectName.Buffer,
+ ObjectName.Length,
+ &FullName
+ ) &&
+ CreateSavedCallState( Process,
+ &Parameters->SavedCallState,
+ QueryPath,
+ HANDLE_TYPE_FILE,
+ FullName
+ )
+ ) {
+ Result = CreateFileReference( FullName,
+ FALSE,
+ (PFILE_REFERENCE *)&Parameters->SavedCallState.Reference
+ );
+ }
+ else {
+ Result = FALSE;
+ }
+
+ return Result;
+ }
+ else {
+ FILE_BASIC_INFORMATION FileInformation;
+
+ ReadMemory( Process,
+ p->FileInformation,
+ &FileInformation,
+ sizeof( FileInformation ),
+ "FileInformation"
+ );
+ return TRUE;
+ }
+}
+
+BOOLEAN
+NtQueryDirectoryFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PQUERYDIRECTORYFILE_PARAMETERS p = &Parameters->InputParameters.QueryDirectoryFile;
+ POPENHANDLE_INFO HandleInfo;
+ PWSTR FileName;
+ NTSTATUS Status;
+ BOOLEAN Result;
+ PULONG pNextEntryOffset;
+ ULONG NextEntryOffset;
+ ULONG EntriesReturned;
+
+ if (!Parameters->ReturnValueValid) {
+ FileName = NULL;
+ Result = FALSE;
+ if ((HandleInfo = FindOpenHandle( Process,
+ p->FileHandle,
+ HANDLE_TYPE_FILE
+ )
+ ) != NULL &&
+ p->FileName != NULL &&
+ CaptureUnicodeString( Process,
+ p->FileName,
+ &FileName
+ )
+ ) {
+ if (HandleInfo->RootDirectory &&
+ !wcscmp( FileName, L"*" ) || !wcscmp( FileName, L"*.*" )
+ ) {
+ if (AskUserOnce) {
+ AskUserOnce = FALSE;
+ if (AskUser( MB_OKCANCEL,
+ INSTALER_ASKUSER_ROOTSCAN,
+ 1,
+ HandleInfo->Name
+ ) == IDCANCEL
+ ) {
+ FailAllScansOfRootDirectories = TRUE;
+ }
+ }
+
+ Parameters->AbortCall = FailAllScansOfRootDirectories;
+ }
+
+ if (FileName) {
+ HandleInfo->QueryName = FileName;
+ }
+ }
+
+ return TRUE;
+ }
+ else {
+ if (Parameters->AbortCall) {
+ //
+ // If we get here, then user wanted to fail this call.
+ //
+ Status = STATUS_NO_MORE_FILES;
+ SetProcedureReturnValue( Process,
+ Thread,
+ &Status,
+ sizeof( Status )
+ );
+ return TRUE;
+ }
+ else {
+ if ((HandleInfo = FindOpenHandle( Process,
+ p->FileHandle,
+ HANDLE_TYPE_FILE
+ )
+ ) != NULL
+ ) {
+ //
+ // If successful, count entries returned
+ //
+ if (NT_SUCCESS( Parameters->ReturnValue.ReturnedLong )) {
+ //
+ // Return buffer is a set of records, the first DWORD of each contains
+ // the offset to the next one or zero to indicate the end. Count them.
+ //
+ pNextEntryOffset = (PULONG)p->FileInformation;
+ EntriesReturned = 1;
+ while (ReadMemory( Process,
+ pNextEntryOffset,
+ &NextEntryOffset,
+ sizeof( NextEntryOffset ),
+ "DirectoryInformation"
+ ) &&
+ NextEntryOffset != 0
+ ) {
+ pNextEntryOffset = (PULONG)((PCHAR)pNextEntryOffset + NextEntryOffset);
+ EntriesReturned += 1;
+ }
+
+ LogEvent( INSTALER_EVENT_SCAN_DIRECTORY,
+ 3,
+ HandleInfo->Name,
+ HandleInfo->QueryName ? HandleInfo->QueryName : L"*",
+ EntriesReturned
+ );
+
+ }
+ else {
+ FreeMem( &HandleInfo->QueryName );
+ }
+ }
+ }
+
+ return TRUE;
+ }
+}
+
+
+BOOLEAN
+IsKeyWriteAccessRequested(
+ ACCESS_MASK DesiredAccess
+ )
+{
+ if (DesiredAccess & (GENERIC_WRITE |
+ GENERIC_ALL |
+ DELETE |
+ KEY_SET_VALUE |
+ KEY_CREATE_SUB_KEY |
+ KEY_CREATE_LINK
+ )
+ ) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOLEAN
+NtCreateKeyHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PCREATEKEY_PARAMETERS p = &Parameters->InputParameters.CreateKey;
+ BOOLEAN Result;
+
+ if (!Parameters->ReturnValueValid) {
+ Result = CaptureOpenState( Process,
+ Parameters,
+ p->ObjectAttributes,
+ IsKeyWriteAccessRequested( p->DesiredAccess ),
+ p->KeyHandle,
+ HANDLE_TYPE_KEY
+ );
+ }
+ else {
+ Result = CompleteOpenState( Process,
+ Parameters
+ );
+ }
+
+ return Result;
+}
+
+BOOLEAN
+NtOpenKeyHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ POPENKEY_PARAMETERS p = &Parameters->InputParameters.OpenKey;
+ BOOLEAN Result;
+
+ if (!Parameters->ReturnValueValid) {
+ Result = CaptureOpenState( Process,
+ Parameters,
+ p->ObjectAttributes,
+ IsKeyWriteAccessRequested( p->DesiredAccess ),
+ p->KeyHandle,
+ HANDLE_TYPE_KEY
+ );
+ }
+ else {
+ Result = CompleteOpenState( Process,
+ Parameters
+ );
+ }
+
+ return Result;
+}
+
+BOOLEAN
+NtDeleteKeyHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PDELETEKEY_PARAMETERS p = &Parameters->InputParameters.DeleteKey;
+ POPENHANDLE_INFO HandleInfo;
+ PKEY_REFERENCE OldKeyReference;
+ BOOLEAN Result;
+
+ //
+ // Marking an open key for delete.
+ //
+ HandleInfo = FindOpenHandle( Process,
+ p->KeyHandle,
+ HANDLE_TYPE_KEY
+ );
+ if (HandleInfo == NULL) {
+ Result = FALSE;
+ }
+ else
+ if (!Parameters->ReturnValueValid) {
+ if (CreateSavedCallState( Process,
+ &Parameters->SavedCallState,
+ DeletePath,
+ HANDLE_TYPE_KEY,
+ HandleInfo->Name
+ )
+ ) {
+ Result = TRUE;
+ }
+ else {
+ Result = FALSE;
+ }
+ }
+ else {
+ if (NT_SUCCESS( Parameters->ReturnValue.ReturnedLong )) {
+ if (OldKeyReference = FindKeyReference( HandleInfo->Name )) {
+ if (OldKeyReference->Created) {
+ LogEvent( INSTALER_EVENT_DELETE_TEMP_KEY,
+ 1,
+ OldKeyReference->Name
+ );
+ DestroyKeyReference( OldKeyReference );
+ }
+ else {
+ MarkKeyDeleted( OldKeyReference );
+ LogEvent( INSTALER_EVENT_DELETE_KEY,
+ 1,
+ OldKeyReference->Name
+ );
+ }
+ }
+ }
+
+ Result = TRUE;
+ }
+
+ return Result;
+}
+
+BOOLEAN
+CaptureValueName(
+ PPROCESS_INFO Process,
+ PUNICODE_STRING Name,
+ PWSTR *ReturnedValueName
+ )
+{
+ UNICODE_STRING ValueName;
+ PWSTR s;
+
+ *ReturnedValueName = NULL;
+ if (Name == NULL ||
+ !ReadMemory( Process,
+ Name,
+ &ValueName,
+ sizeof( ValueName ),
+ "value name string"
+ )
+ ) {
+ return FALSE;
+ }
+
+ s = AllocMem( ValueName.Length + sizeof( UNICODE_NULL ) );
+ if (s == NULL) {
+ return FALSE;
+ }
+
+ if (!ReadMemory( Process,
+ ValueName.Buffer,
+ s,
+ ValueName.Length,
+ "value name buffer"
+ )
+ ) {
+ FreeMem( &s );
+ return FALSE;
+ }
+
+ s[ ValueName.Length / sizeof( WCHAR ) ] = UNICODE_NULL;
+ ValueName.Buffer = s;
+ ValueName.MaximumLength = (USHORT)(ValueName.Length + sizeof( UNICODE_NULL ));
+ *ReturnedValueName = AddName( &ValueName );
+ FreeMem( &ValueName.Buffer );
+ return TRUE;
+}
+
+
+BOOLEAN
+NtSetValueKeyHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PSETVALUEKEY_PARAMETERS p = &Parameters->InputParameters.SetValueKey;
+ POPENHANDLE_INFO HandleInfo;
+ PWSTR ValueName;
+ PKEY_REFERENCE KeyReference;
+ PVALUE_REFERENCE ValueReference;
+ BOOLEAN Result;
+
+ //
+ // Setting a value
+ //
+ HandleInfo = FindOpenHandle( Process,
+ p->KeyHandle,
+ HANDLE_TYPE_KEY
+ );
+ if (HandleInfo == NULL) {
+ Result = FALSE;
+ }
+ else
+ if (!Parameters->ReturnValueValid) {
+ if (CaptureValueName( Process,
+ p->ValueName,
+ &ValueName
+ ) &&
+ CreateSavedCallState( Process,
+ &Parameters->SavedCallState,
+ SetValue,
+ HANDLE_TYPE_KEY,
+ ValueName
+ )
+ ) {
+ Result = TRUE;
+ }
+ else {
+ Result = FALSE;
+ }
+ }
+ else {
+ if (NT_SUCCESS( Parameters->ReturnValue.ReturnedLong ) &&
+ (KeyReference = FindKeyReference( HandleInfo->Name ))
+ ) {
+ CreateValueReference( Process,
+ KeyReference,
+ Parameters->SavedCallState.FullName,
+ p->TitleIndex,
+ p->Type,
+ p->Data,
+ p->DataLength,
+ &ValueReference
+ );
+
+ if (ValueReference != NULL) {
+ LogEvent( INSTALER_EVENT_SET_KEY_VALUE,
+ 2,
+ ValueReference->Name,
+ KeyReference->Name
+ );
+ }
+ }
+
+ Result = TRUE;
+ }
+
+ return Result;
+}
+
+BOOLEAN
+NtDeleteValueKeyHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PDELETEVALUEKEY_PARAMETERS p = &Parameters->InputParameters.DeleteValueKey;
+ POPENHANDLE_INFO HandleInfo;
+ PWSTR ValueName;
+ PKEY_REFERENCE KeyReference;
+ PVALUE_REFERENCE ValueReference;
+ BOOLEAN Result;
+
+ //
+ // Marking a value for delete.
+ //
+ HandleInfo = FindOpenHandle( Process,
+ p->KeyHandle,
+ HANDLE_TYPE_KEY
+ );
+ if (HandleInfo == NULL) {
+ Result = FALSE;
+ }
+ else
+ if (!Parameters->ReturnValueValid) {
+ if (CaptureValueName( Process,
+ p->ValueName,
+ &ValueName
+ ) &&
+ CreateSavedCallState( Process,
+ &Parameters->SavedCallState,
+ DeleteValue,
+ HANDLE_TYPE_KEY,
+ ValueName
+ )
+ ) {
+ Result = TRUE;
+ }
+ else {
+ Result = FALSE;
+ }
+ }
+ else {
+ if (NT_SUCCESS( Parameters->ReturnValue.ReturnedLong ) &&
+ (KeyReference = FindKeyReference( HandleInfo->Name )) &&
+ (ValueReference = FindValueReference( KeyReference, Parameters->SavedCallState.FullName ))
+ ) {
+ if (ValueReference->Created) {
+ LogEvent( INSTALER_EVENT_DELETE_KEY_TEMP_VALUE,
+ 2,
+ ValueReference->Name,
+ KeyReference->Name
+ );
+ DestroyValueReference( ValueReference );
+ }
+ else {
+ ValueReference->Deleted = TRUE;
+ LogEvent( INSTALER_EVENT_DELETE_KEY_VALUE,
+ 2,
+ ValueReference->Name,
+ KeyReference->Name
+ );
+ }
+ }
+
+ Result = TRUE;
+ }
+
+ return Result;
+}
+
+
+BOOLEAN
+NtCloseHandleHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PCLOSEHANDLE_PARAMETERS p = &Parameters->InputParameters.CloseHandle;
+ POPENHANDLE_INFO HandleInfo;
+ PFILE_REFERENCE FileReference;
+
+ HandleInfo = FindOpenHandle( Process, p->Handle, (ULONG)-1 );
+ if (!Parameters->ReturnValueValid) {
+ if (HandleInfo != NULL) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+ }
+ else {
+ if (NT_SUCCESS( Parameters->ReturnValue.ReturnedLong )) {
+ if (HandleInfo != NULL &&
+ HandleInfo->Type == HANDLE_TYPE_FILE &&
+ (FileReference = FindFileReference( HandleInfo->Name )) != NULL &&
+ FileReference->WriteAccess &&
+ !FileReference->Created &&
+ !FileReference->DirectoryFile &&
+ IsNewFileSameAsBackup( FileReference )
+ ) {
+ DestroyFileReference( FileReference );
+ FileReference = NULL;
+ }
+
+ DeleteOpenHandle( Process, p->Handle, (ULONG)-1 );
+ }
+
+ return TRUE;
+ }
+}
+
+UCHAR AnsiStringBuffer[ MAX_PATH+1 ];
+WCHAR UnicodeStringBuffer[ MAX_PATH+1 ];
+
+BOOLEAN
+CaptureAnsiAsUnicode(
+ PPROCESS_INFO Process,
+ LPCSTR Address,
+ BOOLEAN DoubleNullTermination,
+ PWSTR *ReturnedName,
+ PWSTR *ReturnedData
+ )
+{
+ NTSTATUS Status;
+ ULONG BytesRead;
+ ANSI_STRING AnsiString;
+ UNICODE_STRING UnicodeString;
+
+ if (Address == NULL) {
+ if (ReturnedName != NULL) {
+ *ReturnedName = NULL;
+ }
+ else {
+ *ReturnedData = NULL;
+ }
+ return TRUE;
+ }
+
+ BytesRead = FillTemporaryBuffer( Process,
+ (PVOID)Address,
+ FALSE,
+ DoubleNullTermination
+ );
+ if (BytesRead != 0 && BytesRead < 0x7FFF) {
+ AnsiString.Buffer = TemporaryBuffer;
+ AnsiString.Length = (USHORT)BytesRead;
+ AnsiString.MaximumLength = (USHORT)BytesRead;
+ Status = RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, TRUE );
+ if (NT_SUCCESS( Status )) {
+ if (ReturnedName != NULL) {
+ *ReturnedName = AddName( &UnicodeString );
+ RtlFreeUnicodeString( &UnicodeString );
+ return TRUE;
+ }
+ else {
+ *ReturnedData = UnicodeString.Buffer;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+BOOLEAN
+CaptureUnicode(
+ PPROCESS_INFO Process,
+ LPCWSTR Address,
+ BOOLEAN DoubleNullTermination,
+ PWSTR *ReturnedName,
+ PWSTR *ReturnedData
+ )
+{
+ ULONG BytesRead;
+ UNICODE_STRING UnicodeString;
+
+ if (Address == NULL) {
+ if (ReturnedName != NULL) {
+ *ReturnedName = NULL;
+ }
+ else {
+ *ReturnedData = NULL;
+ }
+ return TRUE;
+ }
+
+ BytesRead = FillTemporaryBuffer( Process,
+ (PVOID)Address,
+ TRUE,
+ DoubleNullTermination
+ );
+ if (BytesRead != 0 && (BytesRead & 1) == 0 && BytesRead <= 0xFFFC) {
+ if (ReturnedName != NULL) {
+ RtlInitUnicodeString( &UnicodeString, TemporaryBuffer );
+ *ReturnedName = AddName( &UnicodeString );
+ return TRUE;
+ }
+ else {
+ *ReturnedData = AllocMem( BytesRead + sizeof( UNICODE_NULL ) );
+ if (*ReturnedData != NULL) {
+ memmove( *ReturnedData, TemporaryBuffer, BytesRead );
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+BOOLEAN
+GetVersionHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ DWORD dwVersion;
+
+ if (!Parameters->ReturnValueValid) {
+ if (AskUserOnce) {
+ AskUserOnce = FALSE;
+ if (AskUser( MB_OKCANCEL,
+ INSTALER_ASKUSER_GETVERSION,
+ 0
+ ) == IDCANCEL
+ ) {
+ DefaultGetVersionToWin95 = TRUE;
+ }
+ else {
+ DefaultGetVersionToWin95 = FALSE;
+ }
+ }
+
+ LogEvent( INSTALER_EVENT_GETVERSION,
+ 1,
+ DefaultGetVersionToWin95 ? L"Windows 95" : L"Windows NT"
+ );
+ Parameters->AbortCall = DefaultGetVersionToWin95;
+ return Parameters->AbortCall;
+ }
+ else {
+ dwVersion = 0xC0000004; // What Windows 95 returns
+ SetProcedureReturnValue( Process,
+ Thread,
+ &dwVersion,
+ sizeof( dwVersion )
+ );
+ return TRUE;
+ }
+}
+
+BOOLEAN
+GetVersionExWHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PGETVERSIONEXW_PARAMETERS p = &Parameters->InputParameters.GetVersionExW;
+ OSVERSIONINFOW VersionInformation;
+
+ if (!Parameters->ReturnValueValid) {
+ if (AskUserOnce) {
+ AskUserOnce = FALSE;
+ if (AskUser( MB_OKCANCEL,
+ INSTALER_ASKUSER_GETVERSION,
+ 0
+ ) == IDCANCEL
+ ) {
+ DefaultGetVersionToWin95 = TRUE;
+ }
+ else {
+ DefaultGetVersionToWin95 = FALSE;
+ }
+ }
+
+ Parameters->AbortCall = DefaultGetVersionToWin95;
+ return Parameters->AbortCall;
+ }
+ else {
+ memset( &VersionInformation, 0, sizeof( VersionInformation ) );
+ VersionInformation.dwMajorVersion = 4;
+ VersionInformation.dwBuildNumber = 0x3B6; // What Windows 95 returns
+ VersionInformation.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;
+ WriteMemory( Process,
+ p->lpVersionInformation,
+ &VersionInformation,
+ sizeof( VersionInformation ),
+ "GetVersionExW"
+ );
+
+ return TRUE;
+ }
+}
+
+BOOLEAN
+SetCurrentDirectoryAHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PSETCURRENTDIRECTORYA_PARAMETERS p = &Parameters->InputParameters.SetCurrentDirectoryA;
+ PWSTR PathName;
+ WCHAR PathBuffer[ MAX_PATH ];
+
+ if (!Parameters->ReturnValueValid) {
+ if (CaptureAnsiAsUnicode( Process, p->lpPathName, FALSE, NULL, &PathName )) {
+ Parameters->CurrentDirectory = AllocMem( (wcslen( PathName ) + 1) * sizeof( WCHAR ) );
+ if (Parameters->CurrentDirectory != NULL) {
+ wcscpy( Parameters->CurrentDirectory, PathName );
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+ }
+ else {
+ if (Parameters->ReturnValue.ReturnedBool != 0) {
+ if (SetCurrentDirectory( Parameters->CurrentDirectory ) &&
+ GetCurrentDirectory( MAX_PATH, PathBuffer )
+ ) {
+ LogEvent( INSTALER_EVENT_SET_DIRECTORY,
+ 1,
+ PathBuffer
+ );
+ }
+ }
+
+ FreeMem( &Parameters->CurrentDirectory );
+ return TRUE;
+ }
+}
+
+BOOLEAN
+SetCurrentDirectoryWHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PSETCURRENTDIRECTORYW_PARAMETERS p = &Parameters->InputParameters.SetCurrentDirectoryW;
+ PWSTR PathName;
+ WCHAR PathBuffer[ MAX_PATH ];
+
+ if (!Parameters->ReturnValueValid) {
+ if (CaptureUnicode( Process, p->lpPathName, FALSE, NULL, &PathName )) {
+ Parameters->CurrentDirectory = AllocMem( (wcslen( PathName ) + 1) * sizeof( WCHAR ) );
+ if (Parameters->CurrentDirectory != NULL) {
+ wcscpy( Parameters->CurrentDirectory, PathName );
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+ }
+ else {
+ if (Parameters->ReturnValue.ReturnedBool != 0) {
+ if (SetCurrentDirectory( Parameters->CurrentDirectory ) &&
+ GetCurrentDirectory( MAX_PATH, PathBuffer )
+ ) {
+ LogEvent( INSTALER_EVENT_SET_DIRECTORY,
+ 1,
+ PathBuffer
+ );
+ }
+ }
+
+ FreeMem( &Parameters->CurrentDirectory );
+ return TRUE;
+ }
+}
+
+BOOLEAN
+GetIniFilePath(
+ PWSTR IniFileName,
+ PWSTR *ReturnedIniFilePath
+ )
+{
+ NTSTATUS Status;
+ UNICODE_STRING FileName, UnicodeString;
+ PWSTR FilePart;
+ DWORD n;
+
+ if (IniFileName == NULL) {
+ RtlInitUnicodeString( &FileName, L"win.ini" );
+ }
+ else {
+ RtlInitUnicodeString( &FileName, IniFileName );
+ }
+
+ if ((FileName.Length > sizeof( WCHAR ) &&
+ FileName.Buffer[ 1 ] == L':'
+ ) ||
+ (FileName.Length != 0 &&
+ wcscspn( FileName.Buffer, L"\\/" ) != (FileName.Length / sizeof( WCHAR ))
+ )
+ ) {
+ UnicodeString.MaximumLength = (USHORT)(MAX_PATH * sizeof( WCHAR ));
+ UnicodeString.Buffer = AllocMem( UnicodeString.MaximumLength );
+ if (UnicodeString.Buffer == NULL) {
+ Status = STATUS_NO_MEMORY;
+ }
+ else {
+ UnicodeString.Length = 0;
+ n = GetFullPathNameW( FileName.Buffer,
+ UnicodeString.MaximumLength / sizeof( WCHAR ),
+ UnicodeString.Buffer,
+ &FilePart
+ );
+ if (n > UnicodeString.MaximumLength) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ else {
+ UnicodeString.Length = (USHORT)(n * sizeof( WCHAR ));
+ Status = STATUS_SUCCESS;
+ }
+ }
+ }
+ else {
+ UnicodeString.Length = 0;
+ UnicodeString.MaximumLength = (USHORT)(WindowsDirectory.Length +
+ sizeof( WCHAR ) +
+ FileName.Length +
+ sizeof( UNICODE_NULL )
+ );
+ UnicodeString.Buffer = AllocMem( UnicodeString.MaximumLength );
+ if (UnicodeString.Buffer == NULL) {
+ Status = STATUS_NO_MEMORY;
+ }
+ else {
+ RtlCopyUnicodeString( &UnicodeString, &WindowsDirectory );
+ Status = RtlAppendUnicodeToString( &UnicodeString,
+ L"\\"
+ );
+ if (NT_SUCCESS( Status )) {
+ Status = RtlAppendUnicodeStringToString( &UnicodeString,
+ &FileName
+ );
+ }
+ }
+ }
+
+ if (IniFileName != NULL) {
+ FreeMem( &IniFileName );
+ }
+
+ if (NT_SUCCESS( Status )) {
+ *ReturnedIniFilePath = AddName( &UnicodeString );
+ FreeMem( &UnicodeString.Buffer );
+ return TRUE;
+ }
+ else {
+ FreeMem( &UnicodeString.Buffer );
+ return FALSE;
+ }
+}
+
+BOOLEAN
+WritePrivateProfileStringEntry(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters,
+ PWSTR IniFilePath,
+ PWSTR SectionName,
+ PWSTR VariableName,
+ PWSTR VariableValue
+ )
+{
+ if (CreateSavedCallState( Process,
+ &Parameters->SavedCallState,
+ WriteIniValue,
+ HANDLE_TYPE_NONE,
+ IniFilePath,
+ SectionName,
+ VariableName,
+ VariableValue
+ ) &&
+ CreateIniFileReference( IniFilePath,
+ (PINI_FILE_REFERENCE *)&Parameters->SavedCallState.Reference
+ )
+ ) {
+ return TRUE;
+ }
+ else {
+ FreeMem( &VariableValue );
+ return FALSE;
+ }
+}
+
+
+BOOLEAN
+WritePrivateProfileStringExit(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PAPI_SAVED_CALL_STATE CallState;
+ PINI_FILE_REFERENCE IniFileReference;
+ PINI_SECTION_REFERENCE IniSectionReference;
+ PINI_VARIABLE_REFERENCE IniVariableReference;
+
+ if (Parameters->ReturnValue.ReturnedLong != 0) {
+ CallState = &Parameters->SavedCallState;
+ if (CallState->FullName == NULL &&
+ CallState->SetIniValue.SectionName == NULL &&
+ CallState->SetIniValue.VariableName == NULL
+ ) {
+ //
+ // Ignore calls to flush INI cache
+ //
+
+ return FALSE;
+ }
+
+ IniFileReference = FindIniFileReference( CallState->FullName );
+ IniSectionReference = FindIniSectionReference( IniFileReference,
+ CallState->SetIniValue.SectionName,
+ (BOOLEAN)(CallState->SetIniValue.VariableName != NULL)
+ );
+ if (IniSectionReference != NULL) {
+ if (CallState->SetIniValue.VariableName == NULL) {
+ IniSectionReference->Deleted = TRUE;
+ LogEvent( INSTALER_EVENT_INI_DELETE_SECTION,
+ 2,
+ CallState->FullName,
+ CallState->SetIniValue.SectionName
+ );
+ }
+ else {
+ IniVariableReference = FindIniVariableReference( IniSectionReference,
+ CallState->SetIniValue.VariableName,
+ (BOOLEAN)(CallState->SetIniValue.VariableValue != NULL)
+ );
+ if (IniVariableReference != NULL) {
+ if (CallState->SetIniValue.VariableValue != NULL) {
+ FreeMem( &IniVariableReference->Value );
+ IniVariableReference->Value = CallState->SetIniValue.VariableValue;
+ CallState->SetIniValue.VariableValue = NULL;
+ if (!IniVariableReference->Created) {
+ if (!wcscmp( IniVariableReference->Value,
+ IniVariableReference->OriginalValue
+ )
+ ) {
+ FreeMem( &IniVariableReference->Value );
+ }
+ else {
+ IniVariableReference->Modified = TRUE;
+ LogEvent( INSTALER_EVENT_INI_CHANGE,
+ 5,
+ CallState->FullName,
+ CallState->SetIniValue.SectionName,
+ CallState->SetIniValue.VariableName,
+ IniVariableReference->Value,
+ IniVariableReference->OriginalValue
+ );
+ }
+ }
+ else {
+ LogEvent( INSTALER_EVENT_INI_CREATE,
+ 4,
+ CallState->FullName,
+ CallState->SetIniValue.SectionName,
+ CallState->SetIniValue.VariableName,
+ IniVariableReference->Value
+ );
+ }
+ }
+ else
+ if (!IniVariableReference->Created) {
+ IniVariableReference->Deleted = TRUE;
+ LogEvent( INSTALER_EVENT_INI_DELETE,
+ 4,
+ CallState->FullName,
+ CallState->SetIniValue.SectionName,
+ CallState->SetIniValue.VariableName,
+ IniVariableReference->OriginalValue
+ );
+ }
+ else {
+ DestroyIniVariableReference( IniVariableReference );
+ }
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+WritePrivateProfileStringAHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PWRITEPRIVATEPROFILESTRINGA_PARAMETERS p = &Parameters->InputParameters.WritePrivateProfileStringA;
+ PWSTR SectionName, VariableName, FileName, VariableValue;
+ PWSTR IniFilePath;
+
+ if (!Parameters->ReturnValueValid) {
+ FileName = NULL;
+ VariableValue = NULL;
+ if (CaptureAnsiAsUnicode( Process, p->lpFileName, FALSE, NULL, &FileName ) &&
+ GetIniFilePath( FileName, &IniFilePath ) &&
+ CaptureAnsiAsUnicode( Process, p->lpAppName, FALSE, &SectionName, NULL ) &&
+ CaptureAnsiAsUnicode( Process, p->lpKeyName, FALSE, &VariableName, NULL ) &&
+ CaptureAnsiAsUnicode( Process, p->lpString, FALSE, NULL, &VariableValue )
+ ) {
+ if (FileName != NULL || SectionName != NULL) {
+ return WritePrivateProfileStringEntry( Process,
+ Thread,
+ Parameters,
+ IniFilePath,
+ SectionName,
+ VariableName,
+ VariableValue
+ );
+ }
+ }
+
+ return TRUE;
+ }
+ else {
+ return WritePrivateProfileStringExit( Process,
+ Thread,
+ Parameters
+ );
+ }
+}
+
+BOOLEAN
+WritePrivateProfileStringWHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PWRITEPRIVATEPROFILESTRINGW_PARAMETERS p = &Parameters->InputParameters.WritePrivateProfileStringW;
+ PWSTR SectionName, VariableName, FileName, VariableValue;
+ PWSTR IniFilePath;
+
+ if (!Parameters->ReturnValueValid) {
+ FileName = NULL;
+ IniFilePath = NULL;
+ SectionName = NULL;
+ VariableName = NULL;
+ VariableValue = NULL;
+ if (CaptureUnicode( Process, p->lpFileName, FALSE, NULL, &FileName ) &&
+ GetIniFilePath( FileName, &IniFilePath ) &&
+ CaptureUnicode( Process, p->lpAppName, FALSE, &SectionName, NULL ) &&
+ CaptureUnicode( Process, p->lpKeyName, FALSE, &VariableName, NULL ) &&
+ CaptureUnicode( Process, p->lpString, FALSE, NULL, &VariableValue )
+ ) {
+ if (FileName != NULL || SectionName != NULL) {
+ return WritePrivateProfileStringEntry( Process,
+ Thread,
+ Parameters,
+ IniFilePath,
+ SectionName,
+ VariableName,
+ VariableValue
+ );
+ }
+ }
+
+ return TRUE;
+ }
+ else {
+ return WritePrivateProfileStringExit( Process,
+ Thread,
+ Parameters
+ );
+ }
+}
+
+
+BOOLEAN
+WritePrivateProfileSectionEntry(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters,
+ PWSTR IniFilePath,
+ PWSTR SectionName,
+ PWSTR SectionValue
+ )
+{
+ if (CreateSavedCallState( Process,
+ &Parameters->SavedCallState,
+ WriteIniSection,
+ HANDLE_TYPE_NONE,
+ IniFilePath,
+ SectionName,
+ SectionValue
+ ) &&
+ CreateIniFileReference( IniFilePath,
+ (PINI_FILE_REFERENCE *)&Parameters->SavedCallState.Reference
+ )
+ ) {
+ return TRUE;
+ }
+ else {
+ FreeMem( &SectionValue );
+ return FALSE;
+ }
+}
+
+
+BOOLEAN
+WritePrivateProfileSectionExit(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PAPI_SAVED_CALL_STATE CallState;
+ PINI_FILE_REFERENCE IniFileReference;
+ PINI_SECTION_REFERENCE IniSectionReference;
+ PINI_VARIABLE_REFERENCE IniVariableReference;
+ PWSTR VariableName, VariableValue;
+ UNICODE_STRING UnicodeString;
+
+ if (Parameters->ReturnValue.ReturnedLong != 0) {
+ CallState = &Parameters->SavedCallState;
+ IniFileReference = FindIniFileReference( CallState->FullName );
+ IniSectionReference = FindIniSectionReference( IniFileReference,
+ CallState->SetIniSection.SectionName,
+ (BOOLEAN)(CallState->SetIniSection.SectionValue != NULL)
+ );
+ if (IniSectionReference != NULL) {
+ if (CallState->SetIniSection.SectionValue == NULL) {
+ IniSectionReference->Deleted = TRUE;
+ }
+ else {
+ VariableName = CallState->SetIniSection.SectionValue;
+ while (*VariableName) {
+ VariableValue = VariableName;
+ while (*VariableValue != UNICODE_NULL && *VariableValue != L'=') {
+ VariableValue += 1;
+ }
+
+ if (*VariableValue != L'=') {
+ break;
+ }
+
+ *VariableValue++ = UNICODE_NULL;
+
+ RtlInitUnicodeString( &UnicodeString, VariableName );
+ IniVariableReference = FindIniVariableReference( IniSectionReference,
+ AddName( &UnicodeString ),
+ (BOOLEAN)(*VariableValue != UNICODE_NULL)
+ );
+ if (IniVariableReference != NULL) {
+ if (*VariableValue != UNICODE_NULL) {
+ FreeMem( &IniVariableReference->Value );
+ IniVariableReference->Value = AllocMem( (wcslen( VariableValue ) + 1) * sizeof( WCHAR ) );
+ wcscpy( IniVariableReference->Value, VariableValue );
+ if (!IniVariableReference->Created) {
+ if (!wcscmp( IniVariableReference->Value,
+ IniVariableReference->OriginalValue
+ )
+ ) {
+ FreeMem( &IniVariableReference->Value );
+ }
+ else {
+ IniVariableReference->Modified = TRUE;
+ }
+ }
+ }
+ else
+ if (!IniVariableReference->Created) {
+ IniVariableReference->Deleted = TRUE;
+ }
+ else {
+ DestroyIniVariableReference( IniVariableReference );
+ }
+ }
+
+ VariableName = VariableValue;
+ while (*VariableName++ != UNICODE_NULL) {
+ }
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+WritePrivateProfileSectionAHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PWRITEPRIVATEPROFILESECTIONA_PARAMETERS p = &Parameters->InputParameters.WritePrivateProfileSectionA;
+ PWSTR SectionName, FileName, SectionValue;
+ PWSTR IniFilePath;
+
+ if (!Parameters->ReturnValueValid) {
+ FileName = NULL;
+ SectionValue = NULL;
+ if (CaptureAnsiAsUnicode( Process, p->lpFileName, FALSE, NULL, &FileName ) &&
+ GetIniFilePath( FileName, &IniFilePath ) &&
+ CaptureAnsiAsUnicode( Process, p->lpAppName, FALSE, &SectionName, NULL ) &&
+ CaptureAnsiAsUnicode( Process, p->lpString, TRUE, NULL, &SectionValue )
+ ) {
+ return WritePrivateProfileSectionEntry( Process,
+ Thread,
+ Parameters,
+ IniFilePath,
+ SectionName,
+ SectionValue
+ );
+ }
+ }
+ else {
+ return WritePrivateProfileSectionExit( Process,
+ Thread,
+ Parameters
+ );
+ }
+}
+
+BOOLEAN
+WritePrivateProfileSectionWHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PWRITEPRIVATEPROFILESECTIONW_PARAMETERS p = &Parameters->InputParameters.WritePrivateProfileSectionW;
+ PWSTR SectionName, FileName, SectionValue;
+ PWSTR IniFilePath;
+
+ if (!Parameters->ReturnValueValid) {
+ FileName = NULL;
+ SectionValue = NULL;
+ if (CaptureUnicode( Process, p->lpFileName, FALSE, NULL, &FileName ) &&
+ GetIniFilePath( FileName, &IniFilePath ) &&
+ CaptureUnicode( Process, p->lpAppName, FALSE, &SectionName, NULL ) &&
+ CaptureUnicode( Process, p->lpString, TRUE, NULL, &SectionValue )
+ ) {
+ return WritePrivateProfileSectionEntry( Process,
+ Thread,
+ Parameters,
+ IniFilePath,
+ SectionName,
+ SectionValue
+ );
+ }
+ }
+ else {
+ return WritePrivateProfileSectionExit( Process,
+ Thread,
+ Parameters
+ );
+ }
+}
+
+
+BOOLEAN
+RegConnectRegistryWHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ )
+{
+ PREGCONNECTREGISTRYW_PARAMETERS p = &Parameters->InputParameters.RegConnectRegistryW;
+ PWSTR MachineName;
+ LONG ErrorCode;
+ HKEY hKey;
+
+ if (!Parameters->ReturnValueValid) {
+ MachineName = NULL;
+ if (!CaptureUnicode( Process, p->lpMachineName, FALSE, NULL, &MachineName )) {
+ MachineName = L"Unknown";
+ }
+
+ if (AskUser( MB_OKCANCEL,
+ INSTALER_ASKUSER_REGCONNECT,
+ 1,
+ MachineName
+ ) == IDCANCEL
+ ) {
+ FreeMem( &MachineName );
+ Parameters->AbortCall = TRUE;
+ return TRUE;
+ }
+ else {
+ FreeMem( &MachineName );
+ return FALSE;
+ }
+ }
+ else
+ if (Parameters->ReturnValue.ReturnedLong == 0) {
+ ErrorCode = ERROR_ACCESS_DENIED;
+ if (SetProcedureReturnValue( Process,
+ Thread,
+ &ErrorCode,
+ sizeof( ErrorCode )
+ )
+ ) {
+ if (ReadMemory( Process,
+ p->phkResult,
+ &hKey,
+ sizeof( hKey ),
+ "phkResult"
+ )
+ ) {
+ RegCloseKey( hKey );
+ hKey = NULL;
+ WriteMemory( Process,
+ p->phkResult,
+ &hKey,
+ sizeof( hKey ),
+ "phkResult"
+ );
+ }
+ }
+ }
+
+ return TRUE;
+}
diff --git a/private/sdktools/instaler/hlist.c b/private/sdktools/instaler/hlist.c
new file mode 100644
index 000000000..03172b5e2
--- /dev/null
+++ b/private/sdktools/instaler/hlist.c
@@ -0,0 +1,484 @@
+#include <windows.h>
+#include <custcntl.h>
+#include <commdlg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "hlist.h"
+
+
+typedef DWORD (CALLBACK* HLISTCALLBACK)(LPDWORD,LPSTR*,LPSTR,DWORD,DWORD);
+
+#define XBITMAP 16
+#define YBITMAP 16
+#define MARGIN 6
+#define INDENT (XBITMAP + MARGIN)
+#define MIDDLE ((XBITMAP/2) + MARGIN)
+#define LINE_COLOR RGB(255,0,0)
+
+#define CLASS_NAME "HList"
+#define DESCRIPTION "Heir List Box"
+
+#define DEFAULT_STYLE WS_CHILD | \
+ WS_VISIBLE | \
+ WS_VSCROLL | \
+ WS_HSCROLL | \
+ WS_BORDER | \
+ LBS_NOTIFY | \
+ LBS_NOINTEGRALHEIGHT | \
+ LBS_WANTKEYBOARDINPUT | \
+ LBS_SORT | \
+ LBS_OWNERDRAWFIXED
+
+
+
+typedef struct _HLISTTYPE {
+ struct _HLISTTYPE *next;
+ DWORD type;
+ HBITMAP bitmap;
+} HLISTTYPE, *LPHLISTTYPE;
+
+typedef struct _HLISTINFO {
+ HWND hwndList;
+ LPHLISTTYPE typeList;
+ HLISTCALLBACK lpCallback;
+} HLISTINFO, *LPHLISTINFO;
+
+typedef struct _ITEMDATA {
+ struct _ITEMDATA *parent;
+ DWORD type;
+ DWORD level;
+ DWORD nchild;
+ DWORD childnum;
+ LPSTR str;
+ HBITMAP bitmap;
+} ITEMDATA, *LPITEMDATA;
+
+
+static HMODULE hInstance;
+
+
+LRESULT HListWndProc(HWND,UINT,WPARAM,LPARAM);
+
+
+BOOL
+HListInitialize(
+ HMODULE hModule
+ )
+{
+ WNDCLASS wndclass;
+
+
+ hInstance = hModule;
+
+ wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
+ wndclass.lpfnWndProc = HListWndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 4;
+ wndclass.hInstance = hInstance;
+ wndclass.hIcon = NULL;
+ wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
+ wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = CLASS_NAME;
+ if (!RegisterClass( &wndclass )) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+VOID
+CollapseNode(
+ HWND hwnd,
+ LPITEMDATA lpid,
+ DWORD nItem
+ )
+{
+ DWORD i;
+ LPITEMDATA lpidChild;
+
+
+ for (i=0; i<lpid->nchild; i++) {
+ lpidChild = (LPITEMDATA) SendMessage( hwnd, LB_GETITEMDATA, nItem, 0 );
+ if (lpidChild->nchild) {
+ CollapseNode( hwnd, lpidChild, nItem+1 );
+ }
+ SendMessage( hwnd, LB_DELETESTRING, nItem, 0 );
+ }
+
+ lpid->nchild = 0;
+}
+
+LRESULT
+HListWndProc(
+ HWND hwnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+{
+ RECT cRect;
+ HFONT hFont;
+ LPHLISTINFO hli;
+ LPHLISTTYPE hlt;
+ LPHLISTTYPE hltNext;
+ LPMEASUREITEMSTRUCT lpmis;
+ LPDELETEITEMSTRUCT lplis;
+ LPDRAWITEMSTRUCT lpdis;
+ HBITMAP hbmp;
+ HBITMAP hbmpOld;
+ HDC hdc;
+ TEXTMETRIC tm;
+ int x;
+ int y;
+ int nItem;
+ RECT rcBitmap;
+ LPITEMDATA lpid;
+ LPITEMDATA lpidParent;
+ DWORD type;
+ LPSTR str;
+ DWORD rval;
+ DWORD level;
+ LPSTR ref;
+ LPSTR p;
+ POINT pt;
+ DWORD childnum;
+ DWORD i;
+ HPEN hpen;
+ HPEN oldhpen;
+
+
+ switch (message) {
+ case WM_CREATE:
+ hli = (LPHLISTINFO) malloc( sizeof(HLISTINFO) );
+ ZeroMemory( hli, sizeof(HLISTINFO) );
+ GetClientRect( hwnd, &cRect );
+ hli->hwndList = CreateWindow(
+ "LISTBOX",
+ NULL,
+ DEFAULT_STYLE,
+ cRect.left,
+ cRect.top,
+ cRect.right - cRect.left,
+ cRect.bottom - cRect.top,
+ hwnd,
+ NULL,
+ GetModuleHandle(NULL),
+ NULL );
+ hFont = GetStockObject( SYSTEM_FIXED_FONT );
+ SendMessage( hli->hwndList, WM_SETFONT, (WPARAM)hFont, (LPARAM)FALSE );
+ SetFocus( hli->hwndList );
+ SetWindowLong( hwnd, 0, (DWORD)hli );
+ break;
+
+ case WM_SIZE:
+ hli = (LPHLISTINFO) GetWindowLong( hwnd, 0 );
+ GetClientRect( hwnd, &cRect );
+ MoveWindow(
+ hli->hwndList,
+ cRect.left,
+ cRect.top,
+ cRect.right - cRect.left,
+ cRect.bottom - cRect.top,
+ TRUE );
+ break;
+
+ case WM_DESTROY:
+ hli = (LPHLISTINFO) GetWindowLong( hwnd, 0 );
+ hlt = hli->typeList;
+ while (hlt) {
+ hltNext = hlt->next;
+ free( hlt );
+ hlt = hltNext;
+ }
+ DestroyWindow( hli->hwndList );
+ hli->hwndList = NULL;
+ free( hli );
+ SetWindowLong( hwnd, 0, 0 );
+ break;
+
+ case WM_COMMAND:
+ if (HIWORD(wParam) == LBN_DBLCLK) {
+ hli = (LPHLISTINFO) GetWindowLong( hwnd, 0 );
+ if (hli->lpCallback) {
+ nItem = SendMessage( hli->hwndList, LB_GETCURSEL, 0, 0 );
+
+ lpidParent = (LPITEMDATA) SendMessage( hli->hwndList, LB_GETITEMDATA, nItem, 0 );
+
+ y = 0;
+ lpid = lpidParent;
+ while (lpid) {
+ y += (strlen(lpid->str) + 1);
+ lpid = lpid->parent;
+ }
+ y += 16;
+ p = ref = malloc( y );
+ lpid = lpidParent;
+ while (lpid) {
+ strcpy( p, lpid->str );
+ p += (strlen(p) + 1);
+ lpid = lpid->parent;
+ }
+ *p = '\0';
+
+ level = lpidParent->level + 1;
+ type = lpidParent->type;
+ str = lpidParent->str;
+ rval = (hli->lpCallback)( &type, &str, ref, level, lpidParent->nchild );
+
+ if (rval == HLB_COLLAPSE) {
+ CollapseNode( hli->hwndList, lpidParent, nItem+1 );
+ break;
+ }
+
+ childnum = 1;
+ while (rval != HLB_END) {
+ if (rval == HLB_EXPAND) {
+ hlt = hli->typeList;
+ while (hlt && hlt->type != type) {
+ hlt = hlt->next;
+ }
+ if (!hlt) {
+ break;
+ }
+ lpidParent->nchild++;
+ lpid = (LPITEMDATA) malloc( sizeof(ITEMDATA) );
+ lpid->type = type;
+ lpid->nchild = 0;
+ lpid->level = level;
+ lpid->bitmap = hlt->bitmap;
+ lpid->str = _strdup( (LPSTR)str );
+ lpid->parent = lpidParent;
+ lpid->childnum = childnum++;
+ nItem = SendMessage( hli->hwndList, LB_INSERTSTRING, nItem+1, (LPARAM)lpid->str );
+ SendMessage( hli->hwndList, LB_SETITEMDATA, nItem, (LPARAM) lpid );
+ }
+ rval = (hli->lpCallback)( &type, &str, ref, level, 0 );
+ }
+ free( ref );
+ }
+ }
+ break;
+
+ case HLB_REGISTER_CALLBACK:
+ hli = (LPHLISTINFO) GetWindowLong( hwnd, 0 );
+ hli->lpCallback = (HLISTCALLBACK) lParam;
+ return 1;
+
+ case HLB_REGISTER_TYPE:
+ //
+ // wParam = type token
+ // lParam = the bitmap
+ //
+ hli = (LPHLISTINFO) GetWindowLong( hwnd, 0 );
+ if (!hli->typeList) {
+ hli->typeList = (LPHLISTTYPE) malloc( sizeof(HLISTTYPE) );
+ hlt = hli->typeList;
+ } else {
+ hlt = hli->typeList;
+ while (hlt->next) {
+ hlt = hlt->next;
+ }
+ hlt->next = (LPHLISTTYPE) malloc( sizeof(HLISTTYPE) );
+ hlt = hlt->next;
+ }
+ hlt->type = (WPARAM) wParam;
+ hlt->bitmap = (HBITMAP) lParam;
+ hlt->next = NULL;
+ return 1;
+
+ case HLB_ADDSTRING:
+ //
+ // wParam = type
+ // lParam = string
+ //
+ hli = (LPHLISTINFO) GetWindowLong( hwnd, 0 );
+ hlt = hli->typeList;
+ while (hlt && hlt->type != wParam) {
+ hlt = hlt->next;
+ }
+ if (!hlt) {
+ return 0;
+ }
+ lpid = (LPITEMDATA) malloc( sizeof(ITEMDATA) );
+ lpid->type = hlt->type;
+ lpid->level = 0;
+ lpid->nchild = 0;
+ lpid->bitmap = hlt->bitmap;
+ lpid->str = _strdup( (LPSTR)lParam );
+ lpid->parent = NULL;
+ lpid->childnum = 0;
+ nItem = SendMessage( hli->hwndList, LB_ADDSTRING, 0, (LPARAM)lpid->str );
+ SendMessage( hli->hwndList, LB_SETITEMDATA, nItem, (LPARAM) lpid );
+ return 1;
+
+ case WM_MEASUREITEM:
+ lpmis = (LPMEASUREITEMSTRUCT) lParam;
+ lpmis->itemHeight = YBITMAP;
+ return 1;
+
+ case WM_DELETEITEM:
+ lplis = (LPDELETEITEMSTRUCT) lParam;
+ lpid = (LPITEMDATA) lplis->itemData;
+ free( lpid->str );
+ free( lpid );
+ return 1;
+
+ case WM_DRAWITEM:
+ lpdis = (LPDRAWITEMSTRUCT) lParam;
+
+ if (lpdis->itemID == -1) {
+ break;
+ }
+
+ switch (lpdis->itemAction) {
+ case ODA_SELECT:
+ case ODA_DRAWENTIRE:
+ lpid = (LPITEMDATA) SendMessage( lpdis->hwndItem, LB_GETITEMDATA, lpdis->itemID, 0 );
+ hbmp = lpid->bitmap;
+
+ hdc = CreateCompatibleDC( lpdis->hDC );
+ hbmpOld = SelectObject( hdc, hbmp );
+
+ lpdis->rcItem.left += (lpid->level * INDENT);
+
+ BitBlt(
+ lpdis->hDC,
+ lpdis->rcItem.left,
+ lpdis->rcItem.top,
+ lpdis->rcItem.right - lpdis->rcItem.left,
+ lpdis->rcItem.bottom - lpdis->rcItem.top,
+ hdc,
+ 0,
+ 0,
+ SRCCOPY );
+
+ GetTextMetrics( lpdis->hDC, &tm );
+
+ y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2;
+
+ TextOut( lpdis->hDC, XBITMAP + MARGIN + (lpid->level * INDENT), y, lpid->str, strlen(lpid->str) );
+
+ SelectObject( hdc, hbmpOld );
+
+ //
+ // now draw lines
+ //
+ if (lpid->level) {
+
+ hpen = CreatePen( PS_SOLID, 1, LINE_COLOR );
+ oldhpen = SelectObject( lpdis->hDC, hpen );
+
+ y = lpdis->rcItem.top + ((lpdis->rcItem.bottom - lpdis->rcItem.top) / 2);
+ MoveToEx(
+ lpdis->hDC,
+ lpdis->rcItem.left - 1,
+ y,
+ &pt );
+
+ LineTo(
+ lpdis->hDC,
+ lpdis->rcItem.left - MIDDLE,
+ y );
+
+ MoveToEx(
+ lpdis->hDC,
+ lpdis->rcItem.left - MIDDLE,
+ lpdis->rcItem.top,
+ &pt );
+
+ if (lpid->childnum < lpid->parent->nchild) {
+ y = lpdis->rcItem.bottom;
+ } else {
+ y++;
+ }
+
+ LineTo(
+ lpdis->hDC,
+ lpdis->rcItem.left - MIDDLE,
+ y );
+
+ if (lpid->level > 1) {
+ x = lpdis->rcItem.left - MIDDLE;
+ y = lpdis->rcItem.top;
+ for (i=1; i<lpid->level; i++) {
+ MoveToEx(
+ lpdis->hDC,
+ x - (i * INDENT),
+ y,
+ &pt );
+ LineTo(
+ lpdis->hDC,
+ x - (i * INDENT),
+ lpdis->rcItem.bottom );
+ }
+ }
+
+ SelectObject( lpdis->hDC, oldhpen );
+ DeleteObject( hpen );
+ }
+
+ DeleteDC( hdc );
+
+ if (lpdis->itemState & ODS_SELECTED) {
+ rcBitmap.left = lpdis->rcItem.left;
+ rcBitmap.top = lpdis->rcItem.top;
+ rcBitmap.right = lpdis->rcItem.left + XBITMAP + (tm.tmMaxCharWidth * strlen(lpid->str)) + MARGIN + 2;
+ rcBitmap.bottom = lpdis->rcItem.top + YBITMAP;
+ DrawFocusRect( lpdis->hDC, &rcBitmap );
+ }
+
+ break;
+
+ case ODA_FOCUS:
+ break;
+ }
+ return 1;
+
+ case LB_ADDSTRING:
+ return SendMessage( hwnd, HLB_ADDSTRING, 1, lParam );
+
+ case LB_INSERTSTRING:
+ case LB_DELETESTRING:
+ case LB_SELITEMRANGEEX:
+ case LB_RESETCONTENT:
+ case LB_SETSEL:
+ case LB_SETCURSEL:
+ case LB_GETSEL:
+ case LB_GETCURSEL:
+ case LB_GETTEXT:
+ case LB_GETTEXTLEN:
+ case LB_GETCOUNT:
+ case LB_SELECTSTRING:
+ case LB_DIR:
+ case LB_ADDFILE:
+ case LB_GETTOPINDEX:
+ case LB_FINDSTRING:
+ case LB_GETSELCOUNT:
+ case LB_GETSELITEMS:
+ case LB_SETTABSTOPS:
+ case LB_GETHORIZONTALEXTENT:
+ case LB_SETHORIZONTALEXTENT:
+ case LB_SETCOLUMNWIDTH:
+ case LB_SETTOPINDEX:
+ case LB_GETITEMRECT:
+ case LB_GETITEMDATA:
+ case LB_SETITEMDATA:
+ case LB_SELITEMRANGE:
+ case LB_SETANCHORINDEX:
+ case LB_GETANCHORINDEX:
+ case LB_SETCARETINDEX:
+ case LB_GETCARETINDEX:
+ case LB_SETITEMHEIGHT:
+ case LB_GETITEMHEIGHT:
+ case LB_FINDSTRINGEXACT:
+ case LB_SETLOCALE:
+ case LB_GETLOCALE:
+ case LB_SETCOUNT:
+ hli = (LPHLISTINFO) GetWindowLong( hwnd, 0 );
+ return SendMessage( hli->hwndList, message, wParam, lParam );
+ }
+
+ return DefWindowProc( hwnd, message, wParam, lParam );
+}
diff --git a/private/sdktools/instaler/hlist.h b/private/sdktools/instaler/hlist.h
new file mode 100644
index 000000000..61a63a98f
--- /dev/null
+++ b/private/sdktools/instaler/hlist.h
@@ -0,0 +1,13 @@
+
+//
+// hlist defines
+//
+#define HLB_ADDSTRING WM_USER+100
+#define HLB_INSERTSTRING WM_USER+101
+#define HLB_REGISTER_TYPE WM_USER+102
+#define HLB_REGISTER_CALLBACK WM_USER+103
+
+#define HLB_IGNORE 0
+#define HLB_EXPAND 1
+#define HLB_COLLAPSE 2
+#define HLB_END 3
diff --git a/private/sdktools/instaler/i386/machine.c b/private/sdktools/instaler/i386/machine.c
new file mode 100644
index 000000000..9f5b120d5
--- /dev/null
+++ b/private/sdktools/instaler/i386/machine.c
@@ -0,0 +1,332 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ machine.c
+
+Abstract:
+
+ This file contains machine specific code to support the INSTALER
+ program. Specifically, routines to fetch parameters from the
+ registers/stack of a target process, routines to set a breakpoint
+ and step over an instruction at a breakpoint.
+
+Author:
+
+ Steve Wood (stevewo) 10-Aug-1994
+
+Revision History:
+
+--*/
+
+#include "instaler.h"
+
+#define BREAKPOINT_OPCODE 0xCC
+#define INT_OPCODE 0xCD
+
+UCHAR InstructionBuffer = BREAKPOINT_OPCODE;
+PVOID BreakpointInstruction = (PVOID)&InstructionBuffer;
+ULONG SizeofBreakpointInstruction = sizeof( InstructionBuffer );
+
+BOOLEAN
+SkipOverHardcodedBreakpoint(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PVOID BreakpointAddress
+ )
+{
+ UCHAR InstructionByte;
+ CONTEXT Context;
+
+ Context.ContextFlags = CONTEXT_FULL;
+ if (!GetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to get context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+
+ if (!ReadMemory( Process,
+ BreakpointAddress,
+ &InstructionByte,
+ sizeof( InstructionByte ),
+ "hard coded breakpoint"
+ )
+ ) {
+ return FALSE;
+ }
+
+ if (InstructionByte == BREAKPOINT_OPCODE) {
+ Context.Eip = (ULONG)((PCHAR)BreakpointAddress + 1);
+ }
+ else
+ if (InstructionByte == INT_OPCODE) {
+ Context.Eip = (ULONG)((PCHAR)BreakpointAddress + 2);
+ }
+ else {
+ return FALSE;
+ }
+
+ if (!SetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to set context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+
+BOOLEAN
+ExtractProcedureParameters(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PULONG ReturnAddress,
+ ULONG SizeOfParameters,
+ PULONG Parameters
+ )
+{
+ UINT i;
+ ULONG NumberOfParameters;
+ CONTEXT Context;
+ ULONG StackBuffer[ 1+31 ];
+
+ NumberOfParameters = SizeOfParameters / sizeof( ULONG );
+ if ((NumberOfParameters * sizeof( ULONG )) != SizeOfParameters ||
+ NumberOfParameters > 31
+ ) {
+ DbgEvent( INTERNALERROR, ( "Invalid parameter size %x\n", SizeOfParameters ) );
+ return FALSE;
+ }
+
+ Context.ContextFlags = CONTEXT_CONTROL;
+ if (!GetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to get context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+
+ if (!ReadMemory( Process,
+ (PVOID)Context.Esp,
+ StackBuffer,
+ SizeOfParameters + sizeof( ULONG ),
+ "parameters"
+ )
+ ) {
+ return FALSE;
+ }
+
+ *ReturnAddress = StackBuffer[ 0 ];
+ for (i=0; i<NumberOfParameters; i++) {
+ Parameters[ i ] = StackBuffer[ 1+i ];
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+ExtractProcedureReturnValue(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PVOID ReturnValue,
+ ULONG SizeOfReturnValue
+ )
+{
+ CONTEXT Context;
+
+ Context.ContextFlags = CONTEXT_FULL;
+ if (!GetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to get context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+
+ switch (SizeOfReturnValue) {
+ case sizeof( UCHAR ):
+ *(PUCHAR)ReturnValue = (UCHAR)Context.Eax;
+ break;
+ case sizeof( USHORT ):
+ *(PUSHORT)ReturnValue = (USHORT)Context.Eax;
+ break;
+ case sizeof( ULONG ):
+ *(PULONG)ReturnValue = (ULONG)Context.Eax;
+ break;
+ case sizeof( ULONGLONG ):
+ *(PULONGLONG)ReturnValue = (ULONGLONG)Context.Edx << 32 | Context.Eax;
+ break;
+ default:
+ DbgEvent( INTERNALERROR, ( "Invalid return value size (%u)\n", SizeOfReturnValue ) );
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+SetProcedureReturnValue(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PVOID ReturnValue,
+ ULONG SizeOfReturnValue
+ )
+{
+ CONTEXT Context;
+
+ Context.ContextFlags = CONTEXT_FULL;
+ if (!GetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to get context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+
+ switch (SizeOfReturnValue) {
+ case sizeof( UCHAR ):
+ (UCHAR)Context.Eax = *(PUCHAR)ReturnValue;
+ break;
+ case sizeof( USHORT ):
+ (USHORT)Context.Eax = *(PUSHORT)ReturnValue;
+ break;
+ case sizeof( ULONG ):
+ (ULONG)Context.Eax = *(PULONG)ReturnValue;
+ break;
+ case sizeof( ULONGLONG ):
+ (ULONG)Context.Eax = *(PULONG)ReturnValue;
+ (ULONG)Context.Edx = *((PULONG)ReturnValue + 1);
+ break;
+ default:
+ DbgEvent( INTERNALERROR, ( "Invalid return value size (%u)\n", SizeOfReturnValue ) );
+ return FALSE;
+ }
+
+ if (!SetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to set context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+BOOLEAN
+ForceReturnToCaller(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ ULONG SizeOfParameters,
+ PVOID ReturnAddress,
+ PVOID ReturnValue,
+ ULONG SizeOfReturnValue
+ )
+{
+ CONTEXT Context;
+
+ Context.ContextFlags = CONTEXT_FULL;
+ if (!GetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to get context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+
+ switch (SizeOfReturnValue) {
+ case sizeof( UCHAR ):
+ (UCHAR)Context.Eax = *(PUCHAR)ReturnValue;
+ break;
+ case sizeof( USHORT ):
+ (USHORT)Context.Eax = *(PUSHORT)ReturnValue;
+ break;
+ case sizeof( ULONG ):
+ (ULONG)Context.Eax = *(PULONG)ReturnValue;
+ break;
+ case sizeof( ULONGLONG ):
+ (ULONG)Context.Eax = *(PULONG)ReturnValue;
+ (ULONG)Context.Edx = *((PULONG)ReturnValue + 1);
+ break;
+ default:
+ DbgEvent( INTERNALERROR, ( "Invalid return value size (%u)\n", SizeOfReturnValue ) );
+ return FALSE;
+ }
+
+ Context.Eip = (ULONG)ReturnAddress;
+ Context.Esp = Context.Esp + sizeof( ReturnAddress ) + SizeOfParameters;
+
+ if (!SetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to set context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+
+
+BOOLEAN
+UndoReturnAddressBreakpoint(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ )
+{
+ CONTEXT Context;
+
+ Context.ContextFlags = CONTEXT_FULL;
+ if (!GetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to get context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+
+ Context.Eip -= 1; // Back up to where breakpoint instruction was
+ if (!SetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to set context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+
+BOOLEAN
+BeginSingleStepBreakpoint(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ )
+{
+ CONTEXT Context;
+
+ Context.ContextFlags = CONTEXT_FULL;
+ if (!GetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to get context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+
+ Context.Eip -= 1; // Back up to where breakpoint instruction was
+ Context.EFlags |= V86FLAGS_TRACE;
+ if (!SetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to set context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+
+BOOLEAN
+EndSingleStepBreakpoint(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ )
+{
+ CONTEXT Context;
+
+ Context.ContextFlags = CONTEXT_FULL;
+ if (!GetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to get context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+
+ Context.EFlags &= ~V86FLAGS_TRACE;
+ if (!SetThreadContext( Thread->Handle, &Context )) {
+ DbgEvent( INTERNALERROR, ( "Failed to set context for thread %x (%x) - %u\n", Thread->Id, Thread->Handle, GetLastError() ) );
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
diff --git a/private/sdktools/instaler/i386/sources b/private/sdktools/instaler/i386/sources
new file mode 100644
index 000000000..96beda740
--- /dev/null
+++ b/private/sdktools/instaler/i386/sources
@@ -0,0 +1,32 @@
+i386_SOURCES=..\instaler.rc \
+ ..\instutil.c \
+ ..\iml.c \
+ ..\init.c \
+ ..\handler.c \
+ ..\debug.c \
+ ..\process.c \
+ ..\handledb.c \
+ ..\namedb.c \
+ ..\file.c \
+ ..\key.c \
+ ..\ini.c \
+ ..\error.c \
+ ..\event.c \
+ i386\machine.c
+
+UMTYPE=console
+UMAPPL=instaler*showinst*undoinst*compinst
+UMTEST=testins1*testins2
+UMLIBS=\nt\public\sdk\lib\*\setargv.obj \
+ obj\*\instaler.lib \
+ \nt\public\sdk\lib\*\ntdll.lib \
+ \nt\public\sdk\lib\*\user32.lib
+UMRES=$(@R).res
+
+NTTARGETFILE0=errormsg.h \
+ obj\*\instaler.res \
+ obj\*\testins1.res \
+ obj\*\testins2.res \
+ obj\*\showinst.res \
+ obj\*\undoinst.res \
+ obj\*\compinst.res
diff --git a/private/sdktools/instaler/iml.c b/private/sdktools/instaler/iml.c
new file mode 100644
index 000000000..5e8dbb7f1
--- /dev/null
+++ b/private/sdktools/instaler/iml.c
@@ -0,0 +1,481 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ iml.c
+
+Abstract:
+
+ This module contains routines for creating and accessing Installation
+ Modification Log files (.IML)
+
+Author:
+
+ Steve Wood (stevewo) 15-Jan-1996
+
+Revision History:
+
+--*/
+
+#include "instutil.h"
+#include "iml.h"
+
+PWSTR
+FormatImlPath(
+ PWSTR DirectoryPath,
+ PWSTR InstallationName
+ )
+{
+ PWSTR ImlPath;
+ ULONG n;
+
+ n = wcslen( DirectoryPath ) + wcslen( InstallationName ) + 8;
+ ImlPath = HeapAlloc( GetProcessHeap(), 0, (n * sizeof( WCHAR )) );
+ if (ImlPath != NULL) {
+ _snwprintf( ImlPath, n, L"%s%s.IML", DirectoryPath, InstallationName );
+ }
+
+ return ImlPath;
+}
+
+
+PINSTALLATION_MODIFICATION_LOGFILE
+CreateIml(
+ PWSTR ImlPath,
+ BOOLEAN IncludeCreateFileContents
+ )
+{
+ HANDLE FileHandle;
+ PINSTALLATION_MODIFICATION_LOGFILE pIml;
+
+ pIml = NULL;
+ FileHandle = CreateFile( ImlPath,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ 0,
+ NULL
+ );
+ if (FileHandle != INVALID_HANDLE_VALUE) {
+ pIml = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( *pIml ) );
+ if (pIml != NULL) {
+ pIml->Signature = IML_SIGNATURE;
+ pIml->FileHandle = FileHandle;
+ pIml->FileOffset = ROUND_UP( sizeof( *pIml ), 8 );
+ if (IncludeCreateFileContents) {
+ pIml->Flags = IML_FLAG_CONTAINS_NEWFILECONTENTS;
+ }
+
+ SetFilePointer( pIml->FileHandle, pIml->FileOffset, NULL, FILE_BEGIN );
+ }
+ }
+
+ return pIml;
+}
+
+
+PINSTALLATION_MODIFICATION_LOGFILE
+LoadIml(
+ PWSTR ImlPath
+ )
+{
+ HANDLE FileHandle, MappingHandle;
+ PINSTALLATION_MODIFICATION_LOGFILE pIml;
+
+ pIml = NULL;
+ FileHandle = CreateFile( ImlPath,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+ if (FileHandle != INVALID_HANDLE_VALUE) {
+ MappingHandle = CreateFileMapping( FileHandle,
+ NULL,
+ PAGE_READONLY,
+ 0,
+ 0,
+ NULL
+ );
+ CloseHandle( FileHandle );
+ if (MappingHandle != NULL) {
+ pIml = MapViewOfFile( MappingHandle,
+ FILE_MAP_READ,
+ 0,
+ 0,
+ 0
+ );
+ CloseHandle( MappingHandle );
+ if (pIml != NULL) {
+ if (pIml->Signature != IML_SIGNATURE) {
+ UnmapViewOfFile( pIml );
+ pIml = NULL;
+ SetLastError( ERROR_BAD_FORMAT );
+ }
+
+ }
+ }
+ }
+
+ return pIml;
+}
+
+
+BOOLEAN
+CloseIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml
+ )
+{
+ HANDLE FileHandle;
+ BOOLEAN Result;
+ ULONG BytesWritten;
+
+ if (pIml->FileHandle == NULL) {
+ if (!UnmapViewOfFile( pIml )) {
+ return FALSE;
+ }
+ else {
+ Result = TRUE;
+ }
+ }
+ else {
+ FileHandle = pIml->FileHandle;
+ pIml->FileHandle = NULL;
+ if (!SetEndOfFile( FileHandle ) ||
+ SetFilePointer( FileHandle, 0, NULL, FILE_BEGIN ) != 0 ||
+ !WriteFile( FileHandle,
+ pIml,
+ sizeof( *pIml ),
+ &BytesWritten,
+ NULL
+ ) ||
+ BytesWritten != sizeof( *pIml )
+ ) {
+ Result = FALSE;
+ }
+ else {
+ Result = TRUE;
+ }
+ CloseHandle( FileHandle );
+ }
+
+ return Result;
+}
+
+
+POFFSET
+ImlWrite(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ PVOID p,
+ ULONG Size
+ )
+{
+ POFFSET Result;
+ ULONG BytesWritten;
+
+ if (!WriteFile( pIml->FileHandle,
+ p,
+ Size,
+ &BytesWritten,
+ NULL
+ ) ||
+ BytesWritten != Size
+ ) {
+ return 0;
+ }
+
+ Result = pIml->FileOffset;
+ pIml->FileOffset += ROUND_UP( Size, 8 );
+ SetFilePointer( pIml->FileHandle,
+ pIml->FileOffset,
+ NULL,
+ FILE_BEGIN
+ );
+ return Result;
+}
+
+POFFSET
+ImlWriteString(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ PWSTR Name
+ )
+{
+ if (Name == NULL) {
+ return 0;
+ }
+ else {
+ return ImlWrite( pIml, Name, (wcslen( Name ) + 1) * sizeof( WCHAR ) );
+ }
+}
+
+
+POFFSET
+ImlWriteFileContents(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ PWSTR FileName
+ )
+{
+ HANDLE hFindFirst, hFile, hMapping;
+ WIN32_FIND_DATA FindFileData;
+ PVOID p;
+ IML_FILE_RECORD_CONTENTS ImlFileContentsRecord;
+
+ hFindFirst = FindFirstFile( FileName, &FindFileData );
+ if (hFindFirst == INVALID_HANDLE_VALUE) {
+ return 0;
+ }
+ FindClose( hFindFirst );
+
+ ImlFileContentsRecord.LastWriteTime = FindFileData.ftLastWriteTime;
+ ImlFileContentsRecord.FileAttributes = FindFileData.dwFileAttributes;
+ if (!(ImlFileContentsRecord.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ ImlFileContentsRecord.FileSize = FindFileData.nFileSizeLow;
+ hFile = CreateFile( FileName,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+ if (hFile == INVALID_HANDLE_VALUE) {
+ printf( "*** CreateFile( '%ws' ) failed (%u)\n", FileName, GetLastError() );
+ return 0;
+ }
+
+ if (ImlFileContentsRecord.FileSize != 0) {
+ hMapping = CreateFileMapping( hFile,
+ NULL,
+ PAGE_READONLY,
+ 0,
+ 0,
+ NULL
+ );
+ CloseHandle( hFile );
+ hFile = NULL;
+ if (hMapping == NULL) {
+ printf( "*** CreateFileMapping( '%ws' ) failed (%u)\n", FileName, GetLastError() );
+ return 0;
+ }
+
+ p = MapViewOfFile( hMapping,
+ FILE_MAP_READ,
+ 0,
+ 0,
+ 0
+ );
+ CloseHandle( hMapping );
+ if (p == NULL) {
+ printf( "*** MapViewOfFile( '%ws' ) failed (%u)\n", FileName, GetLastError() );
+ return 0;
+ }
+ }
+ else {
+ CloseHandle( hFile );
+ p = NULL;
+ }
+ }
+ else {
+ ImlFileContentsRecord.FileSize = 0;
+ p = NULL;
+ }
+
+ ImlFileContentsRecord.Contents = ImlWrite( pIml, p, ImlFileContentsRecord.FileSize );
+ if (p != NULL) {
+ UnmapViewOfFile( p );
+ }
+
+ return ImlWrite( pIml,
+ &ImlFileContentsRecord,
+ sizeof( ImlFileContentsRecord )
+ );
+}
+
+BOOLEAN
+ImlAddFileRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_FILE_ACTION Action,
+ PWSTR Name,
+ PWSTR BackupFileName,
+ PFILETIME BackupLastWriteTime,
+ DWORD BackupFileAttributes
+ )
+{
+ IML_FILE_RECORD ImlFileRecord;
+ IML_FILE_RECORD_CONTENTS ImlFileContentsRecord;
+
+ memset( &ImlFileRecord, 0, sizeof( ImlFileRecord ) );
+ ImlFileRecord.Action = Action;
+ ImlFileRecord.Name = ImlWriteString( pIml, Name );
+ if (Action == CreateNewFile) {
+ if ((pIml->Flags & IML_FLAG_CONTAINS_NEWFILECONTENTS) != 0) {
+ ImlFileRecord.NewFile = ImlWriteFileContents( pIml, Name );
+ }
+ }
+ else
+ if (Action == ModifyOldFile ||
+ Action == DeleteOldFile
+ ) {
+ if (BackupFileName != NULL) {
+ ImlFileRecord.OldFile = ImlWriteFileContents( pIml, BackupFileName );
+ }
+
+ if (Action == ModifyOldFile &&
+ (pIml->Flags & IML_FLAG_CONTAINS_NEWFILECONTENTS) != 0
+ ) {
+ ImlFileRecord.NewFile = ImlWriteFileContents( pIml, Name );
+ }
+ }
+ else {
+ ImlFileContentsRecord.LastWriteTime = *BackupLastWriteTime;
+ ImlFileContentsRecord.FileAttributes = BackupFileAttributes;
+ ImlFileContentsRecord.FileSize = 0;
+ ImlFileContentsRecord.Contents = 0;
+ ImlFileRecord.OldFile = ImlWrite( pIml,
+ &ImlFileContentsRecord,
+ sizeof( ImlFileContentsRecord )
+ );
+ }
+
+ ImlFileRecord.Next = pIml->FileRecords;
+ pIml->FileRecords = ImlWrite( pIml, &ImlFileRecord, sizeof( ImlFileRecord ) );
+ pIml->NumberOfFileRecords += 1;
+ return TRUE;
+}
+
+
+BOOLEAN
+ImlAddValueRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_VALUE_ACTION Action,
+ PWSTR Name,
+ DWORD ValueType,
+ ULONG ValueLength,
+ PVOID ValueData,
+ DWORD BackupValueType,
+ ULONG BackupValueLength,
+ PVOID BackupValueData,
+ POFFSET *Values
+ )
+{
+ IML_VALUE_RECORD ImlValueRecord;
+ IML_VALUE_RECORD_CONTENTS ImlValueContentsRecord;
+
+ memset( &ImlValueRecord, 0, sizeof( ImlValueRecord ) );
+ ImlValueRecord.Action = Action;
+ ImlValueRecord.Name = ImlWriteString( pIml, Name );
+ if (Action != DeleteOldValue) {
+ ImlValueContentsRecord.Type = ValueType;
+ ImlValueContentsRecord.Length = ValueLength;
+ ImlValueContentsRecord.Data = ImlWrite( pIml, ValueData, ValueLength );
+ ImlValueRecord.NewValue = ImlWrite( pIml,
+ &ImlValueContentsRecord,
+ sizeof( ImlValueContentsRecord )
+ );
+ }
+ if (Action != CreateNewValue) {
+ ImlValueContentsRecord.Type = BackupValueType;
+ ImlValueContentsRecord.Length = BackupValueLength;
+ ImlValueContentsRecord.Data = ImlWrite( pIml, BackupValueData, BackupValueLength );
+ ImlValueRecord.OldValue = ImlWrite( pIml,
+ &ImlValueContentsRecord,
+ sizeof( ImlValueContentsRecord )
+ );
+ }
+
+ ImlValueRecord.Next = *Values;
+ *Values = ImlWrite( pIml, &ImlValueRecord, sizeof( ImlValueRecord ) );
+ return TRUE;
+}
+
+
+BOOLEAN
+ImlAddKeyRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_KEY_ACTION Action,
+ PWSTR Name,
+ POFFSET Values
+ )
+{
+ IML_KEY_RECORD ImlKeyRecord;
+
+ memset( &ImlKeyRecord, 0, sizeof( ImlKeyRecord ) );
+ ImlKeyRecord.Action = Action;
+ ImlKeyRecord.Name = ImlWriteString( pIml, Name );
+ ImlKeyRecord.Values = Values;
+ ImlKeyRecord.Next = pIml->KeyRecords;
+ pIml->KeyRecords = ImlWrite( pIml, &ImlKeyRecord, sizeof( ImlKeyRecord ) );
+ pIml->NumberOfKeyRecords += 1;
+ return TRUE;
+}
+
+
+
+BOOLEAN
+ImlAddIniVariableRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_INIVARIABLE_ACTION Action,
+ PWSTR Name,
+ PWSTR OldValue,
+ PWSTR NewValue,
+ POFFSET *Variables
+ )
+{
+ IML_INIVARIABLE_RECORD ImlIniVariableRecord;
+
+ memset( &ImlIniVariableRecord, 0, sizeof( ImlIniVariableRecord ) );
+ ImlIniVariableRecord.Action = Action;
+ ImlIniVariableRecord.Name = ImlWriteString( pIml, Name );
+ ImlIniVariableRecord.OldValue = ImlWriteString( pIml, OldValue );
+ ImlIniVariableRecord.NewValue = ImlWriteString( pIml, NewValue );
+ ImlIniVariableRecord.Next = *Variables;
+ *Variables = ImlWrite( pIml, &ImlIniVariableRecord, sizeof( ImlIniVariableRecord ) );
+ return TRUE;
+}
+
+BOOLEAN
+ImlAddIniSectionRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_INISECTION_ACTION Action,
+ PWSTR Name,
+ POFFSET Variables,
+ POFFSET *Sections
+ )
+{
+ IML_INISECTION_RECORD ImlIniSectionRecord;
+
+ memset( &ImlIniSectionRecord, 0, sizeof( ImlIniSectionRecord ) );
+ ImlIniSectionRecord.Action = Action;
+ ImlIniSectionRecord.Name = ImlWriteString( pIml, Name );
+ ImlIniSectionRecord.Variables = Variables;
+ ImlIniSectionRecord.Next = *Sections;
+ *Sections = ImlWrite( pIml, &ImlIniSectionRecord, sizeof( ImlIniSectionRecord ) );
+ return TRUE;
+}
+
+BOOLEAN
+ImlAddIniRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_INI_ACTION Action,
+ PWSTR Name,
+ PFILETIME BackupLastWriteTime,
+ POFFSET Sections
+ )
+{
+ IML_INI_RECORD ImlIniRecord;
+
+ memset( &ImlIniRecord, 0, sizeof( ImlIniRecord ) );
+ ImlIniRecord.Action = Action;
+ ImlIniRecord.Name = ImlWriteString( pIml, Name );
+ ImlIniRecord.LastWriteTime = *BackupLastWriteTime;
+ ImlIniRecord.Sections = Sections;
+ ImlIniRecord.Next = pIml->IniRecords;
+ pIml->IniRecords = ImlWrite( pIml, &ImlIniRecord, sizeof( ImlIniRecord ) );
+ pIml->NumberOfIniRecords += 1;
+ return TRUE;
+}
diff --git a/private/sdktools/instaler/iml.h b/private/sdktools/instaler/iml.h
new file mode 100644
index 000000000..dea3eadb7
--- /dev/null
+++ b/private/sdktools/instaler/iml.h
@@ -0,0 +1,221 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ iml.h
+
+Abstract:
+
+ Header for for creating, accessing and manipulating Installation Modification
+ Log files (.IML extension) created by INSTALER.EXE
+
+Author:
+
+ Steve Wood (stevewo) 23-Jan-1996
+
+--*/
+
+typedef ULONG POFFSET;
+
+#define MP(t,b,o) ((t)((o) ? (PVOID)((ULONG)(b) + (o)) : NULL))
+
+typedef struct _INSTALLATION_MODIFICATION_LOGFILE {
+ ULONG Signature;
+ USHORT HeaderSize;
+ USHORT Flags;
+ HANDLE FileHandle;
+ ULONG FileOffset;
+ ULONG NumberOfFileRecords;
+ POFFSET FileRecords; // IML_FILE_RECORD
+ ULONG NumberOfKeyRecords;
+ POFFSET KeyRecords; // IML_KEY_RECORD
+ ULONG NumberOfIniRecords;
+ POFFSET IniRecords; // IML_INI_RECORD
+} INSTALLATION_MODIFICATION_LOGFILE, *PINSTALLATION_MODIFICATION_LOGFILE;
+
+#define IML_SIGNATURE 0xFF0AA0FF
+
+#define IML_FLAG_CONTAINS_NEWFILECONTENTS 0x0001
+#define IML_FLAG_ACTIONS_UNDONE 0x0002
+
+PWSTR
+FormatImlPath(
+ PWSTR DirectoryPath,
+ PWSTR InstallationName
+ );
+
+PINSTALLATION_MODIFICATION_LOGFILE
+CreateIml(
+ PWSTR ImlPath,
+ BOOLEAN IncludeCreateFileContents
+ );
+
+PINSTALLATION_MODIFICATION_LOGFILE
+LoadIml(
+ PWSTR ImlPath
+ );
+
+BOOLEAN
+CloseIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml
+ );
+
+typedef enum _IML_FILE_ACTION {
+ CreateNewFile,
+ ModifyOldFile,
+ DeleteOldFile,
+ ModifyFileDateTime,
+ ModifyFileAttributes
+} IML_FILE_ACTION;
+
+typedef struct _IML_FILE_RECORD_CONTENTS {
+ FILETIME LastWriteTime;
+ DWORD FileAttributes;
+ DWORD FileSize;
+ POFFSET Contents; // VOID
+} IML_FILE_RECORD_CONTENTS, *PIML_FILE_RECORD_CONTENTS;
+
+
+typedef struct _IML_FILE_RECORD {
+ POFFSET Next; // IML_FILE_RECORD
+ IML_FILE_ACTION Action;
+ POFFSET Name; // WCHAR
+ POFFSET OldFile; // IML_FILE_RECORD_CONTENTS
+ POFFSET NewFile; // IML_FILE_RECORD_CONTENTS
+} IML_FILE_RECORD, *PIML_FILE_RECORD;
+
+BOOLEAN
+ImlAddFileRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_FILE_ACTION Action,
+ PWSTR Name,
+ PWSTR BackupFileName,
+ PFILETIME BackupLastWriteTime,
+ DWORD BackupFileAttributes
+ );
+
+typedef enum _IML_VALUE_ACTION {
+ CreateNewValue,
+ DeleteOldValue,
+ ModifyOldValue
+} IML_VALUE_ACTION;
+
+typedef struct _IML_VALUE_RECORD_CONTENTS {
+ DWORD Type;
+ DWORD Length;
+ POFFSET Data; // VOID
+} IML_VALUE_RECORD_CONTENTS, *PIML_VALUE_RECORD_CONTENTS;
+
+typedef struct _IML_VALUE_RECORD {
+ POFFSET Next; // IML_VALUE_RECORD
+ IML_VALUE_ACTION Action;
+ POFFSET Name; // WCHAR
+ POFFSET OldValue; // IML_VALUE_RECORD_CONTENTS
+ POFFSET NewValue; // IML_VALUE_RECORD_CONTENTS
+} IML_VALUE_RECORD, *PIML_VALUE_RECORD;
+
+BOOLEAN
+ImlAddValueRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_VALUE_ACTION Action,
+ PWSTR Name,
+ DWORD ValueType,
+ ULONG ValueLength,
+ PVOID ValueData,
+ DWORD BackupValueType,
+ ULONG BackupValueLength,
+ PVOID BackupValueData,
+ POFFSET *Values // IML_VALUE_RECORD
+ );
+
+typedef enum _IML_KEY_ACTION {
+ CreateNewKey,
+ DeleteOldKey,
+ ModifyKeyValues
+} IML_KEY_ACTION;
+
+typedef struct _IML_KEY_RECORD {
+ POFFSET Next; // IML_KEY_RECORD
+ IML_KEY_ACTION Action;
+ POFFSET Name; // WCHAR
+ POFFSET Values; // IML_VALUE_RECORD
+} IML_KEY_RECORD, *PIML_KEY_RECORD;
+
+BOOLEAN
+ImlAddKeyRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_KEY_ACTION Action,
+ PWSTR Name,
+ POFFSET Values // IML_VALUE_RECORD
+ );
+
+typedef enum _IML_INIVARIABLE_ACTION {
+ CreateNewVariable,
+ DeleteOldVariable,
+ ModifyOldVariable
+} IML_INIVARIABLE_ACTION;
+
+typedef struct _IML_INIVARIABLE_RECORD {
+ POFFSET Next; // IML_INIVARIABLE_RECORD
+ IML_INIVARIABLE_ACTION Action;
+ POFFSET Name; // WCHAR
+ POFFSET OldValue; // WCHAR
+ POFFSET NewValue; // WCHAR
+} IML_INIVARIABLE_RECORD, *PIML_INIVARIABLE_RECORD;
+
+BOOLEAN
+ImlAddIniVariableRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_INIVARIABLE_ACTION Action,
+ PWSTR Name,
+ PWSTR OldValue,
+ PWSTR NewValue,
+ POFFSET *Variables // IML_INIVARIABLE_RECORD
+ );
+
+typedef enum _IML_INISECTION_ACTION {
+ CreateNewSection,
+ DeleteOldSection,
+ ModifySectionVariables
+} IML_INISECTION_ACTION;
+
+typedef struct _IML_INISECTION_RECORD {
+ POFFSET Next; // IML_INISECTION_RECORD
+ IML_INISECTION_ACTION Action;
+ POFFSET Name; // WCHAR
+ POFFSET Variables; // IML_INIVARIABLE_RECORD
+} IML_INISECTION_RECORD, *PIML_INISECTION_RECORD;
+
+BOOLEAN
+ImlAddIniSectionRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_INISECTION_ACTION Action,
+ PWSTR Name,
+ POFFSET Variables, // IML_INIVARIABLE_RECORD
+ POFFSET *Sections // IML_INISECTION_RECORD
+ );
+
+typedef enum _IML_INI_ACTION {
+ CreateNewIniFile,
+ ModifyOldIniFile
+} IML_INI_ACTION;
+
+typedef struct _IML_INI_RECORD {
+ POFFSET Next; // IML_INI_RECORD
+ IML_INI_ACTION Action;
+ POFFSET Name; // WCHAR
+ POFFSET Sections; // IML_INISECTION_RECORD
+ FILETIME LastWriteTime;
+} IML_INI_RECORD, *PIML_INI_RECORD;
+
+
+BOOLEAN
+ImlAddIniRecord(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml,
+ IML_INI_ACTION Action,
+ PWSTR Name,
+ PFILETIME BackupLastWriteTime,
+ POFFSET Sections // IML_INISECTION_RECORD
+ );
diff --git a/private/sdktools/instaler/ini.c b/private/sdktools/instaler/ini.c
new file mode 100644
index 000000000..f50a177c7
--- /dev/null
+++ b/private/sdktools/instaler/ini.c
@@ -0,0 +1,382 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ini.c
+
+Abstract:
+
+ This module implements the functions to save references to .INI file
+ sections and values for the INSTALER program. Part of each reference is
+ a a backup copy of sections and values for an .INI file.
+
+Author:
+
+ Steve Wood (stevewo) 22-Aug-1994
+
+Revision History:
+
+--*/
+
+#include "instaler.h"
+
+
+BOOLEAN
+CreateIniFileReference(
+ PWSTR Name,
+ PINI_FILE_REFERENCE *ReturnedReference
+ )
+{
+ PINI_FILE_REFERENCE p;
+ PINI_SECTION_REFERENCE p1;
+ PINI_VARIABLE_REFERENCE p2;
+ PWSTR SectionName;
+ PWSTR VariableName;
+ PWSTR VariableValue;
+ UNICODE_STRING UnicodeString;
+ PLIST_ENTRY Head, Next;
+ ULONG n, n1;
+ BOOLEAN Result;
+ HANDLE FindHandle;
+ WIN32_FIND_DATA FindFileData;
+
+ p = FindIniFileReference( Name );
+ if (p != NULL) {
+ *ReturnedReference = p;
+ return TRUE;
+ }
+
+ p = AllocMem( sizeof( *p ) );
+ if (p == NULL) {
+ return FALSE;
+ }
+
+ p->Name = Name;
+ InitializeListHead( &p->SectionReferencesListHead );
+ InsertTailList( &IniReferenceListHead, &p->Entry );
+ FindHandle = FindFirstFile( Name, &FindFileData );
+ if (FindHandle == INVALID_HANDLE_VALUE) {
+ p->Created = TRUE;
+ *ReturnedReference = p;
+ return TRUE;
+ }
+ FindClose( FindHandle );
+ p->BackupLastWriteTime = FindFileData.ftLastWriteTime;
+
+ GrowTemporaryBuffer( n = 0xF000 );
+ SectionName = TemporaryBuffer;
+
+ Result = TRUE;
+ n1 = GetPrivateProfileStringW( NULL,
+ NULL,
+ L"",
+ SectionName,
+ n,
+ Name
+ );
+ if (n1 == 0 || n1 == n-2) {
+ Result = FALSE;
+ }
+ else {
+ while (*SectionName != UNICODE_NULL) {
+ p1 = AllocMem( sizeof( *p1 ) );
+ if (p1 == NULL) {
+ Result = FALSE;
+ break;
+ }
+
+ RtlInitUnicodeString( &UnicodeString, SectionName );
+ p1->Name = AddName( &UnicodeString );
+ if (FindIniSectionReference( p, p1->Name, FALSE )) {
+ FreeMem( &p1 );
+ }
+ else {
+ InitializeListHead( &p1->VariableReferencesListHead );
+ InsertTailList( &p->SectionReferencesListHead, &p1->Entry );
+ }
+ while (*SectionName++ != UNICODE_NULL) {
+ }
+ }
+ }
+
+ VariableName = TemporaryBuffer;
+ Head = &p->SectionReferencesListHead;
+ Next = Head->Flink;
+ while (Result && (Head != Next)) {
+ p1 = CONTAINING_RECORD( Next, INI_SECTION_REFERENCE, Entry );
+ n1 = GetPrivateProfileSectionW( p1->Name,
+ VariableName,
+ n,
+ Name
+ );
+ if (n1 == n-2) {
+ Result = FALSE;
+ break;
+ }
+
+ while (*VariableName != UNICODE_NULL) {
+ VariableValue = VariableName;
+ while (*VariableValue != UNICODE_NULL && *VariableValue != L'=') {
+ VariableValue += 1;
+ }
+
+ if (*VariableValue != L'=') {
+ Result = FALSE;
+ break;
+ }
+ *VariableValue++ = UNICODE_NULL;
+
+ p2 = AllocMem( sizeof( *p2 ) + ((wcslen( VariableValue ) + 1) * sizeof( WCHAR )) );
+ if (p2 == NULL) {
+ Result = FALSE;
+ break;
+ }
+ RtlInitUnicodeString( &UnicodeString, VariableName );
+ p2->Name = AddName( &UnicodeString );
+ p2->OriginalValue = (PWSTR)(p2+1);
+ wcscpy( p2->OriginalValue, VariableValue );
+ InsertTailList( &p1->VariableReferencesListHead, &p2->Entry );
+
+ VariableName = VariableValue;
+ while (*VariableName++ != UNICODE_NULL) {
+ }
+ }
+
+ Next = Next->Flink;
+ }
+
+
+ if (Result) {
+ *ReturnedReference = p;
+ }
+ else {
+ DestroyIniFileReference( p );
+ }
+
+ return Result;
+}
+
+
+BOOLEAN
+DestroyIniFileReference(
+ PINI_FILE_REFERENCE p
+ )
+{
+ PLIST_ENTRY Next, Head;
+ PINI_SECTION_REFERENCE p1;
+
+ Head = &p->SectionReferencesListHead;
+ Next = Head->Flink;
+ while (Head != Next) {
+ p1 = CONTAINING_RECORD( Next, INI_SECTION_REFERENCE, Entry );
+ Next = Next->Flink;
+ DestroyIniSectionReference( p1 );
+ }
+
+ RemoveEntryList( &p->Entry );
+ FreeMem( &p );
+ return TRUE;
+}
+
+
+BOOLEAN
+DestroyIniSectionReference(
+ PINI_SECTION_REFERENCE p1
+ )
+{
+ PLIST_ENTRY Next, Head;
+ PINI_VARIABLE_REFERENCE p2;
+
+ Head = &p1->VariableReferencesListHead;
+ Next = Head->Flink;
+ while (Head != Next) {
+ p2 = CONTAINING_RECORD( Next, INI_VARIABLE_REFERENCE, Entry );
+ Next = Next->Flink;
+ DestroyIniVariableReference( p2 );
+ }
+
+ RemoveEntryList( &p1->Entry );
+ FreeMem( &p1 );
+ return TRUE;
+}
+
+
+BOOLEAN
+DestroyIniVariableReference(
+ PINI_VARIABLE_REFERENCE p2
+ )
+{
+ RemoveEntryList( &p2->Entry );
+ FreeMem( &p2 );
+ return TRUE;
+}
+
+
+PINI_FILE_REFERENCE
+FindIniFileReference(
+ PWSTR Name
+ )
+{
+ PINI_FILE_REFERENCE p;
+ PLIST_ENTRY Next, Head;
+
+ Head = &IniReferenceListHead;
+ Next = Head->Flink;
+ while (Head != Next) {
+ p = CONTAINING_RECORD( Next, INI_FILE_REFERENCE, Entry );
+ if (p->Name == Name) {
+ return p;
+ }
+
+ Next = Next->Flink;
+ }
+
+ return NULL;
+}
+
+PINI_SECTION_REFERENCE
+FindIniSectionReference(
+ PINI_FILE_REFERENCE p,
+ PWSTR Name,
+ BOOLEAN CreateOkay
+ )
+{
+ PLIST_ENTRY Next, Head;
+ PINI_SECTION_REFERENCE p1;
+
+ Head = &p->SectionReferencesListHead;
+ Next = Head->Flink;
+ while (Head != Next) {
+ p1 = CONTAINING_RECORD( Next, INI_SECTION_REFERENCE, Entry );
+ if (p1->Name == Name) {
+ return p1;
+ }
+
+ Next = Next->Flink;
+ }
+
+ if (CreateOkay) {
+ p1 = AllocMem( sizeof( *p1 ) );
+ if (p1 != NULL) {
+ p1->Created = TRUE;
+ p1->Name = Name;
+ InitializeListHead( &p1->VariableReferencesListHead );
+ InsertTailList( &p->SectionReferencesListHead, &p1->Entry );
+ }
+ }
+ else {
+ p1 = NULL;
+ }
+
+ return p1;
+}
+
+
+PINI_VARIABLE_REFERENCE
+FindIniVariableReference(
+ PINI_SECTION_REFERENCE p1,
+ PWSTR Name,
+ BOOLEAN CreateOkay
+ )
+{
+ PLIST_ENTRY Next, Head;
+ PINI_VARIABLE_REFERENCE p2;
+
+ Head = &p1->VariableReferencesListHead;
+ Next = Head->Flink;
+ while (Head != Next) {
+ p2 = CONTAINING_RECORD( Next, INI_VARIABLE_REFERENCE, Entry );
+ if (p2->Name == Name) {
+ return p2;
+ }
+
+ Next = Next->Flink;
+ }
+
+ if (CreateOkay) {
+ p2 = AllocMem( sizeof( *p2 ) );
+ if (p2 != NULL) {
+ p2->Created = TRUE;
+ p2->Name = Name;
+ InsertTailList( &p1->VariableReferencesListHead, &p2->Entry );
+ }
+ }
+ else {
+ p2 = NULL;
+ }
+
+ return p2;
+}
+
+
+VOID
+DumpIniFileReferenceList(
+ FILE *LogFile
+ )
+{
+ PINI_FILE_REFERENCE p;
+ PINI_SECTION_REFERENCE p1;
+ PINI_VARIABLE_REFERENCE p2;
+ PLIST_ENTRY Head, Next;
+ PLIST_ENTRY Head1, Next1;
+ PLIST_ENTRY Head2, Next2;
+ POFFSET Variables;
+ POFFSET Sections;
+
+
+ Head = &IniReferenceListHead;
+ Next = Head->Blink;
+ while (Head != Next) {
+ p = CONTAINING_RECORD( Next, INI_FILE_REFERENCE, Entry );
+ Sections = 0;
+ Head1 = &p->SectionReferencesListHead;
+ Next1 = Head1->Blink;
+ while (Head1 != Next1) {
+ p1 = CONTAINING_RECORD( Next1, INI_SECTION_REFERENCE, Entry );
+ Variables = 0;
+ Head2 = &p1->VariableReferencesListHead;
+ Next2 = Head2->Blink;
+ while (Head2 != Next2) {
+ p2 = CONTAINING_RECORD( Next2, INI_VARIABLE_REFERENCE, Entry );
+ if (p2->Created || p2->Deleted || p2->Modified) {
+ ImlAddIniVariableRecord( pImlNew,
+ p2->Created ? CreateNewVariable :
+ p2->Deleted ? DeleteOldVariable : ModifyOldVariable,
+ p2->Name,
+ p2->OriginalValue,
+ p2->Value,
+ &Variables
+ );
+ }
+ Next2 = Next2->Blink;
+ }
+
+ if (Variables != 0) {
+ ImlAddIniSectionRecord( pImlNew,
+ p1->Created ? CreateNewSection :
+ p1->Deleted ? DeleteOldSection : ModifySectionVariables,
+ p1->Name,
+ Variables,
+ &Sections
+ );
+ }
+
+ Next1 = Next1->Blink;
+ }
+
+ if (Sections != 0) {
+ ImlAddIniRecord( pImlNew,
+ p->Created ? CreateNewIniFile : ModifyOldIniFile,
+ p->Name,
+ &p->BackupLastWriteTime,
+ Sections
+ );
+ }
+
+ Next = Next->Blink;
+ }
+
+ return;
+}
diff --git a/private/sdktools/instaler/init.c b/private/sdktools/instaler/init.c
new file mode 100644
index 000000000..5a9a5c170
--- /dev/null
+++ b/private/sdktools/instaler/init.c
@@ -0,0 +1,770 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ init.c
+
+Abstract:
+
+ This is the initialization module for the INSTALER program.
+
+Author:
+
+ Steve Wood (stevewo) 09-Aug-1994
+
+Revision History:
+
+--*/
+
+#include "instaler.h"
+
+#if TRACE_ENABLED
+ULONG EnabledTraceEvents = DBG_MASK_INTERNALERROR |
+ DBG_MASK_MEMORYERROR;
+#endif
+
+MODULE_INFO ModuleInfo[ MAXIMUM_MODULE_INDEX ] = {
+ {L"NTDLL", NULL},
+ {L"KERNEL32", NULL},
+ {L"ADVAPI32", NULL}
+};
+
+
+
+API_INFO ApiInfo[ MAXIMUM_API_INDEX ] = {
+ {NTDLL_MODULE_INDEX, "NtCreateFile", NULL, NtCreateFileHandler , sizeof( CREATEFILE_PARAMETERS ) , sizeof( NTSTATUS )},
+ {NTDLL_MODULE_INDEX, "NtOpenFile", NULL, NtOpenFileHandler , sizeof( OPENFILE_PARAMETERS ) , sizeof( NTSTATUS )},
+ {NTDLL_MODULE_INDEX, "NtDeleteFile", NULL, NtDeleteFileHandler , sizeof( DELETEFILE_PARAMETERS ) , sizeof( NTSTATUS )},
+ {NTDLL_MODULE_INDEX, "NtSetInformationFile", NULL, NtSetInformationFileHandler , sizeof( SETINFORMATIONFILE_PARAMETERS ) , sizeof( NTSTATUS )},
+ {NTDLL_MODULE_INDEX, "NtQueryAttributesFile", NULL, NtQueryAttributesFileHandler , sizeof( QUERYATTRIBUTESFILE_PARAMETERS ) , sizeof( NTSTATUS )},
+ {NTDLL_MODULE_INDEX, "NtQueryDirectoryFile", NULL, NtQueryDirectoryFileHandler , sizeof( QUERYDIRECTORYFILE_PARAMETERS ) , sizeof( NTSTATUS )},
+ {NTDLL_MODULE_INDEX, "NtCreateKey", NULL, NtCreateKeyHandler , sizeof( CREATEKEY_PARAMETERS ) , sizeof( NTSTATUS )},
+ {NTDLL_MODULE_INDEX, "NtOpenKey", NULL, NtOpenKeyHandler , sizeof( OPENKEY_PARAMETERS ) , sizeof( NTSTATUS )},
+ {NTDLL_MODULE_INDEX, "NtDeleteKey", NULL, NtDeleteKeyHandler , sizeof( DELETEKEY_PARAMETERS ) , sizeof( NTSTATUS )},
+ {NTDLL_MODULE_INDEX, "NtSetValueKey", NULL, NtSetValueKeyHandler , sizeof( SETVALUEKEY_PARAMETERS ) , sizeof( NTSTATUS )},
+ {NTDLL_MODULE_INDEX, "NtDeleteValueKey", NULL, NtDeleteValueKeyHandler , sizeof( DELETEVALUEKEY_PARAMETERS ) , sizeof( NTSTATUS )},
+ {NTDLL_MODULE_INDEX, "NtClose", NULL, NtCloseHandleHandler , sizeof( CLOSEHANDLE_PARAMETERS ) , sizeof( NTSTATUS )},
+ {KERNEL32_MODULE_INDEX, "GetVersion", NULL, GetVersionHandler , 0 , sizeof( DWORD )},
+ {KERNEL32_MODULE_INDEX, "GetVersionExW", NULL, GetVersionExWHandler , sizeof( GETVERSIONEXW_PARAMETERS ) , sizeof( BOOL )},
+ {KERNEL32_MODULE_INDEX, "SetCurrentDirectoryA", NULL, SetCurrentDirectoryAHandler , sizeof( SETCURRENTDIRECTORYA_PARAMETERS ) , sizeof( BOOL )},
+ {KERNEL32_MODULE_INDEX, "SetCurrentDirectoryW", NULL, SetCurrentDirectoryWHandler , sizeof( SETCURRENTDIRECTORYW_PARAMETERS ) , sizeof( BOOL )},
+ {KERNEL32_MODULE_INDEX, "WritePrivateProfileStringA", NULL, WritePrivateProfileStringAHandler , sizeof( WRITEPRIVATEPROFILESTRINGA_PARAMETERS ) , sizeof( DWORD )},
+ {KERNEL32_MODULE_INDEX, "WritePrivateProfileStringW", NULL, WritePrivateProfileStringWHandler , sizeof( WRITEPRIVATEPROFILESTRINGW_PARAMETERS ) , sizeof( DWORD )},
+ {KERNEL32_MODULE_INDEX, "WritePrivateProfileSectionA", NULL, WritePrivateProfileSectionAHandler, sizeof( WRITEPRIVATEPROFILESECTIONA_PARAMETERS ) , sizeof( DWORD )},
+ {KERNEL32_MODULE_INDEX, "WritePrivateProfileSectionW", NULL, WritePrivateProfileSectionWHandler, sizeof( WRITEPRIVATEPROFILESECTIONW_PARAMETERS ) , sizeof( DWORD )},
+ {KERNEL32_MODULE_INDEX, "GetPrivateProfileStringW", NULL, NULL, 0, sizeof( DWORD )},
+ {KERNEL32_MODULE_INDEX, "GetPrivateProfileStringA", NULL, NULL, 0, sizeof( DWORD )},
+ {KERNEL32_MODULE_INDEX, "GetPrivateProfileSectionW", NULL, NULL, 0, sizeof( DWORD )},
+ {KERNEL32_MODULE_INDEX, "GetPrivateProfileSectionA", NULL, NULL, 0, sizeof( DWORD )},
+ {ADVAPI32_MODULE_INDEX, "RegConnectRegistryW", NULL, RegConnectRegistryWHandler , sizeof( REGCONNECTREGISTRYW_PARAMETERS ) , sizeof( DWORD )}
+};
+
+BOOLEAN
+LoadApplicationForDebug(
+ PWSTR CommandLine
+ );
+
+BOOLEAN
+LoadDriveLetterDefinitions( VOID );
+
+WCHAR WindowsDirectoryBuffer[ MAX_PATH ];
+UNICODE_STRING WindowsDirectory;
+
+BOOLEAN
+InitializeInstaler(
+ int argc,
+ char *argv[]
+ )
+{
+ UINT ModuleIndex, ApiIndex;
+ PWSTR CommandLine;
+ UINT i;
+ LPSTR s;
+ UCHAR c;
+ SYSTEM_INFO SystemInfo;
+ BOOLEAN ProcessingDebugSwitch;
+
+ InitCommonCode( "INSTALER",
+ "[-9] [-r] [-dAE] CommandLine...",
+ "-9 specifies that GetVersion should lie and tell application\n"
+ " that it is running on Windows 95\n"
+ "-r specifies that attempts to do a wildcard scan of the root\n"
+ " directory of a drive should fail.\n"
+ "-d specifies one or more debugging message options\n"
+ " A - shows all errors\n"
+ " E - shows all debug events\n"
+ " C - shows all create API calls\n"
+ );
+
+ AppHeap = GetProcessHeap();
+ InstalerModuleHandle = GetModuleHandle( NULL );
+
+ GetWindowsDirectoryW( WindowsDirectoryBuffer, MAX_PATH );
+ RtlInitUnicodeString( &WindowsDirectory, WindowsDirectoryBuffer );
+
+ GetSystemInfo( &SystemInfo );
+
+ TemporaryBufferLengthGrowth = SystemInfo.dwPageSize;
+ TemporaryBufferMaximumLength = ((128 * 1024) + SystemInfo.dwAllocationGranularity - 1) &
+ ~SystemInfo.dwAllocationGranularity;
+ TemporaryBuffer = VirtualAlloc( NULL,
+ TemporaryBufferMaximumLength,
+ MEM_RESERVE,
+ PAGE_READWRITE
+ );
+ if (TemporaryBuffer == NULL) {
+ return FALSE;
+ }
+
+ InitializeListHead( &ProcessListHead );
+ InitializeListHead( &FileReferenceListHead );
+ InitializeListHead( &KeyReferenceListHead );
+ InitializeListHead( &IniReferenceListHead );
+ InitializeCriticalSection( &BreakTable );
+
+ //
+ // Loop over the table of modules and make sure each is loaded in our
+ // address space so we can access their export tables.
+ //
+ for (ModuleIndex=0; ModuleIndex<MAXIMUM_MODULE_INDEX; ModuleIndex++) {
+ ModuleInfo[ ModuleIndex ].ModuleHandle =
+ GetModuleHandle( ModuleInfo[ ModuleIndex ].ModuleName );
+
+ if (ModuleInfo[ ModuleIndex ].ModuleHandle == NULL) {
+ //
+ // Bail if any are missing.
+ //
+ DeclareError( INSTALER_MISSING_MODULE,
+ GetLastError(),
+ ModuleInfo[ ModuleIndex ].ModuleName
+ );
+ return FALSE;
+ }
+ }
+
+ //
+ // Now loop over the table of entry points that we care about and
+ // get the address of each entry point from the
+
+ for (ApiIndex=0; ApiIndex<MAXIMUM_API_INDEX; ApiIndex++) {
+ ModuleIndex = ApiInfo[ ApiIndex ].ModuleIndex;
+
+ ApiInfo[ ApiIndex ].EntryPointAddress =
+ LookupEntryPointInIAT( ModuleInfo[ ModuleIndex ].ModuleHandle,
+ ApiInfo[ ApiIndex ].EntryPointName
+ );
+
+ if (ApiInfo[ ApiIndex ].EntryPointAddress == NULL) {
+ //
+ // Bail if we are unable to get the address of any of them
+ //
+ DeclareError( INSTALER_MISSING_ENTRYPOINT,
+ GetLastError(),
+ ApiInfo[ ApiIndex ].EntryPointName,
+ ModuleInfo[ ModuleIndex ].ModuleName
+ );
+ return FALSE;
+ }
+ }
+
+ CommandLine = NULL;
+ ProcessingDebugSwitch = FALSE;
+ AskUserOnce = FALSE;
+ while (--argc) {
+ s = *++argv;
+ if (*s == '-' || *s == '/') {
+ while (*++s) {
+ switch( tolower( *s ) ) {
+ case '9':
+ DefaultGetVersionToWin95 = TRUE;
+ break;
+
+ case 'r':
+ FailAllScansOfRootDirectories = TRUE;
+ break;
+#if TRACE_ENABLED
+ case 'd':
+ ProcessingDebugSwitch = TRUE;
+ break;
+
+ case 'a':
+ if (ProcessingDebugSwitch) {
+ EnabledTraceEvents = 0xFFFFFFFF;
+ break;
+ }
+ case 'e':
+ if (ProcessingDebugSwitch) {
+ EnabledTraceEvents |= DBG_MASK_DBGEVENT;
+ break;
+ }
+ case 'c':
+ if (ProcessingDebugSwitch) {
+ EnabledTraceEvents |= DBG_MASK_CREATEEVENT;
+ break;
+ }
+#endif
+ default:
+ if (!ProcessingDebugSwitch) {
+ CommonSwitchProcessing( &argc, &argv, *s );
+ }
+ else {
+ Usage( "Invalid debug switch (-%c)", (ULONG)*s );
+ }
+ break;
+ }
+ }
+
+ ProcessingDebugSwitch = FALSE;
+ }
+ else
+ if (!CommonArgProcessing( &argc, &argv )) {
+ CommandLine = GetCommandLine();
+ while (*CommandLine && *CommandLine <= L' ') {
+ CommandLine += 1;
+ }
+
+ while (*CommandLine && *CommandLine > L' ') {
+ CommandLine += 1;
+ }
+
+ CommandLine = wcsstr( CommandLine, GetArgAsUnicode( s ) );
+ break;
+ }
+ }
+
+ if (ImlPath == NULL) {
+ Usage( "Must specify an installation name as first argument", 0 );
+ }
+
+ if (CommandLine == NULL) {
+ Usage( "Command line missing", 0 );
+ }
+
+ pImlNew = CreateIml( ImlPath, FALSE );
+ if (pImlNew == NULL) {
+ FatalError( "Unable to create %ws (%u)\n",
+ (ULONG)ImlPath,
+ GetLastError()
+ );
+ }
+
+ //
+ // Ready to roll. Cache the drive letter information and then load
+ // the application with the DEBUG option so we can watch what it does
+ //
+ if (!LoadDriveLetterDefinitions() ||
+ !LoadApplicationForDebug( CommandLine )
+ ) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+LoadApplicationForDebug(
+ PWSTR CommandLine
+ )
+{
+ STARTUPINFO StartupInfo;
+ PROCESS_INFORMATION ProcessInformation;
+ PWSTR ImageFilePath;
+ PWSTR ImageFileName;
+ PWSTR CurrentDirectory;
+ PWSTR TemporaryNull;
+ WCHAR TemporaryChar;
+ ULONG Length;
+ WCHAR PathBuffer[ MAX_PATH ];
+
+ TemporaryNull = CommandLine;
+ while(TemporaryChar = *TemporaryNull) {
+ if (TemporaryChar == L' ' || TemporaryChar == L'\t') {
+ *TemporaryNull = UNICODE_NULL;
+ break;
+ }
+
+ TemporaryNull += 1;
+ }
+
+ ImageFilePath = AllocMem( MAX_PATH * sizeof( WCHAR ) );
+ Length = SearchPath( NULL,
+ CommandLine,
+ L".exe",
+ MAX_PATH,
+ ImageFilePath,
+ &ImageFileName
+ );
+ *TemporaryNull = TemporaryChar;
+ if (!Length || Length >= MAX_PATH) {
+ DeclareError( INSTALER_CANT_DEBUG_PROGRAM,
+ GetLastError(),
+ CommandLine
+ );
+ return FALSE;
+ }
+
+ if (ImageFileName != NULL &&
+ ImageFileName > ImageFilePath &&
+ ImageFileName[ -1 ] == L'\\'
+ ) {
+ ImageFileName[ -1 ] = UNICODE_NULL;
+ CurrentDirectory = wcscpy( PathBuffer, ImageFilePath );
+ ImageFileName[ -1 ] = L'\\';
+ SetCurrentDirectory( CurrentDirectory );
+ }
+ else {
+ CurrentDirectory = NULL;
+ }
+
+ memset( &StartupInfo, 0, sizeof( StartupInfo ) );
+ StartupInfo.cb = sizeof(StartupInfo);
+
+ //
+ // Create the process with DEBUG option, as a separate WOW so we only see our
+ // application's damage.
+ //
+ if (!CreateProcess( ImageFilePath,
+ CommandLine,
+ NULL,
+ NULL,
+ FALSE, // No handles to inherit
+ DEBUG_PROCESS |
+ CREATE_SEPARATE_WOW_VDM, // Ignored for 32 bit apps
+ NULL,
+ CurrentDirectory,
+ &StartupInfo,
+ &ProcessInformation
+ )
+ ) {
+ DeclareError( INSTALER_CANT_DEBUG_PROGRAM,
+ GetLastError(),
+ CommandLine
+ );
+ return FALSE;
+ }
+ else {
+ sprintf( (LPSTR)PathBuffer, "%ws%ws.log", InstalerDirectory, InstallationName );
+ InstalerLogFile = fopen( (LPSTR)PathBuffer, "w" );
+
+ //
+ // Will pick up the process and thread handles with the
+ // CREATE_PROCESS_DEBUG_EVENT and CREATE_PROCESS_THREAD_EVENT
+ //
+ return TRUE;
+ }
+}
+
+
+PVOID
+LookupEntryPointInIAT(
+ HMODULE ModuleHandle,
+ PCHAR EntryPointName
+ )
+{
+ PIMAGE_EXPORT_DIRECTORY Exports;
+ PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTableEntries;
+ DWORD NumberOfFunctionTableEntries;
+ ULONG ExportSize, FunctionTableSize;
+ PCHAR *EntryPointNames;
+ PULONG EntryPointAddresses;
+ PUSHORT EntryPointOrdinals;
+ PVOID EntryPointAddress;
+ ULONG i;
+
+ Exports = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData( ModuleHandle,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_EXPORT,
+ &ExportSize
+ );
+ if (Exports == NULL) {
+ return NULL;
+ }
+
+ FunctionTableEntries = (PIMAGE_RUNTIME_FUNCTION_ENTRY)
+ RtlImageDirectoryEntryToData( ModuleHandle,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_EXCEPTION,
+ &FunctionTableSize
+ );
+ if (FunctionTableEntries != NULL) {
+ NumberOfFunctionTableEntries = FunctionTableSize / sizeof( IMAGE_RUNTIME_FUNCTION_ENTRY );
+ }
+ else {
+ NumberOfFunctionTableEntries = 0;
+ }
+
+ EntryPointNames = (PCHAR *)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfNames);
+ EntryPointOrdinals = (PUSHORT)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfNameOrdinals);
+ EntryPointAddresses = (PULONG)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfFunctions);
+ for (i=0; i<Exports->NumberOfNames; i++) {
+ EntryPointAddress = (PVOID)((PCHAR)ModuleHandle +
+ EntryPointAddresses[ EntryPointOrdinals[ i ] ]
+ );
+ if ((ULONG)EntryPointAddress > (ULONG)Exports &&
+ (ULONG)EntryPointAddress < ((ULONG)Exports + ExportSize)
+ ) {
+ //
+ // Skip this... It's a forwarded reference
+ }
+ else
+ if (!_stricmp( EntryPointName, (PCHAR)ModuleHandle + (ULONG)EntryPointNames[ i ] )) {
+ return GetAddressForEntryPointBreakpoint( EntryPointAddress,
+ NumberOfFunctionTableEntries,
+ FunctionTableEntries
+ );
+ }
+ }
+
+ DbgEvent( INTERNALERROR, ( "Unable to find entry point '%s' in module at %x\n", EntryPointName, ModuleHandle ) );
+ return NULL;
+}
+
+
+PVOID
+GetAddressForEntryPointBreakpoint(
+ PVOID EntryPointAddress,
+ DWORD NumberOfFunctionTableEntries OPTIONAL,
+ PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTableEntries OPTIONAL
+ )
+{
+ PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry;
+ PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTable;
+ LONG High;
+ LONG Low;
+ LONG Middle;
+
+ //
+ // If no function table, then okay to set breakpoint at exported
+ // address.
+ //
+ if (NumberOfFunctionTableEntries == 0) {
+ return EntryPointAddress;
+ }
+
+ //
+ // Initialize search indicies.
+ //
+ Low = 0;
+ High = NumberOfFunctionTableEntries - 1;
+ FunctionTable = FunctionTableEntries;
+
+ //
+ // Perform binary search on the function table for a function table
+ // entry that subsumes the specified PC.
+ //
+ while (High >= Low) {
+ //
+ // Compute next probe index and test entry. If the specified PC
+ // is greater than of equal to the beginning address and less
+ // than the ending address of the function table entry, then
+ // return the address of the function table entry. Otherwise,
+ // continue the search.
+ //
+ Middle = (Low + High) >> 1;
+ FunctionEntry = &FunctionTable[Middle];
+ if ((ULONG)EntryPointAddress < FunctionEntry->BeginAddress) {
+ High = Middle - 1;
+ }
+ else
+ if ((ULONG)EntryPointAddress >= FunctionEntry->EndAddress) {
+ Low = Middle + 1;
+ }
+ else {
+ if (((ULONG)EntryPointAddress >= FunctionEntry->BeginAddress) &&
+ ((ULONG)EntryPointAddress < FunctionEntry->PrologEndAddress)) {
+ return (PVOID)FunctionEntry->PrologEndAddress;
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ //
+ // A function table entry for the specified PC was not found. Just use
+ // the exported address and hope for the best.
+ //
+ return EntryPointAddress;
+}
+
+
+BOOLEAN
+LoadDriveLetterDefinitions( VOID )
+{
+ PVOID Buffer;
+ ULONG BufferSize;
+ ULONG cchDeviceName;
+ ULONG cch;
+ ULONG cchTargetPath;
+ PWSTR DeviceName;
+ PWSTR TargetPath;
+ ULONG DriveLetterIndex;
+ ULONG cb;
+ PDRIVE_LETTER_INFO p;
+
+ RtlInitUnicodeString( &AltDriveLetterPrefix, L"\\DosDevices\\" );
+ RtlInitUnicodeString( &DriveLetterPrefix, L"\\??\\" );
+ RtlInitUnicodeString( &UNCPrefix, L"UNC\\" );
+
+ BufferSize = 0x1000;
+ Buffer = VirtualAlloc( NULL, BufferSize, MEM_COMMIT, PAGE_READWRITE );
+ if (Buffer == NULL) {
+ DbgEvent( INTERNALERROR, ( "VirtualAlloc( %0x8 ) failed (%u)\n",
+ BufferSize,
+ GetLastError()
+ )
+ );
+
+ return FALSE;
+ }
+
+ cchTargetPath = BufferSize / sizeof( WCHAR );
+ cchDeviceName = QueryDosDeviceW( NULL,
+ Buffer,
+ cchTargetPath
+ );
+ if (cchDeviceName == 0) {
+ DbgEvent( INTERNALERROR, ( "QueryDosDeviceW( NULL ) failed (%u)\n",
+ GetLastError()
+ )
+ );
+ return FALSE;
+ }
+
+ cchTargetPath -= (cchDeviceName + 2);
+ DeviceName = Buffer;
+ TargetPath = DeviceName + 2 + cchDeviceName;
+ while (*DeviceName) {
+ if (wcslen( DeviceName ) == 2 &&
+ DeviceName[ 1 ] == L':' &&
+ _wcsupr( DeviceName ) &&
+ DeviceName[ 0 ] >= L'@' &&
+ DeviceName[ 0 ] <= L'_'
+ ) {
+ cch = QueryDosDeviceW( DeviceName, TargetPath, cchTargetPath );
+ if (cch == 0) {
+ DbgEvent( INTERNALERROR, ( "QueryDosDeviceW( %ws ) failed (%u)\n",
+ DeviceName,
+ GetLastError()
+ )
+ );
+ return FALSE;
+ }
+
+ DriveLetterIndex = DeviceName[ 0 ] - L'@';
+ p = &DriveLetters[ DriveLetterIndex ];
+ p->DriveLetter = (WCHAR)(L'@' + DriveLetterIndex);
+ cb = (cch+1) * sizeof( WCHAR );
+ p->NtLinkTarget.Buffer = AllocMem( cb );
+ if (p->NtLinkTarget.Buffer == NULL) {
+ return FALSE;
+ }
+ MoveMemory( p->NtLinkTarget.Buffer, TargetPath, cb );
+ p->NtLinkTarget.Length = (USHORT)( cb - sizeof( WCHAR ));
+ p->NtLinkTarget.MaximumLength = (USHORT)cb;
+ p->DriveLetterValid = TRUE;
+ }
+
+ while (*DeviceName++)
+ ;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+IsDriveLetterPath(
+ PUNICODE_STRING Path
+ )
+{
+ ULONG DriveLetterIndex;
+ PDRIVE_LETTER_INFO p;
+ PUNICODE_STRING Prefix;
+
+ if (RtlPrefixUnicodeString( Prefix = &DriveLetterPrefix, Path, TRUE ) ||
+ RtlPrefixUnicodeString( Prefix = &AltDriveLetterPrefix, Path, TRUE )
+ ) {
+ Path->Length -= Prefix->Length;
+ RtlMoveMemory( Path->Buffer,
+ (PCHAR)(Path->Buffer) + Prefix->Length,
+ Path->Length + sizeof( UNICODE_NULL )
+ );
+ if (RtlPrefixUnicodeString( &UNCPrefix, Path, TRUE )) {
+ Path->Length -= UNCPrefix.Length;
+ Path->Buffer[0] = L'\\';
+ Path->Buffer[1] = L'\\';
+ RtlMoveMemory( &Path->Buffer[2],
+ (PCHAR)(Path->Buffer) + UNCPrefix.Length,
+ Path->Length + sizeof( UNICODE_NULL )
+ );
+ Path->Length += 2 * sizeof( WCHAR );
+ }
+
+ return TRUE;
+ }
+
+ for (DriveLetterIndex=0, p = &DriveLetters[ DriveLetterIndex ];
+ DriveLetterIndex<32;
+ DriveLetterIndex++, p++
+ ) {
+ if (p->DriveLetterValid &&
+ RtlPrefixUnicodeString( &p->NtLinkTarget, Path, TRUE )
+ ) {
+ Path->Length -= p->NtLinkTarget.Length;
+ RtlMoveMemory( &Path->Buffer[ 2 ],
+ (PCHAR)(Path->Buffer) + p->NtLinkTarget.Length,
+ Path->Length + sizeof( UNICODE_NULL )
+ );
+ Path->Buffer[ 0 ] = p->DriveLetter;
+ Path->Buffer[ 1 ] = L':';
+ Path->Length += 2;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+VOID
+TrimTemporaryBuffer( VOID )
+{
+ PVOID CommitAddress;
+
+ if (TemporaryBufferLength == 0) {
+ return;
+ }
+
+ if (VirtualFree( (PCHAR)TemporaryBuffer,
+ TemporaryBufferLength,
+ MEM_DECOMMIT
+ )
+ ) {
+ TemporaryBufferLength = 0;
+ }
+
+ return;
+}
+
+BOOLEAN
+GrowTemporaryBuffer(
+ ULONG NeededSize
+ )
+{
+ PVOID CommitAddress;
+
+ if (NeededSize <= TemporaryBufferLength) {
+ return TRUE;
+ }
+
+ if (TemporaryBufferLength == TemporaryBufferMaximumLength) {
+ return FALSE;
+ }
+
+ CommitAddress = VirtualAlloc( (PCHAR)TemporaryBuffer + TemporaryBufferLength,
+ NeededSize - TemporaryBufferLength,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (CommitAddress == NULL) {
+ DbgEvent( INTERNALERROR, ( "VirtualAlloc( %0x8 ) failed (%u)\n",
+ NeededSize - TemporaryBufferLength,
+ GetLastError()
+ )
+ );
+
+ return FALSE;
+ }
+
+ TemporaryBufferLength += TemporaryBufferLengthGrowth;
+ return TRUE;
+}
+
+ULONG
+FillTemporaryBuffer(
+ PPROCESS_INFO Process,
+ PVOID Address,
+ BOOLEAN Unicode,
+ BOOLEAN DoubleNullTermination
+ )
+{
+ ULONG BytesRead;
+ ULONG TotalBytesRead;
+ PVOID Source;
+ PVOID Destination;
+ LPSTR s1;
+ PWSTR s2;
+ BOOLEAN MoreToRead;
+ BOOLEAN SeenOneNull;
+
+ TotalBytesRead = 0;
+ Destination = (PCHAR)TemporaryBuffer;
+ MoreToRead = TRUE;
+ SeenOneNull = FALSE;
+ while (MoreToRead) {
+ TotalBytesRead += TemporaryBufferLengthGrowth;
+ if (!GrowTemporaryBuffer( TotalBytesRead )) {
+ return 0;
+ }
+
+ Source = Destination;
+ if (!ReadProcessMemory( Process->Handle,
+ Address,
+ Destination,
+ TemporaryBufferLengthGrowth,
+ &BytesRead
+ )
+ ) {
+ if (BytesRead == 0) {
+ MoreToRead = FALSE;
+ }
+ else {
+ return 0;
+ }
+ }
+
+ Destination = (PCHAR)Destination + TotalBytesRead;
+ if (Unicode) {
+ s2 = (PWSTR)Source;
+ while (s2 < (PWSTR)Destination) {
+ if (*s2 == UNICODE_NULL) {
+ if (SeenOneNull || !DoubleNullTermination) {
+ return (PCHAR)s2 - (PCHAR)TemporaryBuffer;
+ }
+ else {
+ SeenOneNull = TRUE;
+ }
+ }
+ else {
+ SeenOneNull = FALSE;
+ }
+
+ s2++;
+ }
+ }
+ else {
+ s1 = (LPSTR)Source;
+ while (s1 < (LPSTR)Destination) {
+ if (*s1 == '\0') {
+ if (SeenOneNull || !DoubleNullTermination) {
+ return (PCHAR)s1 - (PCHAR)TemporaryBuffer;
+ }
+ else {
+ SeenOneNull = TRUE;
+ }
+ }
+ else {
+ SeenOneNull = FALSE;
+ }
+
+ s1++;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/private/sdktools/instaler/instaler.c b/private/sdktools/instaler/instaler.c
new file mode 100644
index 000000000..e3b73297c
--- /dev/null
+++ b/private/sdktools/instaler/instaler.c
@@ -0,0 +1,159 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ instaler.c
+
+Abstract:
+
+ USAGE: instaler "setup/install command line"
+
+ Main source file for the INSTALER application. This application is
+ intended for use as a wrapper application around another
+ application's setup/install program. This program uses the Debugger
+ API calls to set breakpoints in the setup/install program and its
+ descendents at all calls to NT/Win32 API calls that modify the
+ current system. The API calls that are tracked include:
+
+ NtCreateFile
+ NtDeleteFile
+ NtSetInformationFile (FileRenameInformation, FileDispositionInformation)
+ NtCreateKey
+ NtOpenKey
+ NtDeleteKey
+ NtSetValueKey
+ NtDeleteValueKey
+ GetVersion
+ GetVersionExW
+ WriteProfileStringA/W
+ WritePrivateProfileStringA/W
+ WriteProfileSectionA/W
+ WritePrivateProfileSectionA/W
+ RegConnectRegistryW
+
+ This program sets breakpoints around each of the above API entry
+ points. At the breakpoint at the entry to an API call, the
+ parameters are inspected and if the call is going to overwrite some
+ state (e.g. create/open a file/key for write access, store a new
+ key value or profile string), then this program will save the old
+ state in a temporary directory before letting the API call proceed.
+ At the exit from the API call, it will determine if the operation
+ was successful. If not, the saved state will be discarded. If
+ successful, it will keep the saved state for when the application
+ setup/install program completes. Part of the Create/Open API
+ tracking is the maintenance of a handle database so that relative opens
+ can be handled correctly with the full path.
+
+Author:
+
+ Steve Wood (stevewo) 09-Aug-1994
+
+--*/
+
+#include "instaler.h"
+
+BOOLEAN
+SortReferenceList(
+ PLIST_ENTRY OldHead,
+ ULONG NumberOfEntriesInList
+ );
+
+int
+_CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ if (!InitializeInstaler( argc, argv )) {
+ ExitProcess( 1 );
+ }
+ else {
+ StartProcessTickCount = GetTickCount();
+ DebugEventLoop();
+ printf( "Creating %ws\n", ImlPath );
+ SortReferenceList( &FileReferenceListHead, NumberOfFileReferences );
+ SortReferenceList( &KeyReferenceListHead, NumberOfKeyReferences );
+ SortReferenceList( &IniReferenceListHead, NumberOfIniReferences );
+ DumpFileReferenceList( InstalerLogFile );
+ DumpKeyReferenceList( InstalerLogFile );
+ DumpIniFileReferenceList( InstalerLogFile );
+ DumpNameDataBase( InstalerLogFile );
+ CloseIml( pImlNew );
+ fclose( InstalerLogFile );
+ ExitProcess( 0 );
+ }
+
+ return 0;
+}
+
+
+typedef struct _GENERIC_REFERENCE {
+ LIST_ENTRY Entry;
+ PWSTR Name;
+} GENERIC_REFERENCE, *PGENERIC_REFERENCE;
+
+
+int
+_CRTAPI1
+CompareReferences(
+ const void *Reference1,
+ const void *Reference2
+ )
+{
+ PGENERIC_REFERENCE p1 = *(PGENERIC_REFERENCE *)Reference1;
+ PGENERIC_REFERENCE p2 = *(PGENERIC_REFERENCE *)Reference2;
+
+ return _wcsicmp( p1->Name, p2->Name );
+}
+
+
+BOOLEAN
+SortReferenceList(
+ PLIST_ENTRY Head,
+ ULONG NumberOfEntriesInList
+ )
+{
+ PGENERIC_REFERENCE p, *SortedArray;
+ PLIST_ENTRY Next;
+ ULONG i;
+
+ if (NumberOfEntriesInList == 0) {
+ return TRUE;
+ }
+
+ SortedArray = AllocMem( NumberOfEntriesInList * sizeof( *SortedArray ) );
+ if (SortedArray == NULL) {
+ return FALSE;
+ }
+
+ Next = Head->Flink;
+ i = 0;
+ while (Head != Next) {
+ p = CONTAINING_RECORD( Next, GENERIC_REFERENCE, Entry );
+ if (i >= NumberOfEntriesInList) {
+ break;
+ }
+
+ SortedArray[ i++ ] = p;
+ Next = Next->Flink;
+ }
+
+ qsort( (void *)SortedArray,
+ NumberOfEntriesInList,
+ sizeof( *SortedArray ),
+ CompareReferences
+ );
+
+ InitializeListHead( Head );
+ for (i=0; i<NumberOfEntriesInList; i++) {
+ p = SortedArray[ i ];
+ InsertTailList( Head, &p->Entry );
+ }
+
+ FreeMem( (PVOID *)&SortedArray );
+
+ return TRUE;
+}
diff --git a/private/sdktools/instaler/instaler.h b/private/sdktools/instaler/instaler.h
new file mode 100644
index 000000000..5ae44b683
--- /dev/null
+++ b/private/sdktools/instaler/instaler.h
@@ -0,0 +1,1165 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ instaler.h
+
+Abstract:
+
+ Main include file for the INSTALER application.
+
+Author:
+
+ Steve Wood (stevewo) 09-Aug-1994
+
+Revision History:
+
+--*/
+
+#ifdef RC_INVOKED
+#include <windows.h>
+
+#define FILEBMP 500
+#define DIRBMP 501
+
+#else
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntdbg.h>
+#include <windows.h>
+#include <vdmdbg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "errormsg.h"
+
+//
+// Data structures and entry points in ERROR.C
+//
+
+#define TRACE_ENABLED 1
+
+#if TRACE_ENABLED
+extern ULONG EnabledTraceEvents;
+
+#define DBG_MASK_DBGEVENT 0x00000001
+#define DBG_MASK_INTERNALERROR 0x00000002
+#define DBG_MASK_MEMORYERROR 0x00000004
+#define DBG_MASK_CREATEEVENT 0x00000008
+
+VOID
+TraceDisplay(
+ const char *FormatString,
+ ...
+ );
+
+#define DbgEvent( m, x ) if (EnabledTraceEvents & (DBG_MASK_##m)) TraceDisplay x
+#define TestDbgEvent( m ) (EnabledTraceEvents & (DBG_MASK_##m))
+#else
+#define DbgEvent( m, x )
+#define TestDbgEvent( m ) (FALSE)
+#endif
+
+
+VOID
+CDECL
+DeclareError(
+ UINT ErrorCode,
+ UINT SupplementalErrorCode,
+ ...
+ );
+
+UINT
+CDECL
+AskUser(
+ UINT MessageBoxFlags,
+ UINT MessageId,
+ UINT NumberOfArguments,
+ ...
+ );
+
+
+//
+// Data structures and entry points in EVENT.C
+//
+
+VOID
+CDECL
+LogEvent(
+ UINT MessageId,
+ UINT NumberOfArguments,
+ ...
+ );
+
+//
+// Data structures and entry points in PROCESS.C
+//
+
+#define HANDLE_TYPE_FILE 0
+#define HANDLE_TYPE_KEY 1
+#define HANDLE_TYPE_NONE 2
+
+typedef struct _OPENHANDLE_INFO {
+ LIST_ENTRY Entry;
+ HANDLE Handle;
+ BOOLEAN Inherit;
+ BOOLEAN RootDirectory;
+ USHORT Type;
+ USHORT LengthOfName;
+ PWSTR Name;
+
+ //
+ // Union for handlers that need to store state in a handle during
+ // use.
+ //
+ union {
+ PWSTR QueryName;
+ };
+} OPENHANDLE_INFO, *POPENHANDLE_INFO;
+
+typedef struct _PROCESS_INFO {
+ LIST_ENTRY Entry;
+ LIST_ENTRY ThreadListHead;
+ LIST_ENTRY BreakpointListHead;
+ LIST_ENTRY OpenHandleListHead;
+ DWORD Id;
+ HANDLE Handle;
+ PROCESS_BASIC_INFORMATION ProcessInformation;
+ WCHAR ImageFileName[ 32 ];
+} PROCESS_INFO, *PPROCESS_INFO;
+
+typedef struct _THREAD_INFO {
+ LIST_ENTRY Entry;
+ LIST_ENTRY BreakpointListHead;
+ DWORD Id;
+ HANDLE Handle;
+ PVOID StartAddress;
+ ULONG ModuleIndexCurrentlyIn;
+ BOOLEAN SingleStepExpected;
+ struct _BREAKPOINT_INFO *BreakpointToStepOver;
+} THREAD_INFO, *PTHREAD_INFO;
+
+LIST_ENTRY ProcessListHead;
+
+BOOLEAN
+AddProcess(
+ LPDEBUG_EVENT DebugEvent,
+ PPROCESS_INFO *ReturnedProcess
+ );
+
+BOOLEAN
+DeleteProcess(
+ PPROCESS_INFO Process
+ );
+
+BOOLEAN
+AddThread(
+ LPDEBUG_EVENT DebugEvent,
+ PPROCESS_INFO Process,
+ PTHREAD_INFO *ReturnedThread
+ );
+
+BOOLEAN
+DeleteThread(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ );
+
+PPROCESS_INFO
+FindProcessById(
+ ULONG Id
+ );
+
+BOOLEAN
+FindProcessAndThreadForEvent(
+ LPDEBUG_EVENT DebugEvent,
+ PPROCESS_INFO *ReturnedProcess,
+ PTHREAD_INFO *ReturnedThread
+ );
+
+BOOLEAN
+HandleThreadsForSingleStep(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO ThreadToSingleStep,
+ BOOLEAN SuspendThreads
+ );
+
+BOOLEAN
+ReadMemory(
+ PPROCESS_INFO Process,
+ PVOID Address,
+ PVOID DataRead,
+ ULONG BytesToRead,
+ PCHAR Reason
+ );
+
+BOOLEAN
+WriteMemory(
+ PPROCESS_INFO Process,
+ PVOID Address,
+ PVOID DataToWrite,
+ ULONG BytesToWrite,
+ PCHAR Reason
+ );
+
+PVOID
+AllocMem(
+ ULONG Size
+ );
+
+VOID
+FreeMem(
+ PVOID *p
+ );
+
+
+//
+// Data structures and entry points in MACHINE.C
+//
+
+PVOID BreakpointInstruction;
+ULONG SizeofBreakpointInstruction;
+
+PVOID
+GetAddressForEntryPointBreakpoint(
+ PVOID EntryPointAddress,
+ DWORD NumberOfFunctionTableEntries OPTIONAL,
+ PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTableEntries OPTIONAL
+ );
+
+BOOLEAN
+SkipOverHardcodedBreakpoint(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PVOID BreakpointAddress
+ );
+
+BOOLEAN
+ExtractProcedureParameters(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PULONG ReturnAddress,
+ ULONG SizeOfParameters,
+ PULONG Parameters
+ );
+
+BOOLEAN
+ExtractProcedureReturnValue(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PVOID ReturnValue,
+ ULONG SizeOfReturnValue
+ );
+
+BOOLEAN
+SetProcedureReturnValue(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PVOID ReturnValue,
+ ULONG SizeOfReturnValue
+ );
+
+BOOLEAN
+ForceReturnToCaller(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ ULONG SizeOfParameters,
+ PVOID ReturnAddress,
+ PVOID ReturnValue,
+ ULONG SizeOfReturnValue
+ );
+
+BOOLEAN
+UndoReturnAddressBreakpoint(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ );
+
+BOOLEAN
+BeginSingleStepBreakpoint(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ );
+
+BOOLEAN
+EndSingleStepBreakpoint(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ );
+
+typedef enum _API_ACTION {
+ QueryPath,
+ OpenPath,
+ RenamePath,
+ DeletePath,
+ SetValue,
+ DeleteValue,
+ WriteIniValue,
+ WriteIniSection
+} API_ACTION;
+
+typedef struct _API_SAVED_CALL_STATE {
+ LIST_ENTRY Entry;
+ API_ACTION Action;
+ ULONG Type;
+ PWSTR FullName;
+ PVOID Reference;
+ union {
+ struct {
+ BOOLEAN InheritHandle;
+ BOOLEAN WriteAccessRequested;
+ PHANDLE ResultHandleAddress;
+ } PathOpen;
+ struct {
+ PWSTR NewName;
+ BOOLEAN ReplaceIfExists;
+ } PathRename;
+ struct {
+ PWSTR SectionName;
+ PWSTR VariableName;
+ PWSTR VariableValue;
+ } SetIniValue;
+ struct {
+ PWSTR SectionName;
+ PWSTR SectionValue;
+ } SetIniSection;
+ };
+} API_SAVED_CALL_STATE, *PAPI_SAVED_CALL_STATE;
+
+BOOLEAN
+CreateSavedCallState(
+ PPROCESS_INFO Process,
+ PAPI_SAVED_CALL_STATE SavedCallState,
+ API_ACTION Action,
+ ULONG Type,
+ PWSTR FullName,
+ ...
+ );
+
+VOID
+FreeSavedCallState(
+ PAPI_SAVED_CALL_STATE CallState
+ );
+
+
+
+//
+// Data structures and entry points in HANDLER.C
+//
+
+typedef struct _CREATEFILE_PARAMETERS {
+ PHANDLE FileHandle;
+ ACCESS_MASK DesiredAccess;
+ POBJECT_ATTRIBUTES ObjectAttributes;
+ PIO_STATUS_BLOCK IoStatusBlock;
+ PLARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG ShareAccess;
+ ULONG CreateDisposition;
+ ULONG CreateOptions;
+} CREATEFILE_PARAMETERS, *PCREATEFILE_PARAMETERS;
+
+typedef struct _OPENFILE_PARAMETERS {
+ PHANDLE FileHandle;
+ ACCESS_MASK DesiredAccess;
+ POBJECT_ATTRIBUTES ObjectAttributes;
+ PIO_STATUS_BLOCK IoStatusBlock;
+ ULONG ShareAccess;
+ ULONG OpenOptions;
+} OPENFILE_PARAMETERS, *POPENFILE_PARAMETERS;
+
+typedef struct _DELETEFILE_PARAMETERS {
+ POBJECT_ATTRIBUTES ObjectAttributes;
+} DELETEFILE_PARAMETERS, *PDELETEFILE_PARAMETERS;
+
+typedef struct _SETINFORMATIONFILE_PARAMETERS {
+ HANDLE FileHandle;
+ PIO_STATUS_BLOCK IoStatusBlock;
+ PVOID FileInformation;
+ ULONG Length;
+ FILE_INFORMATION_CLASS FileInformationClass;
+} SETINFORMATIONFILE_PARAMETERS, *PSETINFORMATIONFILE_PARAMETERS;
+
+typedef struct _QUERYATTRIBUTESFILE_PARAMETERS {
+ POBJECT_ATTRIBUTES ObjectAttributes;
+ PFILE_BASIC_INFORMATION FileInformation;
+} QUERYATTRIBUTESFILE_PARAMETERS, *PQUERYATTRIBUTESFILE_PARAMETERS;
+
+typedef struct _QUERYDIRECTORYFILE_PARAMETERS {
+ HANDLE FileHandle;
+ HANDLE Event;
+ PIO_APC_ROUTINE ApcRoutine;
+ PVOID ApcContext;
+ PIO_STATUS_BLOCK IoStatusBlock;
+ PVOID FileInformation;
+ ULONG Length;
+ FILE_INFORMATION_CLASS FileInformationClass;
+ BOOLEAN ReturnSingleEntry;
+ PUNICODE_STRING FileName;
+ BOOLEAN RestartScan;
+} QUERYDIRECTORYFILE_PARAMETERS, *PQUERYDIRECTORYFILE_PARAMETERS;
+
+typedef struct _CREATEKEY_PARAMETERS {
+ PHANDLE KeyHandle;
+ ACCESS_MASK DesiredAccess;
+ POBJECT_ATTRIBUTES ObjectAttributes;
+ ULONG TitleIndex;
+ PUNICODE_STRING Class;
+ ULONG CreateOptions;
+ PULONG Disposition;
+} CREATEKEY_PARAMETERS, *PCREATEKEY_PARAMETERS;
+
+typedef struct _OPENKEY_PARAMETERS {
+ PHANDLE KeyHandle;
+ ACCESS_MASK DesiredAccess;
+ POBJECT_ATTRIBUTES ObjectAttributes;
+} OPENKEY_PARAMETERS, *POPENKEY_PARAMETERS;
+
+typedef struct _DELETEKEY_PARAMETERS {
+ HANDLE KeyHandle;
+} DELETEKEY_PARAMETERS, *PDELETEKEY_PARAMETERS;
+
+typedef struct _SETVALUEKEY_PARAMETERS {
+ HANDLE KeyHandle;
+ PUNICODE_STRING ValueName;
+ ULONG TitleIndex;
+ ULONG Type;
+ PVOID Data;
+ ULONG DataLength;
+} SETVALUEKEY_PARAMETERS, *PSETVALUEKEY_PARAMETERS;
+
+typedef struct _DELETEVALUEKEY_PARAMETERS {
+ HANDLE KeyHandle;
+ PUNICODE_STRING ValueName;
+} DELETEVALUEKEY_PARAMETERS, *PDELETEVALUEKEY_PARAMETERS;
+
+typedef struct _CLOSEHANDLE_PARAMETERS {
+ HANDLE Handle;
+} CLOSEHANDLE_PARAMETERS, *PCLOSEHANDLE_PARAMETERS;
+
+typedef struct _GETVERSIONEXW_PARAMETERS {
+ LPOSVERSIONINFOA lpVersionInformation;
+} GETVERSIONEXW_PARAMETERS, *PGETVERSIONEXW_PARAMETERS;
+
+typedef struct _SETCURRENTDIRECTORYA_PARAMETERS {
+ LPSTR lpPathName;
+} SETCURRENTDIRECTORYA_PARAMETERS, *PSETCURRENTDIRECTORYA_PARAMETERS;
+
+typedef struct _SETCURRENTDIRECTORYW_PARAMETERS {
+ PWSTR lpPathName;
+} SETCURRENTDIRECTORYW_PARAMETERS, *PSETCURRENTDIRECTORYW_PARAMETERS;
+
+typedef struct _WRITEPRIVATEPROFILESTRINGA_PARAMETERS {
+ LPCSTR lpAppName;
+ LPCSTR lpKeyName;
+ LPCSTR lpString;
+ LPCSTR lpFileName;
+} WRITEPRIVATEPROFILESTRINGA_PARAMETERS, *PWRITEPRIVATEPROFILESTRINGA_PARAMETERS;
+
+typedef struct _WRITEPRIVATEPROFILESTRINGW_PARAMETERS {
+ LPCWSTR lpAppName;
+ LPCWSTR lpKeyName;
+ LPCWSTR lpString;
+ LPCWSTR lpFileName;
+} WRITEPRIVATEPROFILESTRINGW_PARAMETERS, *PWRITEPRIVATEPROFILESTRINGW_PARAMETERS;
+
+typedef struct _WRITEPRIVATEPROFILESECTIONA_PARAMETERS {
+ LPCSTR lpAppName;
+ LPCSTR lpString;
+ LPCSTR lpFileName;
+} WRITEPRIVATEPROFILESECTIONA_PARAMETERS, *PWRITEPRIVATEPROFILESECTIONA_PARAMETERS;
+
+typedef struct _WRITEPRIVATEPROFILESECTIONW_PARAMETERS {
+ LPCWSTR lpAppName;
+ LPCWSTR lpString;
+ LPCWSTR lpFileName;
+} WRITEPRIVATEPROFILESECTIONW_PARAMETERS, *PWRITEPRIVATEPROFILESECTIONW_PARAMETERS;
+
+typedef struct _REGCONNECTREGISTRYW_PARAMETERS {
+ LPWSTR lpMachineName;
+ HKEY hKey;
+ PHKEY phkResult;
+} REGCONNECTREGISTRYW_PARAMETERS, *PREGCONNECTREGISTRYW_PARAMETERS;
+
+typedef struct _API_SAVED_PARAMETERS {
+ API_SAVED_CALL_STATE SavedCallState;
+ union {
+ CREATEFILE_PARAMETERS CreateFile;
+ OPENFILE_PARAMETERS OpenFile;
+ DELETEFILE_PARAMETERS DeleteFile;
+ SETINFORMATIONFILE_PARAMETERS SetInformationFile;
+ QUERYATTRIBUTESFILE_PARAMETERS QueryAttributesFile;
+ QUERYDIRECTORYFILE_PARAMETERS QueryDirectoryFile;
+ CREATEKEY_PARAMETERS CreateKey;
+ OPENKEY_PARAMETERS OpenKey;
+ DELETEKEY_PARAMETERS DeleteKey;
+ SETVALUEKEY_PARAMETERS SetValueKey;
+ DELETEVALUEKEY_PARAMETERS DeleteValueKey;
+ CLOSEHANDLE_PARAMETERS CloseHandle;
+ GETVERSIONEXW_PARAMETERS GetVersionExW;
+ SETCURRENTDIRECTORYA_PARAMETERS SetCurrentDirectoryA;
+ SETCURRENTDIRECTORYW_PARAMETERS SetCurrentDirectoryW;
+ WRITEPRIVATEPROFILESTRINGA_PARAMETERS WritePrivateProfileStringA;
+ WRITEPRIVATEPROFILESTRINGW_PARAMETERS WritePrivateProfileStringW;
+ WRITEPRIVATEPROFILESECTIONA_PARAMETERS WritePrivateProfileSectionA;
+ WRITEPRIVATEPROFILESECTIONW_PARAMETERS WritePrivateProfileSectionW;
+ REGCONNECTREGISTRYW_PARAMETERS RegConnectRegistryW;
+ } InputParameters;
+ PVOID ReturnAddress;
+ union {
+ UCHAR ReturnedByte;
+ USHORT ReturnedShort;
+ ULONG ReturnedLong;
+ ULONGLONG ReturnedQuad;
+ BOOL ReturnedBool;
+ } ReturnValue;
+ BOOLEAN ReturnValueValid;
+
+ //
+ // Union for handlers that need to store state between entry and
+ // exit, without the overhead of allocating an API_SAVED_CALL_STATE
+ //
+ union {
+ BOOLEAN AbortCall;
+ PWSTR CurrentDirectory;
+ };
+} API_SAVED_PARAMETERS, *PAPI_SAVED_PARAMETERS;
+
+BOOLEAN
+NtCreateFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+NtOpenFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+NtDeleteFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+NtSetInformationFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+NtQueryAttributesFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+NtQueryDirectoryFileHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+NtCreateKeyHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+NtOpenKeyHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+NtDeleteKeyHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+NtSetValueKeyHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+NtDeleteValueKeyHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+
+BOOLEAN
+NtCloseHandleHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+GetVersionHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+GetVersionExWHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+SetCurrentDirectoryAHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+SetCurrentDirectoryWHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+WritePrivateProfileStringAHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+WritePrivateProfileStringWHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+WritePrivateProfileSectionAHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+WritePrivateProfileSectionWHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+BOOLEAN
+RegConnectRegistryWHandler(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+//
+// Data structures and entry points in DEBUG.C
+//
+
+CRITICAL_SECTION BreakTable;
+
+typedef struct _BREAKPOINT_INFO {
+ LIST_ENTRY Entry;
+ PVOID Address;
+ UCHAR ApiIndex;
+ PWSTR ModuleName;
+ LPSTR ProcedureName;
+ BOOLEAN SavedInstructionValid;
+ BOOLEAN SavedParametersValid;
+ union {
+ UCHAR Byte;
+ USHORT Short;
+ ULONG Long;
+ ULONGLONG LongLong;
+ } SavedInstruction;
+ API_SAVED_PARAMETERS SavedParameters;
+} BREAKPOINT_INFO, *PBREAKPOINT_INFO;
+
+VOID
+DebugEventLoop( VOID );
+
+
+BOOLEAN
+InstallBreakpoint(
+ PPROCESS_INFO Process,
+ PBREAKPOINT_INFO Breakpoint
+ );
+
+BOOLEAN
+RemoveBreakpoint(
+ PPROCESS_INFO Process,
+ PBREAKPOINT_INFO Breakpoint
+ );
+
+BOOLEAN
+HandleBreakpoint(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PBREAKPOINT_INFO Breakpoint
+ );
+
+
+BOOLEAN
+CreateBreakpoint(
+ PVOID Address,
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ UCHAR ApiIndex,
+ PVOID SavedParameters,
+ PBREAKPOINT_INFO *Breakpoint
+ );
+
+BOOLEAN
+DestroyBreakpoint(
+ PVOID Address,
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ );
+
+PBREAKPOINT_INFO
+FindBreakpoint(
+ PVOID Address,
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ );
+
+//
+// Data structures and entry points in HANDLEDB.C
+//
+
+POPENHANDLE_INFO
+FindOpenHandle(
+ PPROCESS_INFO Process,
+ HANDLE Handle,
+ ULONG Type
+ );
+
+BOOLEAN
+AddOpenHandle(
+ PPROCESS_INFO Process,
+ HANDLE Handle,
+ ULONG Type,
+ PWSTR Name,
+ BOOLEAN InheritHandle
+ );
+
+BOOLEAN
+DeleteOpenHandle(
+ PPROCESS_INFO Process,
+ HANDLE Handle,
+ ULONG Type
+ );
+
+VOID
+InheritHandles(
+ PPROCESS_INFO Process
+ );
+
+
+
+//
+// Data structures and entry points in FILE.C
+//
+
+
+typedef struct _FILE_REFERENCE {
+ LIST_ENTRY Entry;
+ PWSTR Name;
+ ULONG PrefixLength;
+ BOOLEAN WriteAccess;
+ BOOLEAN Deleted;
+ BOOLEAN Created;
+ BOOLEAN AttributesModified;
+ BOOLEAN DateModified;
+ BOOLEAN ContentsModified;
+ BOOLEAN DirectoryFile;
+ USHORT BackupFileUniqueId;
+ DWORD BackupFileAttributes;
+ FILETIME BackupLastWriteTime;
+ ULARGE_INTEGER BackupFileSize;
+} FILE_REFERENCE, *PFILE_REFERENCE;
+
+
+BOOLEAN
+CreateFileReference(
+ PWSTR Name,
+ BOOLEAN WriteAccess,
+ PFILE_REFERENCE *ReturnedReference
+ );
+
+BOOLEAN
+CompleteFileReference(
+ PFILE_REFERENCE p,
+ BOOLEAN CallSuccessful,
+ BOOLEAN Delete,
+ PFILE_REFERENCE RenameReference
+ );
+
+BOOLEAN
+DestroyFileReference(
+ PFILE_REFERENCE p
+ );
+
+BOOLEAN
+IsNewFileSameAsBackup(
+ PFILE_REFERENCE p
+ );
+
+PFILE_REFERENCE
+FindFileReference(
+ PWSTR Name
+ );
+
+VOID
+DumpFileReferenceList(
+ FILE *LogFile
+ );
+
+//
+// Data structures and entry points in KEY.C
+//
+
+typedef struct _KEY_REFERENCE {
+ LIST_ENTRY Entry;
+ PWSTR Name;
+ ULONG PrefixLength;
+ BOOLEAN WriteAccess;
+ BOOLEAN Deleted;
+ BOOLEAN Created;
+ PKEY_FULL_INFORMATION BackupKeyInfo;
+ LIST_ENTRY ValueReferencesListHead;
+} KEY_REFERENCE, *PKEY_REFERENCE;
+
+typedef struct _VALUE_REFERENCE {
+ LIST_ENTRY Entry;
+ PWSTR Name;
+ BOOLEAN Modified;
+ BOOLEAN Deleted;
+ BOOLEAN Created;
+ PKEY_VALUE_PARTIAL_INFORMATION Value;
+ KEY_VALUE_PARTIAL_INFORMATION OriginalValue;
+} VALUE_REFERENCE, *PVALUE_REFERENCE;
+
+BOOLEAN
+CreateKeyReference(
+ PWSTR Name,
+ BOOLEAN WriteAccess,
+ PKEY_REFERENCE *ReturnedReference
+ );
+
+BOOLEAN
+CompleteKeyReference(
+ PKEY_REFERENCE p,
+ BOOLEAN CallSuccessful,
+ BOOLEAN Delete
+ );
+
+BOOLEAN
+DestroyKeyReference(
+ PKEY_REFERENCE p
+ );
+
+PKEY_REFERENCE
+FindKeyReference(
+ PWSTR Name
+ );
+
+VOID
+MarkKeyDeleted(
+ PKEY_REFERENCE KeyReference
+ );
+
+BOOLEAN
+CreateValueReference(
+ PPROCESS_INFO Process,
+ PKEY_REFERENCE KeyReference,
+ PWSTR Name,
+ ULONG TitleIndex,
+ ULONG Type,
+ PVOID Data,
+ ULONG DataLength,
+ PVALUE_REFERENCE *ReturnedValueReference
+ );
+
+PVALUE_REFERENCE
+FindValueReference(
+ PKEY_REFERENCE KeyReference,
+ PWSTR Name
+ );
+
+BOOLEAN
+DestroyValueReference(
+ PVALUE_REFERENCE p
+ );
+
+VOID
+DumpKeyReferenceList(
+ FILE *LogFile
+ );
+
+//
+// Data structures and entry points in INI.C
+//
+
+typedef struct _INI_FILE_REFERENCE {
+ LIST_ENTRY Entry;
+ PWSTR Name;
+ ULONG PrefixLength;
+ BOOLEAN Created;
+ FILETIME BackupLastWriteTime;
+ LIST_ENTRY SectionReferencesListHead;
+} INI_FILE_REFERENCE, *PINI_FILE_REFERENCE;
+
+typedef struct _INI_SECTION_REFERENCE {
+ LIST_ENTRY Entry;
+ PWSTR Name;
+ BOOLEAN Deleted;
+ BOOLEAN Created;
+ LIST_ENTRY VariableReferencesListHead;
+} INI_SECTION_REFERENCE, *PINI_SECTION_REFERENCE;
+
+typedef struct _INI_VARIABLE_REFERENCE {
+ LIST_ENTRY Entry;
+ PWSTR Name;
+ BOOLEAN Modified;
+ BOOLEAN Deleted;
+ BOOLEAN Created;
+ PWSTR OriginalValue;
+ PWSTR Value;
+} INI_VARIABLE_REFERENCE, *PINI_VARIABLE_REFERENCE;
+
+
+BOOLEAN
+CreateIniFileReference(
+ PWSTR Name,
+ PINI_FILE_REFERENCE *ReturnedReference
+ );
+
+BOOLEAN
+DestroyIniFileReference(
+ PINI_FILE_REFERENCE p
+ );
+
+BOOLEAN
+DestroyIniSectionReference(
+ PINI_SECTION_REFERENCE p1
+ );
+
+BOOLEAN
+DestroyIniVariableReference(
+ PINI_VARIABLE_REFERENCE p2
+ );
+
+PINI_FILE_REFERENCE
+FindIniFileReference(
+ PWSTR Name
+ );
+
+PINI_SECTION_REFERENCE
+FindIniSectionReference(
+ PINI_FILE_REFERENCE p,
+ PWSTR Name,
+ BOOLEAN CreateOkay
+ );
+
+PINI_VARIABLE_REFERENCE
+FindIniVariableReference(
+ PINI_SECTION_REFERENCE p1,
+ PWSTR Name,
+ BOOLEAN CreateOkay
+ );
+
+VOID
+DumpIniFileReferenceList(
+ FILE *LogFile
+ );
+
+//
+// Data structures and entry points in NAMEDB.C
+//
+
+PWSTR
+AddName(
+ PUNICODE_STRING Name
+ );
+
+VOID
+DumpNameDataBase(
+ FILE *LogFile
+ );
+
+BOOLEAN
+IncrementOpenCount(
+ PWSTR Name,
+ BOOLEAN CallSuccessful
+ );
+
+ULONG
+QueryOpenCount(
+ PWSTR Name,
+ BOOLEAN CallSuccessful
+ );
+
+
+
+//
+// Data structures and entry points in INIT.C
+//
+
+HMODULE InstalerModuleHandle;
+HANDLE AppHeap;
+UNICODE_STRING WindowsDirectory;
+ULONG StartProcessTickCount;
+FILE *InstalerLogFile;
+BOOLEAN AskUserOnce;
+BOOLEAN DefaultGetVersionToWin95;
+BOOLEAN FailAllScansOfRootDirectories;
+
+
+
+LIST_ENTRY FileReferenceListHead;
+ULONG NumberOfFileReferences;
+LIST_ENTRY KeyReferenceListHead;
+ULONG NumberOfKeyReferences;
+LIST_ENTRY IniReferenceListHead;
+ULONG NumberOfIniReferences;
+
+#define NTDLL_MODULE_INDEX 0
+#define KERNEL32_MODULE_INDEX 1
+#define ADVAPI32_MODULE_INDEX 2
+#define MAXIMUM_MODULE_INDEX 3
+
+typedef struct _MODULE_INFO {
+ PWSTR ModuleName;
+ HMODULE ModuleHandle;
+} MODULE_INFO, *PMODULE_INFO;
+
+MODULE_INFO ModuleInfo[ MAXIMUM_MODULE_INDEX ];
+
+typedef BOOLEAN (*PAPI_HANDLER)(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ PAPI_SAVED_PARAMETERS Parameters
+ );
+
+typedef struct _API_INFO {
+ ULONG ModuleIndex;
+ LPSTR EntryPointName; // Exported names are ANSI only
+ PVOID EntryPointAddress;
+ PAPI_HANDLER EntryPointHandler;
+ ULONG SizeOfParameters;
+ ULONG SizeOfReturnValue;
+} API_INFO, *PAPI_INFO;
+
+#define NTCREATEFILE_INDEX 0
+#define NTOPENFILE_INDEX 1
+#define NTDELETEFILE_INDEX 2
+#define NTSETINFORMATIONFILE_INDEX 3
+#define NTQUERYATTRIBUTESFILE_INDEX 4
+#define NTQUERYDIRECTORYFILE_INDEX 5
+#define NTCREATEKEY_INDEX 6
+#define NTOPENKEY_INDEX 7
+#define NTDELETEKEY_INDEX 8
+#define NTSETVALUEKEY_INDEX 9
+#define NTDELETEVALUEKEY_INDEX 10
+#define NTCLOSEHANDLE_INDEX 11
+#define GETVERSION_INDEX 12
+#define GETVERSIONEXW_INDEX 13
+#define SETCURRENTDIRECTORYA_INDEX 14
+#define SETCURRENTDIRECTORYW_INDEX 15
+#define WRITEPRIVATEPROFILESTRINGA_INDEX 16
+#define WRITEPRIVATEPROFILESTRINGW_INDEX 17
+#define WRITEPRIVATEPROFILESECTIONA_INDEX 18
+#define WRITEPRIVATEPROFILESECTIONW_INDEX 19
+#define GETPRIVATEPROFILESTRINGA_INDEX 20
+#define GETPRIVATEPROFILESTRINGW_INDEX 21
+#define GETPRIVATEPROFILESECTIONA_INDEX 22
+#define GETPRIVATEPROFILESECTIONW_INDEX 23
+#define REGCONNECTREGISTRYW_INDEX 24
+#define MAXIMUM_API_INDEX 25
+
+API_INFO ApiInfo[ MAXIMUM_API_INDEX ];
+
+typedef struct _DRIVE_LETTER_INFO {
+ WCHAR DriveLetter;
+ BOOLEAN DriveLetterValid;
+ UNICODE_STRING NtLinkTarget;
+} DRIVE_LETTER_INFO, *PDRIVE_LETTER_INFO;
+
+UNICODE_STRING UNCPrefix;
+UNICODE_STRING DriveLetterPrefix;
+UNICODE_STRING AltDriveLetterPrefix;
+DRIVE_LETTER_INFO DriveLetters[ 32 ];
+
+BOOLEAN
+InitializeInstaler(
+ int argc,
+ char *argv[]
+ );
+
+PVOID
+LookupEntryPointInIAT(
+ HMODULE ModuleHandle,
+ PCHAR EntryPointName
+ );
+
+BOOLEAN
+IsDriveLetterPath(
+ PUNICODE_STRING Path
+ );
+
+
+PVOID TemporaryBuffer;
+ULONG TemporaryBufferLength;
+ULONG TemporaryBufferLengthGrowth;
+ULONG TemporaryBufferMaximumLength;
+
+VOID
+TrimTemporaryBuffer( VOID );
+
+BOOLEAN
+GrowTemporaryBuffer(
+ ULONG NeededSize
+ );
+
+ULONG
+FillTemporaryBuffer(
+ PPROCESS_INFO Process,
+ PVOID Address,
+ BOOLEAN Unicode,
+ BOOLEAN DoubleNullTermination
+ );
+
+#include "instutil.h"
+#include "iml.h"
+
+PINSTALLATION_MODIFICATION_LOGFILE pImlNew;
+
+
+#endif // defined( RC_INVOKED )
diff --git a/private/sdktools/instaler/instaler.rc b/private/sdktools/instaler/instaler.rc
new file mode 100644
index 000000000..c608ba1aa
--- /dev/null
+++ b/private/sdktools/instaler/instaler.rc
@@ -0,0 +1,12 @@
+#include "instaler.h"
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Windows NT Instaler Program"
+#define VER_INTERNALNAME_STR "INSTALER\0"
+#define VER_ORIGINALFILENAME_STR "INSTALER.EXE"
+
+#include "common.ver"
+
+RCINCLUDE errormsg.rc
diff --git a/private/sdktools/instaler/instutil.c b/private/sdktools/instaler/instutil.c
new file mode 100644
index 000000000..fb9b0c683
--- /dev/null
+++ b/private/sdktools/instaler/instutil.c
@@ -0,0 +1,365 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ instutil.c
+
+Abstract:
+
+ Common code for INSTALER.EXE, DISPINST.EXE, COMPINST.EXE and UNDOINST.EXE
+
+Author:
+
+ Steve Wood (stevewo) 14-Jan-1996
+
+Revision History:
+
+--*/
+
+#include "instutil.h"
+#include "iml.h"
+
+LPSTR SavedModuleName;
+LPSTR SavedModuleUsage1;
+LPSTR SavedModuleUsage2;
+
+WCHAR AltTempFilePathBuffer[ MAX_PATH ];
+PWSTR AltTempFilePathFileName;
+WCHAR TempFilePathBuffer[ MAX_PATH ];
+PWSTR TempFilePathFileName;
+USHORT TempFileNextUniqueId;
+
+BOOL WINAPI
+CtrlCHandler(
+ ULONG CtrlType
+ )
+{
+ //
+ // Ignore control C interrupts. Let child process deal with them
+ // if it wants. If it doesn't then it will terminate and we will
+ // get control and terminate ourselves
+ //
+
+ return TRUE;
+}
+
+void
+InitCommonCode(
+ LPSTR ModuleName,
+ LPSTR ModuleUsage1,
+ LPSTR ModuleUsage2
+ )
+{
+ SavedModuleName = ModuleName;
+ SavedModuleUsage1 = ModuleUsage1;
+ SavedModuleUsage2 = ModuleUsage2;
+
+ GetTempPath( sizeof( TempFilePathBuffer ) / sizeof( WCHAR ),
+ TempFilePathBuffer
+ );
+ TempFilePathFileName = TempFilePathBuffer + wcslen( TempFilePathBuffer );
+ TempFileNextUniqueId = (USHORT)0x0001;
+
+ InstalerDirectory[ 0 ] = UNICODE_NULL;
+ return;
+}
+
+void
+DisplayIndentedString(
+ ULONG IndentAmount,
+ PCHAR sBegin
+ )
+{
+ PCHAR sEnd;
+
+ while (sBegin != NULL) {
+ sEnd = sBegin;
+ while (*sEnd && *sEnd != '\n') {
+ sEnd += 1;
+ }
+
+ fprintf( stderr, "%.*s%.*s\n",
+ IndentAmount,
+ " ",
+ sEnd - sBegin, sBegin
+ );
+
+ if (*sEnd == '\0') {
+ break;
+ }
+ else {
+ sBegin = ++sEnd;
+ }
+ }
+ return;
+}
+
+
+void
+Usage(
+ LPSTR Message,
+ ULONG MessageParameter
+ )
+{
+ ULONG n;
+ LPSTR sBegin, sEnd;
+
+ n = fprintf( stderr, "usage: %s ", SavedModuleName );
+ fprintf( stderr, "InstallationName\n" );
+ DisplayIndentedString( n, SavedModuleUsage1 );
+ fprintf( stderr, "\n" );
+
+ n = fprintf( stderr, "where: " );
+ fprintf( stderr, "InstallationName specifies a name for the installation. This is a required parameter.\n" );
+ DisplayIndentedString( n,
+ "-? Displays this message."
+ );
+ fprintf( stderr, "\n" );
+ DisplayIndentedString( n, SavedModuleUsage2 );
+
+ if (Message != NULL) {
+ fprintf( stderr, "\n" );
+ }
+
+ //
+ // No return from FatalError
+ //
+ FatalError( Message, MessageParameter, 0 );
+}
+
+void
+FatalError(
+ LPSTR Message,
+ ULONG MessageParameter1,
+ ULONG MessageParameter2
+ )
+{
+ if (Message != NULL) {
+ fprintf( stderr, "%s: ", SavedModuleName );
+ fprintf( stderr, Message, MessageParameter1, MessageParameter2 );
+ fprintf( stderr, "\n" );
+ }
+
+ exit( 1 );
+}
+
+
+PWSTR
+GetArgAsUnicode(
+ LPSTR s
+ )
+{
+ ULONG n;
+ PWSTR ps;
+
+ n = strlen( s );
+ ps = HeapAlloc( GetProcessHeap(),
+ 0,
+ (n + 1) * sizeof( WCHAR )
+ );
+ if (ps == NULL) {
+ FatalError( "Out of memory", 0, 0 );
+ }
+
+ if (MultiByteToWideChar( CP_ACP,
+ MB_PRECOMPOSED,
+ s,
+ n,
+ ps,
+ n
+ ) != (LONG)n
+ ) {
+ FatalError( "Unable to convert parameter '%s' to Unicode (%u)", (ULONG)s, GetLastError() );
+ }
+
+ ps[ n ] = UNICODE_NULL;
+ return ps;
+}
+
+
+void
+CommonSwitchProcessing(
+ PULONG argc,
+ PCHAR **argv,
+ CHAR c
+ )
+{
+ DWORD dwFileAttributes;
+ PWSTR s;
+
+ switch( c = tolower( c ) ) {
+ case 'd':
+ DebugOutput = TRUE;
+ break;
+
+ case '?':
+ Usage( NULL, 0 );
+ break;
+
+ default:
+ Usage( "Invalid switch (-%c)", (ULONG)c );
+ break;
+ }
+
+ return;
+}
+
+
+BOOLEAN
+CommonArgProcessing(
+ PULONG argc,
+ PCHAR **argv
+ )
+{
+ PWSTR s;
+
+ if (InstallationName == NULL) {
+ GetCurrentDirectory( MAX_PATH, InstalerDirectory );
+ s = wcschr( InstalerDirectory, UNICODE_NULL );
+ if (s[-1] != L'\\') {
+ *s++ = L'\\';
+ *s = UNICODE_NULL;
+ }
+
+ InstallationName = GetArgAsUnicode( **argv );
+ ImlPath = FormatImlPath( InstalerDirectory, InstallationName );
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+PWSTR
+FormatTempFileName(
+ PWSTR Directory,
+ PUSHORT TempFileUniqueId
+ )
+{
+ if (*TempFileUniqueId == 0) {
+ *TempFileUniqueId = (USHORT)(TempFileNextUniqueId++);
+ if (TempFileNextUniqueId == 0) {
+ return NULL;
+ }
+ }
+ else
+ if (*TempFileUniqueId == 0xFFFF) {
+ return NULL;
+ }
+
+ if (Directory != NULL) {
+ GetFullPathName( Directory, MAX_PATH, AltTempFilePathBuffer, &AltTempFilePathFileName );
+ AltTempFilePathFileName = wcsrchr( AltTempFilePathBuffer, TEXT('\\') );
+ AltTempFilePathFileName += 1;
+ swprintf( AltTempFilePathFileName, L"~INS%04x.TMP", *TempFileUniqueId );
+ return AltTempFilePathBuffer;
+ }
+ else {
+ swprintf( TempFilePathFileName, L"~INS%04x.TMP", *TempFileUniqueId );
+ return TempFilePathBuffer;
+ }
+}
+
+PWSTR
+CreateBackupFileName(
+ PUSHORT TempFileUniqueId
+ )
+{
+ PWSTR BackupFileName;
+
+ while (BackupFileName = FormatTempFileName( NULL, TempFileUniqueId )) {
+ if (GetFileAttributesW( BackupFileName ) == 0xFFFFFFFF) {
+ break;
+ }
+ else {
+ *TempFileUniqueId = 0; // Temp file name existed, try next unique id
+ }
+ }
+
+ return BackupFileName;
+}
+
+
+UCHAR EnumTypeBuffer0[ 512 ];
+UCHAR EnumTypeBuffer1[ 512 ];
+UCHAR EnumTypeBuffer2[ 512 ];
+UCHAR EnumTypeBuffer3[ 512 ];
+LPSTR EnumTypeBuffers[ 4 ] = {
+ EnumTypeBuffer0,
+ EnumTypeBuffer1,
+ EnumTypeBuffer2,
+ EnumTypeBuffer3
+};
+
+LPSTR
+FormatEnumType(
+ ULONG BufferIndex,
+ PENUM_TYPE_NAMES Table,
+ ULONG Value,
+ BOOLEAN FlagFormat
+ )
+{
+ LPSTR s, FlagsBuffer = EnumTypeBuffers[ BufferIndex ];
+
+ FlagsBuffer[ 0 ] = '\0';
+ while (Table->Value != 0xFFFFFFFF) {
+ if (FlagFormat) {
+ if (Table->Value & Value) {
+ if (FlagsBuffer[ 0 ] != '\0') {
+ strcat( FlagsBuffer, " | " );
+ }
+
+ strcat( FlagsBuffer, Table->Name );
+ Value &= ~Table->Value;
+ if (Value == 0) {
+ return FlagsBuffer;
+ }
+ }
+ }
+ else
+ if (Table->Value == Value) {
+ if (Value == 0) {
+ if (!strcmp( Table->Name, "STATUS_WAIT_0" )) {
+ return "STATUS_SUCCESS";
+ }
+ else
+ if (!strcmp( Table->Name, "ERROR_SUCCESS" )) {
+ return "NO_ERROR";
+ }
+ }
+ return Table->Name;
+ }
+
+ Table += 1;
+ }
+
+ s = FlagsBuffer;
+ if (FlagFormat) {
+ if (s[ 0 ] != '\0') {
+ strcat( s, " | " );
+ s += strlen( s );
+ }
+ }
+
+ sprintf( s, Table->Name ? Table->Name : "%x", Value );
+ return FlagsBuffer;
+}
+
+
+
+ENUM_TYPE_NAMES ValueDataTypeNames[] = {
+ REG_NONE, "REG_NONE",
+ REG_SZ, "REG_SZ",
+ REG_EXPAND_SZ, "REG_EXPAND_SZ",
+ REG_BINARY, "REG_BINARY",
+ REG_DWORD, "REG_DWORD",
+ REG_DWORD_BIG_ENDIAN, "REG_DWORD_BIG_ENDIAN",
+ REG_LINK, "REG_LINK",
+ REG_MULTI_SZ, "REG_MULTI_SZ",
+ REG_RESOURCE_LIST, "REG_RESOURCE_LIST",
+ REG_FULL_RESOURCE_DESCRIPTOR, "REG_FULL_RESOURCE_DESCRIPTOR",
+ REG_RESOURCE_REQUIREMENTS_LIST,"REG_RESOURCE_REQUIREMENTS_LIST",
+ 0xFFFFFFFF, "%x"
+};
diff --git a/private/sdktools/instaler/instutil.h b/private/sdktools/instaler/instutil.h
new file mode 100644
index 000000000..1b98ddc51
--- /dev/null
+++ b/private/sdktools/instaler/instutil.h
@@ -0,0 +1,114 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ instutil.h
+
+Abstract:
+
+ Header file for common definition for programs that manipulate the output of
+ the INSTALER.EXE program (e.g. DISPINST.EXE, COMPINST.EXE, UNDOINST.EXE)
+
+Author:
+
+ Steve Wood (stevewo) 14-Jan-1996
+
+Revision History:
+
+--*/
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <tchar.h>
+
+WCHAR InstalerDirectory[ MAX_PATH ];
+PWSTR InstallationName;
+PWSTR ImlPath;
+BOOLEAN DebugOutput;
+
+//
+// Useful rounding macros that the rounding amount is always a
+// power of two.
+//
+
+#define ROUND_DOWN( Size, Amount ) ((DWORD)(Size) & ~((Amount) - 1))
+#define ROUND_UP( Size, Amount ) (((DWORD)(Size) + ((Amount) - 1)) & ~((Amount) - 1))
+
+void
+InitCommonCode(
+ LPSTR ModuleName,
+ LPSTR ModuleUsage1,
+ LPSTR ModuleUsage2
+ );
+
+void
+Usage(
+ LPSTR Message,
+ ULONG MessageParameter
+ );
+
+void
+FatalError(
+ LPSTR Message,
+ ULONG MessageParameter1,
+ ULONG MessageParameter2
+ );
+
+void
+InputMessage(
+ PWSTR FileName,
+ ULONG LineNumber,
+ BOOLEAN Error,
+ LPSTR Message,
+ ULONG MessageParameter1,
+ ULONG MessageParameter2
+ );
+
+PWSTR
+GetArgAsUnicode(
+ LPSTR s
+ );
+
+void
+CommonSwitchProcessing(
+ PULONG argc,
+ PCHAR **argv,
+ CHAR c
+ );
+
+BOOLEAN
+CommonArgProcessing(
+ PULONG argc,
+ PCHAR **argv
+ );
+
+PWSTR
+FormatTempFileName(
+ PWSTR Directory,
+ PUSHORT TempFileUniqueId
+ );
+
+
+PWSTR
+CreateBackupFileName(
+ PUSHORT TempFileUniqueId
+ );
+
+
+typedef struct _ENUM_TYPE_NAMES {
+ ULONG Value;
+ LPSTR Name;
+} ENUM_TYPE_NAMES, *PENUM_TYPE_NAMES;
+
+LPSTR
+FormatEnumType(
+ ULONG BufferIndex,
+ PENUM_TYPE_NAMES Table,
+ ULONG Value,
+ BOOLEAN FlagFormat
+ );
+
+ENUM_TYPE_NAMES ValueDataTypeNames[];
diff --git a/private/sdktools/instaler/key.c b/private/sdktools/instaler/key.c
new file mode 100644
index 000000000..2b7096e4d
--- /dev/null
+++ b/private/sdktools/instaler/key.c
@@ -0,0 +1,524 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ key.c
+
+Abstract:
+
+ This module implements the functions to save references to registry
+ keys and values for the INSTALER program. Part of each reference is
+ a a backup copy of a key information and its values that have been
+ changed/deleted.
+
+Author:
+
+ Steve Wood (stevewo) 22-Aug-1994
+
+Revision History:
+
+--*/
+
+#include "instaler.h"
+
+#define DEFAULT_KEY_VALUE_BUFFER_SIZE 512
+PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
+
+VOID
+BackupKeyInfo(
+ PKEY_REFERENCE p
+ )
+{
+ NTSTATUS Status;
+ UNICODE_STRING KeyName;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE KeyHandle;
+ KEY_FULL_INFORMATION KeyInformation;
+ ULONG ResultLength;
+ ULONG ValueIndex;
+ UNICODE_STRING ValueName;
+ PVALUE_REFERENCE ValueReference;
+
+ RtlInitUnicodeString( &KeyName, p->Name );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ Status = NtOpenKey( &KeyHandle,
+ KEY_READ,
+ &ObjectAttributes
+ );
+ if (!NT_SUCCESS( Status )) {
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+ p->Created = TRUE;
+ p->WriteAccess = TRUE;
+ }
+
+ fprintf( InstalerLogFile, "*** Unable to open key %ws (%x)\n", p->Name, Status );
+ return;
+ }
+
+ if (!p->WriteAccess) {
+ NtClose( KeyHandle );
+ return;
+ }
+
+ fprintf( InstalerLogFile, "Saving values for %ws\n", p->Name );
+ if (KeyValueInformation == NULL) {
+ KeyValueInformation = AllocMem( DEFAULT_KEY_VALUE_BUFFER_SIZE );
+ if (KeyValueInformation == NULL) {
+ fprintf( InstalerLogFile, "*** No memory for key value information\n" );
+ return;
+ }
+ }
+
+ Status = NtQueryKey( KeyHandle,
+ KeyFullInformation,
+ &KeyInformation,
+ sizeof( KeyInformation ),
+ &ResultLength
+ );
+ if (!NT_SUCCESS( Status ) && Status != STATUS_BUFFER_OVERFLOW) {
+ NtClose( KeyHandle );
+ fprintf( InstalerLogFile, "*** Unable to query key (%x)\n", Status );
+ return;
+ }
+
+ p->BackupKeyInfo = AllocMem( ResultLength );
+ if (p->BackupKeyInfo == NULL) {
+ NtClose( KeyHandle );
+ fprintf( InstalerLogFile, "*** No memory for backup information\n" );
+ return;
+ }
+
+ if (!NT_SUCCESS( Status )) {
+ Status = NtQueryKey( KeyHandle,
+ KeyFullInformation,
+ p->BackupKeyInfo,
+ ResultLength,
+ &ResultLength
+ );
+ if (!NT_SUCCESS( Status )) {
+ NtClose( KeyHandle );
+ fprintf( InstalerLogFile, "*** Unable to query key (%x)\n", Status );
+ return;
+ }
+ }
+ else {
+ memmove( p->BackupKeyInfo, &KeyInformation, ResultLength );
+ }
+
+
+ for (ValueIndex = 0; TRUE; ValueIndex++) {
+ Status = NtEnumerateValueKey( KeyHandle,
+ ValueIndex,
+ KeyValueFullInformation,
+ KeyValueInformation,
+ DEFAULT_KEY_VALUE_BUFFER_SIZE,
+ &ResultLength
+ );
+ if (!NT_SUCCESS( Status ) && Status != STATUS_BUFFER_OVERFLOW) {
+ break;
+ }
+
+ ValueReference = AllocMem( FIELD_OFFSET( VALUE_REFERENCE, OriginalValue ) +
+ FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data ) +
+ KeyValueInformation->DataLength
+ );
+ if (ValueReference == NULL) {
+ fprintf( InstalerLogFile, "*** No memory for value ref\n", Status );
+ break;
+ }
+
+ if (KeyValueInformation->NameLength != 0) {
+ ValueName.Length = (USHORT)KeyValueInformation->NameLength;
+ ValueName.MaximumLength = ValueName.Length;
+ ValueName.Buffer = KeyValueInformation->Name;
+ ValueReference->Name = AddName( &ValueName );
+ fprintf( InstalerLogFile, " Saving data for '%ws'\n", ValueReference->Name );
+ }
+ else {
+ fprintf( InstalerLogFile, " Saving data for empty value name\n" );
+ }
+
+ if (Status == STATUS_BUFFER_OVERFLOW) {
+ Status = NtEnumerateValueKey( KeyHandle,
+ ValueIndex,
+ KeyValuePartialInformation,
+ &ValueReference->OriginalValue,
+ FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data ) +
+ KeyValueInformation->DataLength,
+ &ResultLength
+ );
+ if (!NT_SUCCESS( Status )) {
+ FreeMem( &ValueReference );
+ break;
+ }
+ }
+ else {
+ ValueReference->OriginalValue.TitleIndex = KeyValueInformation->TitleIndex;
+ ValueReference->OriginalValue.Type = KeyValueInformation->Type;
+ ValueReference->OriginalValue.DataLength = KeyValueInformation->DataLength;
+ memmove( &ValueReference->OriginalValue.Data,
+ (PUCHAR)KeyValueInformation + KeyValueInformation->DataOffset,
+ KeyValueInformation->DataLength
+ );
+ }
+
+ InsertTailList( &p->ValueReferencesListHead, &ValueReference->Entry );
+ }
+
+ NtClose( KeyHandle );
+ return;
+}
+
+
+
+BOOLEAN
+CreateKeyReference(
+ PWSTR Name,
+ BOOLEAN WriteAccess,
+ PKEY_REFERENCE *ReturnedReference
+ )
+{
+ PKEY_REFERENCE p;
+
+ *ReturnedReference = NULL;
+ p = FindKeyReference( Name );
+ if (p != NULL) {
+ if (p->WriteAccess) {
+ *ReturnedReference = p;
+ return TRUE;
+ }
+ }
+ else {
+ p = AllocMem( sizeof( *p ) );
+ if (p == NULL) {
+ return FALSE;
+ }
+
+ p->Name = Name;
+ InsertTailList( &KeyReferenceListHead, &p->Entry );
+ NumberOfKeyReferences += 1;
+ }
+
+ InitializeListHead( &p->ValueReferencesListHead );
+ p->WriteAccess = WriteAccess;
+ BackupKeyInfo( p );
+
+ *ReturnedReference = p;
+ return TRUE;
+}
+
+
+BOOLEAN
+CompleteKeyReference(
+ PKEY_REFERENCE p,
+ BOOLEAN CallSuccessful,
+ BOOLEAN Deleted
+ )
+{
+ if (!CallSuccessful) {
+ DestroyKeyReference( p );
+ return FALSE;
+ }
+
+ if (Deleted && p->Created) {
+ LogEvent( INSTALER_EVENT_DELETE_TEMP_KEY,
+ 1,
+ p->Name
+ );
+ DestroyKeyReference( p );
+ return FALSE;
+ }
+
+ if (Deleted) {
+ LogEvent( INSTALER_EVENT_DELETE_KEY,
+ 1,
+ p->Name
+ );
+ }
+ else
+ if (p->WriteAccess) {
+ LogEvent( INSTALER_EVENT_WRITE_KEY,
+ 1,
+ p->Name
+ );
+ }
+ else {
+ LogEvent( INSTALER_EVENT_READ_KEY,
+ 1,
+ p->Name
+ );
+ }
+
+ p->Deleted = Deleted;
+ return TRUE;
+}
+
+
+BOOLEAN
+DestroyKeyReference(
+ PKEY_REFERENCE p
+ )
+{
+ RemoveEntryList( &p->Entry );
+ NumberOfKeyReferences -= 1;
+ FreeMem( &p );
+ return TRUE;
+}
+
+PKEY_REFERENCE
+FindKeyReference(
+ PWSTR Name
+ )
+{
+ PKEY_REFERENCE p;
+ PLIST_ENTRY Head, Next;
+
+ Head = &KeyReferenceListHead;
+ Next = Head->Flink;
+ while (Head != Next) {
+ p = CONTAINING_RECORD( Next, KEY_REFERENCE, Entry );
+ if (p->Name == Name) {
+ return p;
+ }
+
+ Next = Next->Flink;
+ }
+
+ return NULL;
+}
+
+VOID
+MarkKeyDeleted(
+ PKEY_REFERENCE KeyReference
+ )
+{
+ PVALUE_REFERENCE p;
+ PLIST_ENTRY Head, Next;
+
+ KeyReference->Deleted = TRUE;
+ Head = &KeyReference->ValueReferencesListHead;
+ Next = Head->Flink;
+ while (Head != Next) {
+ p = CONTAINING_RECORD( Next, VALUE_REFERENCE, Entry );
+ Next = Next->Flink;
+ if (p->Created || KeyReference->Created) {
+ DestroyValueReference( p );
+ }
+ else {
+ p->Deleted = TRUE;
+ }
+ }
+
+ if (KeyReference->Created) {
+ DestroyKeyReference( KeyReference );
+ }
+
+ return;
+}
+
+BOOLEAN
+CreateValueReference(
+ PPROCESS_INFO Process,
+ PKEY_REFERENCE KeyReference,
+ PWSTR Name,
+ ULONG TitleIndex,
+ ULONG Type,
+ PVOID Data,
+ ULONG DataLength,
+ PVALUE_REFERENCE *ReturnedValueReference
+ )
+{
+ PVALUE_REFERENCE p;
+ PKEY_VALUE_PARTIAL_INFORMATION OldValue, NewValue;
+
+ *ReturnedValueReference = NULL;
+
+ NewValue = AllocMem( FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data ) +
+ DataLength
+ );
+ if (NewValue == NULL) {
+ return FALSE;
+ }
+
+ NewValue->TitleIndex = TitleIndex;
+ NewValue->Type = Type;
+ NewValue->DataLength = DataLength;
+ if (!ReadMemory( Process,
+ Data,
+ &NewValue->Data[0],
+ DataLength,
+ "new value data"
+ )
+ ) {
+ FreeMem( &NewValue );
+ return FALSE;
+ }
+
+ p = FindValueReference( KeyReference, Name );
+ if (p != NULL) {
+ FreeMem( &p->Value );
+ p->Value = NewValue;
+ OldValue = &p->OriginalValue;
+ if (OldValue->TitleIndex == NewValue->TitleIndex &&
+ OldValue->Type == NewValue->Type &&
+ OldValue->DataLength == NewValue->DataLength &&
+ RtlCompareMemory( &OldValue->Data, &NewValue->Data, OldValue->DataLength )
+ ) {
+ FreeMem( &p->Value );
+ }
+ else {
+ p->Modified = TRUE;
+ }
+
+ return TRUE;
+ }
+
+ p = AllocMem( FIELD_OFFSET( VALUE_REFERENCE, OriginalValue ) );
+ if (p == NULL) {
+ FreeMem( &NewValue );
+ return FALSE;
+ }
+
+ p->Name = Name;
+ p->Created = TRUE;
+ p->Value = NewValue;
+ InsertTailList( &KeyReference->ValueReferencesListHead, &p->Entry );
+
+ *ReturnedValueReference = p;
+ return TRUE;
+}
+
+
+PVALUE_REFERENCE
+FindValueReference(
+ PKEY_REFERENCE KeyReference,
+ PWSTR Name
+ )
+{
+ PVALUE_REFERENCE p;
+ PLIST_ENTRY Head, Next;
+
+ Head = &KeyReference->ValueReferencesListHead;
+ Next = Head->Flink;
+ while (Head != Next) {
+ p = CONTAINING_RECORD( Next, VALUE_REFERENCE, Entry );
+ if (p->Name == Name) {
+ return p;
+ }
+
+ Next = Next->Flink;
+ }
+
+ return NULL;
+}
+
+
+BOOLEAN
+DestroyValueReference(
+ PVALUE_REFERENCE p
+ )
+{
+ RemoveEntryList( &p->Entry );
+ FreeMem( &p );
+ return TRUE;
+}
+
+
+VOID
+DumpKeyReferenceList(
+ FILE *LogFile
+ )
+{
+ PKEY_REFERENCE p;
+ PLIST_ENTRY Head, Next;
+ PVALUE_REFERENCE p1;
+ PLIST_ENTRY Head1, Next1;
+ PKEY_VALUE_PARTIAL_INFORMATION OldValue, NewValue;
+ KEY_VALUE_PARTIAL_INFORMATION NullValue;
+ POFFSET Values;
+
+ memset( &NullValue, 0, sizeof( NullValue ) );
+ NullValue.Type = REG_SZ;
+
+ Head = &KeyReferenceListHead;
+ Next = Head->Flink;
+ while (Head != Next) {
+ p = CONTAINING_RECORD( Next, KEY_REFERENCE, Entry );
+ if (p->Created || p->WriteAccess) {
+ Values = 0;
+ Head1 = &p->ValueReferencesListHead;
+ Next1 = Head1->Flink;
+ while (Head1 != Next1) {
+ p1 = CONTAINING_RECORD( Next1, VALUE_REFERENCE, Entry );
+ NewValue = p1->Value;
+ if (NewValue == NULL) {
+ NewValue = &NullValue;
+ }
+
+ OldValue = &p1->OriginalValue;
+ if (!p1->Deleted) {
+ if (p1->Created) {
+ ImlAddValueRecord( pImlNew,
+ CreateNewValue,
+ p1->Name,
+ NewValue->Type,
+ NewValue->DataLength,
+ NewValue->Data,
+ 0,
+ 0,
+ NULL,
+ &Values
+ );
+ }
+ else
+ if (p1->Modified) {
+ ImlAddValueRecord( pImlNew,
+ ModifyOldValue,
+ p1->Name,
+ NewValue->Type,
+ NewValue->DataLength,
+ NewValue->Data,
+ OldValue->Type,
+ OldValue->DataLength,
+ OldValue->Data,
+ &Values
+ );
+ }
+ }
+ else {
+ ImlAddValueRecord( pImlNew,
+ DeleteOldValue,
+ p1->Name,
+ 0,
+ 0,
+ NULL,
+ OldValue->Type,
+ OldValue->DataLength,
+ OldValue->Data,
+ &Values
+ );
+ }
+
+ Next1 = Next1->Flink;
+ }
+
+ ImlAddKeyRecord( pImlNew,
+ p->Created ? CreateNewKey :
+ p->Deleted ? DeleteOldKey : ModifyKeyValues,
+ p->Name,
+ Values
+ );
+ }
+
+ Next = Next->Flink;
+ }
+
+ return;
+}
diff --git a/private/sdktools/instaler/makefile b/private/sdktools/instaler/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/sdktools/instaler/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/sdktools/instaler/makefile.inc b/private/sdktools/instaler/makefile.inc
new file mode 100644
index 000000000..5c217612d
--- /dev/null
+++ b/private/sdktools/instaler/makefile.inc
@@ -0,0 +1,10 @@
+instaler.rc: errormsg.rc
+
+showinst.rc: errormsg.rc
+
+undoinst.rc: errormsg.rc
+
+compinst.rc: errormsg.rc
+
+errormsg.h errormsg.rc msg00001.bin: errormsg.mc
+ mc -v errormsg.mc
diff --git a/private/sdktools/instaler/namedb.c b/private/sdktools/instaler/namedb.c
new file mode 100644
index 000000000..3d9c2274d
--- /dev/null
+++ b/private/sdktools/instaler/namedb.c
@@ -0,0 +1,173 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ namedb.c
+
+Abstract:
+
+ This module maintains a database of path names detected by the INSTALER program
+
+Author:
+
+ Steve Wood (stevewo) 20-Aug-1994
+
+Revision History:
+
+--*/
+
+
+#include "instaler.h"
+
+typedef struct _NAME_TABLE_ENTRY {
+ struct _NAME_TABLE_ENTRY *HashLink;
+ UNICODE_STRING Name;
+ ULONG OpenCount;
+ ULONG FailedOpenCount;
+} NAME_TABLE_ENTRY, *PNAME_TABLE_ENTRY;
+
+#define NUMBER_OF_HASH_BUCKETS 37
+PNAME_TABLE_ENTRY NameTableBuckets[ NUMBER_OF_HASH_BUCKETS ];
+
+BOOLEAN
+IncrementOpenCount(
+ PWSTR Name,
+ BOOLEAN CallSuccessful
+ )
+{
+ PNAME_TABLE_ENTRY p;
+
+ p = (PNAME_TABLE_ENTRY)Name - 1;
+ if (p->Name.Buffer == Name) {
+ if (CallSuccessful) {
+ p->OpenCount += 1;
+ }
+ else {
+ p->FailedOpenCount += 1;
+ }
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+ULONG
+QueryOpenCount(
+ PWSTR Name,
+ BOOLEAN CallSuccessful
+ )
+{
+ PNAME_TABLE_ENTRY p;
+
+ p = (PNAME_TABLE_ENTRY)Name - 1;
+ if (p->Name.Buffer == Name) {
+ if (CallSuccessful) {
+ return p->OpenCount;
+ }
+ else {
+ return p->FailedOpenCount;
+ }
+
+ }
+ else {
+ return 0;
+ }
+}
+
+PWSTR
+AddName(
+ PUNICODE_STRING Name
+ )
+{
+ ULONG n, Hash;
+ WCHAR c;
+ PWCH s;
+ PNAME_TABLE_ENTRY *pp, p;
+
+ n = Name->Length / sizeof( c );
+ s = Name->Buffer;
+ Hash = 0;
+ while (n--) {
+ c = RtlUpcaseUnicodeChar( *s++ );
+ Hash = Hash + (c << 1) + (c >> 1) + c;
+ }
+
+ pp = &NameTableBuckets[ Hash % NUMBER_OF_HASH_BUCKETS ];
+ while (p = *pp) {
+ if (RtlEqualUnicodeString( &p->Name, Name, TRUE )) {
+ return p->Name.Buffer;
+ }
+ else {
+ pp = &p->HashLink;
+ }
+ }
+
+ p = AllocMem( sizeof( *p ) + Name->Length + sizeof( UNICODE_NULL ) );
+ *pp = p;
+ p->HashLink = NULL;
+ p->Name.Buffer = (PWSTR)(p + 1);
+ p->Name.Length = Name->Length;
+ p->Name.MaximumLength = (USHORT)(Name->Length + sizeof( UNICODE_NULL ));
+ RtlMoveMemory( p->Name.Buffer, Name->Buffer, Name->Length );
+ p->Name.Buffer[ Name->Length / sizeof( WCHAR ) ] = UNICODE_NULL;
+ return p->Name.Buffer;
+}
+
+
+VOID
+DumpNameDataBase(
+ FILE *LogFile
+ )
+{
+ ULONG i;
+ PNAME_TABLE_ENTRY p;
+
+#if 0
+ fprintf( LogFile, "Name Data Base entries:\n" );
+ for (i=0; i<NUMBER_OF_HASH_BUCKETS; i++) {
+ p = NameTableBuckets[ i ];
+ if (p != NULL) {
+ fprintf( LogFile, "Bucket[ %02u ]:\n", i );
+ while (p) {
+ fprintf( LogFile, " %ws\n", p->Name.Buffer );
+ p = p->HashLink;
+ }
+ }
+ }
+ fprintf( LogFile, "\n" );
+#endif
+
+ fprintf( LogFile, "List of paths with non-zero successful open counts\n" );
+ for (i=0; i<NUMBER_OF_HASH_BUCKETS; i++) {
+ p = NameTableBuckets[ i ];
+ if (p != NULL) {
+ while (p) {
+ if (p->OpenCount != 0) {
+ fprintf( LogFile, " %4u %ws\n", p->OpenCount, p->Name.Buffer );
+ }
+
+ p = p->HashLink;
+ }
+ }
+ }
+ fprintf( LogFile, "\n" );
+ fprintf( LogFile, "List of paths with non-zero failed open counts\n" );
+ for (i=0; i<NUMBER_OF_HASH_BUCKETS; i++) {
+ p = NameTableBuckets[ i ];
+ if (p != NULL) {
+ while (p) {
+ if (p->FailedOpenCount != 0) {
+ fprintf( LogFile, " %4u %ws\n", p->FailedOpenCount, p->Name.Buffer );
+ }
+
+ p = p->HashLink;
+ }
+ }
+ }
+ fprintf( LogFile, "\n" );
+
+ return;
+}
diff --git a/private/sdktools/instaler/process.c b/private/sdktools/instaler/process.c
new file mode 100644
index 000000000..c3a57627f
--- /dev/null
+++ b/private/sdktools/instaler/process.c
@@ -0,0 +1,560 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ process.c
+
+Abstract:
+
+ This module maintains state about each process/thread created by the application
+ setup/install program.
+
+Author:
+
+ Steve Wood (stevewo) 09-Aug-1994
+
+Revision History:
+
+--*/
+
+#include "instaler.h"
+
+BOOLEAN
+AddProcess(
+ LPDEBUG_EVENT DebugEvent,
+ PPROCESS_INFO *ReturnedProcess
+ )
+{
+ NTSTATUS Status;
+ PPROCESS_INFO Process;
+ RTL_USER_PROCESS_PARAMETERS ProcessParameters;
+ PEB Peb;
+ PWSTR FreeBuffer, s;
+
+ Process = AllocMem( sizeof( *Process ) );
+ if (Process == NULL) {
+ return FALSE;
+ }
+
+ Process->Id = DebugEvent->dwProcessId;
+ Process->Handle = DebugEvent->u.CreateProcessInfo.hProcess;
+ InitializeListHead( &Process->ThreadListHead );
+ InitializeListHead( &Process->BreakpointListHead );
+ InitializeListHead( &Process->OpenHandleListHead );
+ InsertTailList( &ProcessListHead, &Process->Entry );
+ *ReturnedProcess = Process;
+
+ Status = NtQueryInformationProcess( Process->Handle,
+ ProcessBasicInformation,
+ &Process->ProcessInformation,
+ sizeof( Process->ProcessInformation ),
+ NULL
+ );
+ FreeBuffer = NULL;
+ if (ReadMemory( Process,
+ Process->ProcessInformation.PebBaseAddress,
+ &Peb,
+ sizeof( Peb ),
+ "PEB"
+ ) &&
+ Peb.ProcessParameters != NULL &&
+ ReadMemory( Process,
+ Peb.ProcessParameters,
+ &ProcessParameters,
+ sizeof( ProcessParameters ),
+ "ProcessParameters"
+ ) &&
+ ProcessParameters.ImagePathName.Length != 0 &&
+ (FreeBuffer = AllocMem( ProcessParameters.ImagePathName.Length + sizeof( UNICODE_NULL ) )) != NULL &&
+ ReadMemory( Process,
+ ProcessParameters.Flags & RTL_USER_PROC_PARAMS_NORMALIZED ?
+ ProcessParameters.ImagePathName.Buffer :
+ (PWSTR)((ULONG)(ProcessParameters.ImagePathName.Buffer) + (PCHAR)(Peb.ProcessParameters)),
+ FreeBuffer,
+ ProcessParameters.ImagePathName.Length,
+ "Image File Name"
+ )
+ ) {
+ s = (PWSTR)((PCHAR)FreeBuffer + ProcessParameters.ImagePathName.Length);
+ while (s > FreeBuffer && s[ -1 ] != OBJ_NAME_PATH_SEPARATOR) {
+ s--;
+ }
+
+ wcsncpy( Process->ImageFileName,
+ s,
+ (sizeof( Process->ImageFileName ) - sizeof( UNICODE_NULL )) / sizeof( WCHAR )
+ );
+ }
+ FreeMem( &FreeBuffer );
+ return TRUE;
+}
+
+BOOLEAN
+DeleteProcess(
+ PPROCESS_INFO Process
+ )
+{
+ PLIST_ENTRY Next, Head;
+ PTHREAD_INFO Thread;
+ PBREAKPOINT_INFO Breakpoint;
+ POPENHANDLE_INFO p;
+
+ RemoveEntryList( &Process->Entry );
+
+ Head = &Process->ThreadListHead;
+ Next = Head->Flink;
+ while (Next != Head) {
+ Thread = CONTAINING_RECORD( Next, THREAD_INFO, Entry );
+ Next = Next->Flink;
+ DeleteThread( Process, Thread );
+ }
+
+ Head = &Process->BreakpointListHead;
+ Next = Head->Flink;
+ while (Next != Head) {
+ Breakpoint = CONTAINING_RECORD( Next, BREAKPOINT_INFO, Entry );
+ Next = Next->Flink;
+ DestroyBreakpoint( Breakpoint->Address, Process, NULL );
+ }
+
+
+ Head = &Process->OpenHandleListHead;
+ Next = Head->Flink;
+ while (Next != Head) {
+ p = CONTAINING_RECORD( Next, OPENHANDLE_INFO, Entry );
+ Next = Next->Flink;
+ DeleteOpenHandle( Process, p->Handle, p->Type );
+ }
+
+ FreeMem( &Process );
+ return TRUE;
+}
+
+
+BOOLEAN
+AddThread(
+ LPDEBUG_EVENT DebugEvent,
+ PPROCESS_INFO Process,
+ PTHREAD_INFO *ReturnedThread
+ )
+{
+ PTHREAD_INFO Thread;
+
+ Thread = AllocMem( sizeof( *Thread ) );
+ if (Thread == NULL) {
+ return FALSE;
+ }
+
+ Thread->Id = DebugEvent->dwThreadId;
+ if (DebugEvent->dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) {
+ Thread->Handle = DebugEvent->u.CreateProcessInfo.hThread;
+ Thread->StartAddress = DebugEvent->u.CreateProcessInfo.lpStartAddress;
+ }
+ else {
+ Thread->Handle = DebugEvent->u.CreateThread.hThread;
+ Thread->StartAddress = DebugEvent->u.CreateThread.lpStartAddress;
+ }
+ Thread->SingleStepExpected = FALSE;
+ InitializeListHead( &Thread->BreakpointListHead );
+ InsertTailList( &Process->ThreadListHead, &Thread->Entry );
+ *ReturnedThread = Thread;
+ return TRUE;
+}
+
+BOOLEAN
+DeleteThread(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ )
+{
+ PLIST_ENTRY Next, Head;
+ PBREAKPOINT_INFO Breakpoint;
+
+ RemoveEntryList( &Thread->Entry );
+
+ Head = &Thread->BreakpointListHead;
+ Next = Head->Flink;
+ while (Next != Head) {
+ Breakpoint = CONTAINING_RECORD( Next, BREAKPOINT_INFO, Entry );
+ Next = Next->Flink;
+ DestroyBreakpoint( Breakpoint->Address, Process, Thread );
+ }
+
+ FreeMem( &Thread );
+ return TRUE;
+}
+
+
+PPROCESS_INFO
+FindProcessById(
+ ULONG Id
+ )
+{
+ PLIST_ENTRY Next, Head;
+ PPROCESS_INFO Process;
+
+ Head = &ProcessListHead;
+ Next = Head->Flink;
+ while (Next != Head) {
+ Process = CONTAINING_RECORD( Next, PROCESS_INFO, Entry );
+ if (Process->Id == Id) {
+ return Process;
+ }
+
+ Next = Next->Flink;
+ }
+
+ return NULL;
+}
+
+BOOLEAN
+FindProcessAndThreadForEvent(
+ LPDEBUG_EVENT DebugEvent,
+ PPROCESS_INFO *ReturnedProcess,
+ PTHREAD_INFO *ReturnedThread
+ )
+{
+ PLIST_ENTRY Next, Head;
+ PPROCESS_INFO Process;
+ PTHREAD_INFO Thread;
+
+ Head = &ProcessListHead;
+ Next = Head->Flink;
+ Process = NULL;
+ Thread = NULL;
+ while (Next != Head) {
+ Process = CONTAINING_RECORD( Next, PROCESS_INFO, Entry );
+ if (Process->Id == DebugEvent->dwProcessId) {
+ Head = &Process->ThreadListHead;
+ Next = Head->Flink;
+ while (Next != Head) {
+ Thread = CONTAINING_RECORD( Next, THREAD_INFO, Entry );
+ if (Thread->Id == DebugEvent->dwThreadId) {
+ break;
+ }
+
+ Thread = NULL;
+ Next = Next->Flink;
+ }
+
+ break;
+ }
+
+ Process = NULL;
+ Next = Next->Flink;
+ }
+
+ *ReturnedProcess = Process;
+ *ReturnedThread = Thread;
+
+ if (DebugEvent->dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) {
+ if (Process != NULL) {
+ DeclareError( INSTALER_DUPLICATE_PROCESS_ID, 0, DebugEvent->dwProcessId );
+ return FALSE;
+ }
+ }
+ else
+ if (DebugEvent->dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT) {
+ if (Thread != NULL) {
+ DeclareError( INSTALER_DUPLICATE_THREAD_ID, 0, DebugEvent->dwThreadId, DebugEvent->dwProcessId );
+ return FALSE;
+ }
+ if (Process == NULL) {
+ DeclareError( INSTALER_MISSING_PROCESS_ID, 0, DebugEvent->dwProcessId );
+ return FALSE;
+ }
+ }
+ else
+ if (Process == NULL) {
+ DeclareError( INSTALER_MISSING_PROCESS_ID, 0, DebugEvent->dwProcessId );
+ return FALSE;
+ }
+ else
+ if (Thread == NULL) {
+ DeclareError( INSTALER_MISSING_THREAD_ID, 0, DebugEvent->dwThreadId, DebugEvent->dwProcessId );
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+PBREAKPOINT_INFO
+FindBreakpoint(
+ LPVOID Address,
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ )
+{
+ PBREAKPOINT_INFO Breakpoint;
+ PLIST_ENTRY Next, Head;
+
+ if (Thread != NULL) {
+ Head = &Thread->BreakpointListHead;
+ Next = Head->Flink;
+ while (Next != Head) {
+ Breakpoint = CONTAINING_RECORD( Next, BREAKPOINT_INFO, Entry );
+ if (Breakpoint->Address == Address) {
+ return Breakpoint;
+ }
+
+ Next = Next->Flink;
+ }
+ }
+
+ Head = &Process->BreakpointListHead;
+ Next = Head->Flink;
+ while (Next != Head) {
+ Breakpoint = CONTAINING_RECORD( Next, BREAKPOINT_INFO, Entry );
+ if (Breakpoint->Address == Address) {
+ return Breakpoint;
+ }
+
+ Next = Next->Flink;
+ }
+
+ return NULL;
+}
+
+BOOLEAN
+CreateBreakpoint(
+ LPVOID Address,
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread,
+ UCHAR ApiIndex,
+ PAPI_SAVED_PARAMETERS SavedParameters,
+ PBREAKPOINT_INFO *ReturnedBreakpoint
+ )
+{
+ PBREAKPOINT_INFO Breakpoint;
+
+ Breakpoint = FindBreakpoint( Address, Process, Thread );
+
+ if (ARGUMENT_PRESENT( ReturnedBreakpoint )) {
+ *ReturnedBreakpoint = Breakpoint;
+ }
+
+ if (Breakpoint != NULL) {
+ return (Breakpoint->ApiIndex == ApiIndex);
+ }
+
+ Breakpoint = AllocMem( sizeof( *Breakpoint ) );
+ if (Breakpoint == NULL) {
+ return FALSE;
+ }
+
+ Breakpoint->Address = Address;
+ Breakpoint->ApiIndex = ApiIndex;
+ if (ARGUMENT_PRESENT( SavedParameters )) {
+ Breakpoint->SavedParameters = *SavedParameters;
+ Breakpoint->SavedParametersValid = TRUE;
+ }
+ else {
+ Breakpoint->SavedParametersValid = FALSE;
+ }
+
+ if (Thread != NULL) {
+ InsertTailList( &Thread->BreakpointListHead, &Breakpoint->Entry );
+ }
+ else {
+ InsertTailList( &Process->BreakpointListHead, &Breakpoint->Entry );
+ }
+
+ InstallBreakpoint( Process, Breakpoint );
+
+ if (ARGUMENT_PRESENT( ReturnedBreakpoint )) {
+ *ReturnedBreakpoint = Breakpoint;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+DestroyBreakpoint(
+ LPVOID Address,
+ PPROCESS_INFO Process,
+ PTHREAD_INFO Thread
+ )
+{
+ PBREAKPOINT_INFO Breakpoint;
+
+ Breakpoint = FindBreakpoint( Address, Process, Thread );
+ if (Breakpoint == NULL) {
+ return FALSE;
+ }
+
+ RemoveBreakpoint( Process, Breakpoint );
+
+ RemoveEntryList( &Breakpoint->Entry );
+
+ FreeMem( &Breakpoint );
+ return TRUE;
+}
+
+
+BOOLEAN
+HandleThreadsForSingleStep(
+ PPROCESS_INFO Process,
+ PTHREAD_INFO ThreadToSingleStep,
+ BOOLEAN SuspendThreads
+ )
+{
+ PLIST_ENTRY Next, Head;
+ PTHREAD_INFO Thread;
+
+ Head = &Process->ThreadListHead;
+ Next = Head->Flink;
+ while (Next != Head) {
+ Thread = CONTAINING_RECORD( Next, THREAD_INFO, Entry );
+ if (Thread != ThreadToSingleStep) {
+ if (SuspendThreads) {
+ if (Thread->BreakpointToStepOver == NULL) {
+ SuspendThread( Thread->Handle );
+ }
+ }
+ else {
+ ResumeThread( Thread->Handle );
+ }
+
+ break;
+ }
+
+ Next = Next->Flink;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+ReadMemory(
+ PPROCESS_INFO Process,
+ PVOID Address,
+ PVOID DataRead,
+ ULONG BytesToRead,
+ PCHAR Reason
+ )
+{
+ ULONG BytesRead;
+
+ if (!ReadProcessMemory( Process->Handle,
+ Address,
+ DataRead,
+ BytesToRead,
+ &BytesRead
+ ) ||
+ BytesRead != BytesToRead
+ ) {
+ DbgEvent( MEMORYERROR, ( "Read memory from %x for %x bytes failed (%u) - '%s'\n",
+ Address,
+ BytesToRead,
+ GetLastError(),
+ Reason
+ )
+ );
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+
+BOOLEAN
+WriteMemory(
+ PPROCESS_INFO Process,
+ PVOID Address,
+ PVOID DataToWrite,
+ ULONG BytesToWrite,
+ PCHAR Reason
+ )
+{
+ ULONG BytesWritten;
+ ULONG OldProtection;
+ BOOLEAN Result;
+
+ if (WriteProcessMemory( Process->Handle,
+ Address,
+ DataToWrite,
+ BytesToWrite,
+ &BytesWritten
+ ) &&
+ BytesWritten == BytesToWrite
+ ) {
+ return TRUE;
+ }
+
+ Result = FALSE;
+ if (GetLastError() == ERROR_NOACCESS &&
+ VirtualProtectEx( Process->Handle,
+ Address,
+ BytesToWrite,
+ PAGE_READWRITE,
+ &OldProtection
+ )
+ ) {
+ if (WriteProcessMemory( Process->Handle,
+ Address,
+ DataToWrite,
+ BytesToWrite,
+ &BytesWritten
+ ) &&
+ BytesWritten == BytesToWrite
+ ) {
+ Result = TRUE;
+ }
+ VirtualProtectEx( Process->Handle,
+ Address,
+ BytesToWrite,
+ OldProtection,
+ &OldProtection
+ );
+ if (Result) {
+ return TRUE;
+ }
+ }
+
+ DbgEvent( MEMORYERROR, ( "Write memory to %x for %x bytes failed (%u) - '%s'\n",
+ Address,
+ BytesToWrite,
+ GetLastError(),
+ Reason
+ )
+ );
+ return FALSE;
+}
+
+
+PVOID
+AllocMem(
+ ULONG Size
+ )
+{
+ PVOID p;
+
+ p = HeapAlloc( AppHeap, HEAP_ZERO_MEMORY, Size );
+ if (p == NULL) {
+ DbgEvent( INTERNALERROR, ( "HeapAlloc( %0x8 ) failed\n", Size ) );
+ }
+
+
+
+ return p;
+}
+
+
+VOID
+FreeMem(
+ PVOID *p
+ )
+{
+ if (*p != NULL) {
+ HeapFree( AppHeap, 0, *p );
+ *p = NULL;
+ }
+
+ return;
+}
diff --git a/private/sdktools/instaler/res.h b/private/sdktools/instaler/res.h
new file mode 100644
index 000000000..c1a489bf4
--- /dev/null
+++ b/private/sdktools/instaler/res.h
@@ -0,0 +1,2 @@
+#define FILEBMP 500
+#define DIRBMP 501
diff --git a/private/sdktools/instaler/showinst.c b/private/sdktools/instaler/showinst.c
new file mode 100644
index 000000000..9cba9b2b3
--- /dev/null
+++ b/private/sdktools/instaler/showinst.c
@@ -0,0 +1,295 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ showinst.c
+
+Abstract:
+
+ This program displays the contents of an Installation Modification Log file
+ created by the INSTALER program
+
+Author:
+
+ Steve Wood (stevewo) 15-Jan-1996
+
+Revision History:
+
+--*/
+
+#include "instutil.h"
+#include "iml.h"
+
+BOOLEAN DisplayContentsOfTextFiles;
+
+int
+ProcessShowIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml
+ );
+
+int
+_CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ int Result;
+ char *s;
+ PWSTR pw;
+ PINSTALLATION_MODIFICATION_LOGFILE pIml;
+
+ InitCommonCode( "SHOWINST",
+ "[-c]",
+ "-c specifies to display the contents of text files\n"
+ );
+ DisplayContentsOfTextFiles = FALSE;
+ while (--argc) {
+ s = *++argv;
+ if (*s == '-' || *s == '/') {
+ while (*++s) {
+ switch( tolower( *s ) ) {
+ case 'c':
+ DisplayContentsOfTextFiles = TRUE;
+ break;
+ default:
+ CommonSwitchProcessing( &argc, &argv, *s );
+ break;
+ }
+ }
+ }
+ else
+ if (!CommonArgProcessing( &argc, &argv )) {
+ Usage( "Arguments not supported - '%s'", (ULONG)s );
+ }
+ }
+
+ if (ImlPath == NULL) {
+ Usage( "Must specify an installation name as first argument", 0 );
+ }
+
+ if (!SetCurrentDirectory( InstalerDirectory )) {
+ FatalError( "Unable to change to '%ws' directory (%u)",
+ (ULONG)InstalerDirectory,
+ GetLastError()
+ );
+ }
+
+ pIml = LoadIml( ImlPath );
+ if (pIml == NULL) {
+ FatalError( "Unable to open '%ws' (%u)",
+ (ULONG)ImlPath,
+ GetLastError()
+ );
+ }
+ Result = ProcessShowIml( pIml );
+
+ CloseIml( pIml );
+ exit( Result );
+ return Result;
+}
+
+int
+ProcessShowIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pIml
+ )
+{
+ PIML_FILE_RECORD pFile;
+ PIML_FILE_RECORD_CONTENTS pOldFile;
+ PIML_FILE_RECORD_CONTENTS pNewFile;
+ PIML_KEY_RECORD pKey;
+ PIML_VALUE_RECORD pValue;
+ PIML_VALUE_RECORD_CONTENTS pOldValue;
+ PIML_VALUE_RECORD_CONTENTS pNewValue;
+ PIML_INI_RECORD pIni;
+ PIML_INISECTION_RECORD pSection;
+ PIML_INIVARIABLE_RECORD pVariable;
+ BOOLEAN FileNameShown;
+ BOOLEAN SectionNameShown;
+
+ if (pIml->NumberOfFileRecords > 0) {
+ printf( "File Modifications:\n" );
+ pFile = MP( PIML_FILE_RECORD, pIml, pIml->FileRecords );
+ while (pFile != NULL) {
+ pOldFile = MP( PIML_FILE_RECORD_CONTENTS, pIml, pFile->OldFile );
+ pNewFile = MP( PIML_FILE_RECORD_CONTENTS, pIml, pFile->NewFile );
+ printf( " %ws - ", MP( PWSTR, pIml, pFile->Name ) );
+ switch( pFile->Action ) {
+ case CreateNewFile:
+ printf( "created\n" );
+ break;
+
+ case ModifyOldFile:
+ printf( "overwritten\n" );
+ if (pOldFile != NULL) {
+ printf( " Old size is %u bytes\n", pOldFile->FileSize );
+ }
+ break;
+
+ case DeleteOldFile:
+ printf( "deleted\n" );
+ if (pOldFile != NULL) {
+ printf( " Old size is %u bytes\n", pOldFile->FileSize );
+ }
+ break;
+
+ case ModifyFileDateTime:
+ printf( "date/time modified\n" );
+ break;
+
+ case ModifyFileAttributes:
+ printf( "attributes modified\n" );
+ break;
+ }
+
+ pFile = MP( PIML_FILE_RECORD, pIml, pFile->Next );
+ }
+
+ printf( "\n" );
+ }
+
+ if (pIml->NumberOfKeyRecords > 0) {
+ printf( "Registry Modifications:\n" );
+ pKey = MP( PIML_KEY_RECORD, pIml, pIml->KeyRecords );
+ while (pKey != NULL) {
+ printf( " %ws - ", MP( PWSTR, pIml, pKey->Name ) );
+ switch( pKey->Action ) {
+ case CreateNewKey:
+ printf( "created\n" );
+ break;
+
+ case DeleteOldKey:
+ printf( "deleted\n" );
+ break;
+
+
+ case ModifyKeyValues:
+ printf( "modified\n" );
+ break;
+
+ }
+
+ pValue = MP( PIML_VALUE_RECORD, pIml, pKey->Values );
+ while (pValue != NULL) {
+ pOldValue = MP( PIML_VALUE_RECORD_CONTENTS, pIml, pValue->OldValue );
+ pNewValue = MP( PIML_VALUE_RECORD_CONTENTS, pIml, pValue->NewValue );
+ printf( " %ws - ", MP( PWSTR, pIml, pValue->Name ) );
+ if (pValue->Action != DeleteOldValue) {
+ printf( "%s [%x]",
+ FormatEnumType( 0, ValueDataTypeNames, pNewValue->Type, FALSE ),
+ pNewValue->Length
+ );
+ if (pNewValue->Type == REG_SZ ||
+ pNewValue->Type == REG_EXPAND_SZ
+ ) {
+ printf( " '%ws'", MP( PWSTR, pIml, pNewValue->Data ) );
+ }
+ else
+ if (pNewValue->Type == REG_DWORD) {
+ printf( " 0x%x", *MP( PULONG, pIml, pNewValue->Data ) );
+ }
+ }
+
+ if (pValue->Action == CreateNewValue) {
+ printf( " (created)\n" );
+ }
+ else {
+ if (pValue->Action == DeleteOldValue) {
+ printf( " (deleted" );
+ }
+ else {
+ printf( " (modified" );
+ }
+
+ printf( " - was %s [%x]",
+ FormatEnumType( 0, ValueDataTypeNames, pOldValue->Type, FALSE ),
+ pOldValue->Length
+ );
+ if (pOldValue->Type == REG_SZ ||
+ pOldValue->Type == REG_EXPAND_SZ
+ ) {
+ printf( " '%ws'", MP( PWSTR, pIml, pOldValue->Data ) );
+ }
+ else
+ if (pOldValue->Type == REG_DWORD) {
+ printf( " 0x%x", *MP( PULONG, pIml, pOldValue->Data ) );
+ }
+
+ printf( " )\n" );
+ }
+
+ pValue = MP( PIML_VALUE_RECORD, pIml, pValue->Next );
+ }
+
+ pKey = MP( PIML_KEY_RECORD, pIml, pKey->Next );
+ }
+
+ printf( "\n" );
+ }
+
+ if (pIml->NumberOfIniRecords > 0) {
+ printf( ".INI File modifications:\n" );
+ pIni = MP( PIML_INI_RECORD, pIml, pIml->IniRecords );
+ while (pIni != NULL) {
+ FileNameShown = FALSE;
+ pSection = MP( PIML_INISECTION_RECORD, pIml, pIni->Sections );
+ while (pSection != NULL) {
+ SectionNameShown = FALSE;
+ pVariable = MP( PIML_INIVARIABLE_RECORD, pIml, pSection->Variables );
+ while (pVariable != NULL) {
+ if (!FileNameShown) {
+ printf( "%ws", MP( PWSTR, pIml, pIni->Name ) );
+ if (pIni->Action == CreateNewIniFile) {
+ printf( " (created)" );
+ }
+ printf( "\n" );
+ FileNameShown = TRUE;
+ }
+
+ if (!SectionNameShown) {
+ printf( " [%ws]", MP( PWSTR, pIml, pSection->Name ) );
+ if (pSection->Action == CreateNewSection) {
+ printf( " (created)" );
+ }
+ else
+ if (pSection->Action == DeleteOldSection) {
+ printf( " (deleted)" );
+ }
+ printf( "\n" );
+
+ SectionNameShown = TRUE;
+ }
+
+ printf( " %ws = ", MP( PWSTR, pIml, pVariable->Name ) );
+ if (pVariable->Action == CreateNewVariable) {
+ printf( "'%ws' (created)\n", MP( PWSTR, pIml, pVariable->NewValue ) );
+ }
+ else
+ if (pVariable->Action == DeleteOldVariable) {
+ printf( " (deleted - was '%ws')\n", MP( PWSTR, pIml, pVariable->OldValue ) );
+ }
+ else {
+ printf( "'%ws' (modified - was '%ws')\n",
+ MP( PWSTR, pIml, pVariable->NewValue ),
+ MP( PWSTR, pIml, pVariable->OldValue )
+ );
+ }
+
+ pVariable = MP( PIML_INIVARIABLE_RECORD, pIml, pVariable->Next );
+ }
+
+ pSection = MP( PIML_INISECTION_RECORD, pIml, pSection->Next );
+ }
+
+ if (FileNameShown) {
+ printf( "\n" );
+ }
+
+ pIni = MP( PIML_INI_RECORD, pIml, pIni->Next );
+ }
+ }
+
+ return 0;
+}
diff --git a/private/sdktools/instaler/showinst.rc b/private/sdktools/instaler/showinst.rc
new file mode 100644
index 000000000..399061b50
--- /dev/null
+++ b/private/sdktools/instaler/showinst.rc
@@ -0,0 +1,12 @@
+#include "instaler.h"
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Windows NT Instaler Display Program"
+#define VER_INTERNALNAME_STR "SHOWINST\0"
+#define VER_ORIGINALFILENAME_STR "SHOWINST.EXE"
+
+#include "common.ver"
+
+RCINCLUDE errormsg.rc
diff --git a/private/sdktools/instaler/sources b/private/sdktools/instaler/sources
new file mode 100644
index 000000000..c77ff7f6b
--- /dev/null
+++ b/private/sdktools/instaler/sources
@@ -0,0 +1,36 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=sdktools
+MINORCOMP=instaler
+
+TARGETNAME=instaler
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+C_DEFINES=-DUNICODE -D_UNICODE
+
+SOURCES=
diff --git a/private/sdktools/instaler/test.c b/private/sdktools/instaler/test.c
new file mode 100644
index 000000000..8579fbcaf
--- /dev/null
+++ b/private/sdktools/instaler/test.c
@@ -0,0 +1,389 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ test.c
+
+Abstract:
+
+ This is a test program that issues various Kernel 32 file/registry/INI file
+ API calls so that we can see if the INSTALER program figures out correctly
+ what is being done.
+
+ This program assumes that the following conditions exist:
+
+ TEST subdirectory
+
+
+Author:
+
+ Steve Wood (stevewo) 13-Aug-1994
+
+--*/
+
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntdbg.h>
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+
+UCHAR AnsiBuffer[ MAX_PATH ];
+WCHAR UnicodeBuffer[ MAX_PATH ];
+
+
+int
+_CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ FILE *File0;
+ FILE *File1;
+ FILE *File2;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ BOOLEAN TranslationStatus;
+ UNICODE_STRING FileName;
+ RTL_RELATIVE_NAME RelativeName;
+ PVOID FreeBuffer;
+ UNICODE_STRING KeyName;
+ UNICODE_STRING SubKeyName;
+ UNICODE_STRING ValueName;
+ HANDLE FileHandle, FindHandle, KeyHandle, SubKeyHandle;
+ ULONG ValueDWord = 0x12345678;
+ WIN32_FIND_DATAW FindFileData;
+ LPSTR s1;
+ PWSTR s2;
+ UCHAR AnsiBuffer[ MAX_PATH ];
+ WCHAR UnicodeBuffer[ MAX_PATH ];
+ DWORD dwVersion;
+ OSVERSIONINFO VersionInfo;
+ HKEY hKey;
+
+ //
+ // File operations we want to test:
+ //
+ // Creating a new file.
+ // Deleting that file using DeleteFile (which does NtOpenFile and NtSetInformationFile
+ // with Delete Dispostion). (INSTALER should use this to forget about create).
+ // Creating a new file with the same name.
+ // Deleting that file using NtDeleteFile. (again INSTALER should not care).
+ //
+ // Open the TEST1 file for read access (INSTALER should not care).
+ // Open the TEST2 file for write access (INSTALER should not care).
+ // Open the TEST2 file for write access (INSTALER should not care).
+ // Open the TEST2 file for write access (INSTALER should not care).
+ //
+ //
+
+ // printf( "TEST: GetFileAttributes( .\\test1 )\n" );
+ GetFileAttributesA( ".\\test1" );
+#if 0
+ dwVersion = GetVersion();
+ if ((dwVersion >> 30) ^ 0x2 == VER_PLATFORM_WIN32_WINDOWS) {
+ printf( "GetVersion returns Windows 95\n" );
+ }
+ else
+ if ((dwVersion >> 30) ^ 0x2 == VER_PLATFORM_WIN32_NT) {
+ printf( "GetVersion returns Windows NT\n" );
+ }
+ else {
+ printf( "GetVersion returns %x\n", dwVersion );
+ }
+ fflush(stdout);
+
+ VersionInfo.dwOSVersionInfoSize = sizeof( VersionInfo );
+ GetVersionEx( &VersionInfo );
+ if (VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+ printf( "GetVersionEx returns Windows 95\n" );
+ }
+ else
+ if (VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ printf( "GetVersionEx returns Windows NT\n" );
+ }
+ else {
+ printf( "GetVersionEx returns %x\n", VersionInfo.dwPlatformId );
+ }
+ fflush(stdout);
+
+ if (RegConnectRegistryA( "\\\\stevewo_dbgr", HKEY_USERS, &hKey )) {
+ printf( "RegConnectRegistryA( \\stevewo_dbgr ) failed (hKey == %x).\n", hKey );
+ }
+ else {
+ printf( "RegConnectRegistryA( \\stevewo_dbgr ) succeeded (hKey == %x).\n", hKey );
+ RegCloseKey( hKey );
+ }
+
+ if (RegConnectRegistryW( L"\\\\stevewo_dbgr", HKEY_USERS, &hKey )) {
+ printf( "RegConnectRegistryW( \\stevewo_dbgr ) failed (hKey == %x).\n", hKey );
+ }
+ else {
+ printf( "RegConnectRegistryW( \\stevewo_dbgr ) succeeded (hKey == %x).\n", hKey );
+ RegCloseKey( hKey );
+ }
+#endif
+
+ RtlInitUnicodeString( &FileName, L"\\DosDevices\\A:" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ // printf( "TEST: NtOpenFile( %wZ ) should succeed without touching floppy.\n", &FileName );
+ Status = NtOpenFile( &FileHandle,
+ SYNCHRONIZE | FILE_READ_ATTRIBUTES,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
+ );
+ if (!NT_SUCCESS( Status )) {
+ printf( "TEST: Failed - Status == %x\n", Status );
+ }
+ else {
+ NtClose( FileHandle );
+ }
+
+ // printf( "TEST: FindFirstFileW( C:\\*.* should fail.\n" );
+ FindHandle = FindFirstFileW( L"C:\\*.*", &FindFileData );
+ if (FindHandle != INVALID_HANDLE_VALUE) {
+ printf( "TEST: *** oops, it worked.\n" );
+ FindClose( FindHandle );
+ }
+ // printf( "TEST: FindFirstFileW( \\TMP\\*.* should work.\n" );
+ FindHandle = FindFirstFileW( L"\\TMP\\*.*", &FindFileData );
+ if (FindHandle == INVALID_HANDLE_VALUE) {
+ printf( "TEST: *** oops, it failed.\n" );
+ }
+ else {
+ FindClose( FindHandle );
+ }
+
+ // printf( "TEST: opening .\\test0 for write access.\n" );
+ if (File0 = fopen( "test0.", "w" )) {
+ fprintf( File0, "This is test file 0\n" );
+ // printf( "TEST: closing .\\test0\n" );
+ fclose( File0 );
+ // printf( "TEST: deleting .\\test0 using DeleteFile (open, set, close)\n" );
+ DeleteFile( L"test0" );
+ }
+
+ // printf( "TEST: opening .\\test0 for write access.\n" );
+ if (File0 = fopen( "test0.", "w" )) {
+ fprintf( File0, "This is test file 0\n" );
+ // printf( "TEST: closing .\\test0\n" );
+ fclose( File0 );
+
+ TranslationStatus = RtlDosPathNameToNtPathName_U(
+ L"test0",
+ &FileName,
+ NULL,
+ &RelativeName
+ );
+
+ if (TranslationStatus ) {
+ FreeBuffer = FileName.Buffer;
+ if ( RelativeName.RelativeName.Length ) {
+ FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
+ }
+ else {
+ RelativeName.ContainingDirectory = NULL;
+ }
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ RelativeName.ContainingDirectory,
+ NULL
+ );
+ // printf( "TEST: deleting .\\test0 using NtDeleteFile\n" );
+ Status = NtDeleteFile( &ObjectAttributes );
+ RtlFreeHeap( RtlProcessHeap(), 0, FreeBuffer );
+ }
+ }
+
+ // printf( "TEST: opening .\\test1 for write access.\n" );
+ if (File1 = fopen( "test1.", "w" )) {
+ fprintf( File1, "This is test file 1\n" );
+ // printf( "TEST: closing .\\test1\n" );
+ fclose( File1 );
+ }
+
+ // printf( "TEST: opening .\\test2 for write access (Instaler should noticed contents different)\n" );
+ if (File2 = fopen( "test2.", "w" )) {
+ fprintf( File2, "This is test file 2\n" );
+ // printf( "TEST: closing .\\test2\n" );
+ fclose( File2 );
+ }
+
+ // printf( "TEST: opening .\\test0.tmp for write access.\n" );
+ if (File0 = fopen( "test0.tmp", "w" )) {
+ fprintf( File0, "This is test file tmp files\n" );
+ // printf( "TEST: closing .\\test0.tmp\n" );
+ fclose( File0 );
+ // printf( "TEST: deleting .\\test0 using DeleteFile (open, set, close)\n" );
+ rename("test0.tmp", "test0.fin");
+ }
+
+ RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\Software\\Test" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ // printf( "TEST: opening %wZ for write access\n", &KeyName );
+ Status = NtOpenKey( &KeyHandle,
+ KEY_WRITE,
+ &ObjectAttributes
+ );
+ if (NT_SUCCESS( Status )) {
+ RtlInitUnicodeString( &ValueName, L"Test0" );
+ // printf( "TEST: setting %wZ . %wZ value\n", &KeyName, &ValueName );
+ Status = NtSetValueKey( KeyHandle,
+ &ValueName,
+ 0,
+ REG_SZ,
+ "0",
+ 2 * sizeof( WCHAR )
+ );
+
+ RtlInitUnicodeString( &ValueName, L"Test1" );
+ // printf( "TEST: deleting %wZ . %wZ value\n", &KeyName, &ValueName );
+ Status = NtDeleteValueKey( KeyHandle,
+ &ValueName
+ );
+
+ RtlInitUnicodeString( &ValueName, L"Test2" );
+ // printf( "TEST: setting %wZ . %wZ value\n", &KeyName, &ValueName );
+ Status = NtSetValueKey( KeyHandle,
+ &ValueName,
+ 0,
+ REG_DWORD,
+ &ValueDWord,
+ sizeof( ValueDWord )
+ );
+ RtlInitUnicodeString( &SubKeyName, L"Test3" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ KeyHandle,
+ NULL
+ );
+ // printf( "TEST: opening %wZ\\%wZ for write access\n", &KeyName, &SubKeyName );
+ Status = NtOpenKey( &SubKeyHandle,
+ DELETE | KEY_WRITE,
+ &ObjectAttributes
+ );
+ if (NT_SUCCESS( Status )) {
+ // printf( "TEST: deleting %wZ\\%wZ key and values\n", &KeyName, &SubKeyName );
+ Status = NtDeleteKey( SubKeyHandle );
+ NtClose( SubKeyHandle );
+ }
+
+ RtlInitUnicodeString( &SubKeyName, L"Test4" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ KeyHandle,
+ NULL
+ );
+ // printf( "TEST: creating %wZ\\%wZ for write access\n", &KeyName, &SubKeyName );
+ Status = NtCreateKey( &SubKeyHandle,
+ DELETE | KEY_WRITE,
+ &ObjectAttributes,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+ if (NT_SUCCESS( Status )) {
+ RtlInitUnicodeString( &ValueName, L"Test4" );
+ // printf( "TEST: creating %wZ\\%wZ %wZ value\n", &KeyName, &SubKeyName, &ValueName );
+ Status = NtSetValueKey( SubKeyHandle,
+ &ValueName,
+ 0,
+ REG_DWORD,
+ &ValueDWord,
+ sizeof( ValueDWord )
+ );
+ NtClose( SubKeyHandle );
+ }
+
+ RtlInitUnicodeString( &SubKeyName, L"Test5" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ KeyHandle,
+ NULL
+ );
+ // printf( "TEST: creating %wZ\\%wZ for write access\n", &KeyName, &SubKeyName );
+ Status = NtCreateKey( &SubKeyHandle,
+ DELETE | KEY_WRITE,
+ &ObjectAttributes,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+ if (NT_SUCCESS( Status )) {
+ RtlInitUnicodeString( &ValueName, L"Test5" );
+ // printf( "TEST: creating %wZ\\%wZ %wZ value\n", &KeyName, &SubKeyName, &ValueName );
+ Status = NtSetValueKey( SubKeyHandle,
+ &ValueName,
+ 0,
+ REG_DWORD,
+ &ValueDWord,
+ sizeof( ValueDWord )
+ );
+ // printf( "TEST: deleting %wZ\\%wZ key and values\n", &KeyName, &SubKeyName );
+ Status = NtDeleteKey( SubKeyHandle );
+ NtClose( SubKeyHandle );
+ }
+
+ NtClose( KeyHandle );
+ }
+
+ GetPrivateProfileStringA( "test", NULL, "",
+ AnsiBuffer,
+ sizeof( AnsiBuffer ),
+ ".\\test.ini"
+ );
+
+ GetPrivateProfileStringW( L"test", NULL, L"",
+ UnicodeBuffer,
+ sizeof( UnicodeBuffer ),
+ L".\\test.ini"
+ );
+
+
+ if (!SetCurrentDirectoryA( ".." )) {
+ printf( "TEST: SetCurrentDirectory to '..' failed (%u)\n", GetLastError() );
+ }
+
+ WriteProfileString( L"fonts", L"FooBar", L"1" );
+
+ WriteProfileString( L"fonts", L"Rockwell Italic (TrueType)", L"ROCKI.FOT" );
+
+ if (!SetCurrentDirectoryW( L"test" )) {
+ printf( "TEST: SetCurrentDirectory to 'test' failed (%u)\n", GetLastError() );
+ }
+
+ WritePrivateProfileStringA( "test", "test1", "-1", ".\\test.ini" );
+
+ WritePrivateProfileStringW( L"test", L"test1", L"-2", L".\\test.ini" );
+
+ WritePrivateProfileSectionA( "test1", "test0=0\0test1=1\0test2=2\0", ".\\test.ini" );
+
+ WritePrivateProfileSectionW( L"test2", L"test0=0\0test1=1\0test2=2\0", L".\\test.ini" );
+
+ return 0;
+}
diff --git a/private/sdktools/instaler/testins1.c b/private/sdktools/instaler/testins1.c
new file mode 100644
index 000000000..4d2c0a0f1
--- /dev/null
+++ b/private/sdktools/instaler/testins1.c
@@ -0,0 +1,384 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ testins1.c
+
+Abstract:
+
+ This is a test program that issues various Kernel 32 file/registry/INI file
+ API calls so that we can see if the INSTALER program figures out correctly
+ what is being done.
+
+Author:
+
+ Steve Wood (stevewo) 13-Aug-1994
+
+--*/
+
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntdbg.h>
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+
+UCHAR AnsiBuffer[ MAX_PATH ];
+WCHAR UnicodeBuffer[ MAX_PATH ];
+
+
+int
+_CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ FILE *File0;
+ FILE *File1;
+ FILE *File2;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ BOOLEAN TranslationStatus;
+ UNICODE_STRING FileName;
+ RTL_RELATIVE_NAME RelativeName;
+ PVOID FreeBuffer;
+ UNICODE_STRING KeyName;
+ UNICODE_STRING SubKeyName;
+ UNICODE_STRING ValueName;
+ HANDLE FileHandle, FindHandle, KeyHandle, SubKeyHandle;
+ ULONG ValueDWord = 0x12345678;
+ WIN32_FIND_DATAW FindFileData;
+ LPSTR s1;
+ PWSTR s2;
+ UCHAR AnsiBuffer[ MAX_PATH ];
+ WCHAR UnicodeBuffer[ MAX_PATH ];
+ DWORD dwVersion;
+ OSVERSIONINFO VersionInfo;
+ HKEY hKey;
+
+ //
+ // File operations we want to test:
+ //
+ // Creating a new file.
+ // Deleting that file using DeleteFile (which does NtOpenFile and NtSetInformationFile
+ // with Delete Dispostion). (INSTALER should use this to forget about create).
+ // Creating a new file with the same name.
+ // Deleting that file using NtDeleteFile. (again INSTALER should not care).
+ //
+ // Open the TEST1 file for read access (INSTALER should not care).
+ // Open the TEST2 file for write access (INSTALER should not care).
+ // Open the TEST2 file for write access (INSTALER should not care).
+ // Open the TEST2 file for write access (INSTALER should not care).
+ //
+ //
+
+ // printf( "TEST: GetFileAttributes( .\\test1 )\n" );
+ GetFileAttributesA( ".\\test1" );
+#if 0
+ dwVersion = GetVersion();
+ if ((dwVersion >> 30) ^ 0x2 == VER_PLATFORM_WIN32_WINDOWS) {
+ printf( "GetVersion returns Windows 95\n" );
+ }
+ else
+ if ((dwVersion >> 30) ^ 0x2 == VER_PLATFORM_WIN32_NT) {
+ printf( "GetVersion returns Windows NT\n" );
+ }
+ else {
+ printf( "GetVersion returns %x\n", dwVersion );
+ }
+ fflush(stdout);
+
+ VersionInfo.dwOSVersionInfoSize = sizeof( VersionInfo );
+ GetVersionEx( &VersionInfo );
+ if (VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+ printf( "GetVersionEx returns Windows 95\n" );
+ }
+ else
+ if (VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ printf( "GetVersionEx returns Windows NT\n" );
+ }
+ else {
+ printf( "GetVersionEx returns %x\n", VersionInfo.dwPlatformId );
+ }
+ fflush(stdout);
+
+ if (RegConnectRegistryA( "\\\\stevewo_dbgr", HKEY_USERS, &hKey )) {
+ printf( "RegConnectRegistryA( \\stevewo_dbgr ) failed (hKey == %x).\n", hKey );
+ }
+ else {
+ printf( "RegConnectRegistryA( \\stevewo_dbgr ) succeeded (hKey == %x).\n", hKey );
+ RegCloseKey( hKey );
+ }
+
+ if (RegConnectRegistryW( L"\\\\stevewo_dbgr", HKEY_USERS, &hKey )) {
+ printf( "RegConnectRegistryW( \\stevewo_dbgr ) failed (hKey == %x).\n", hKey );
+ }
+ else {
+ printf( "RegConnectRegistryW( \\stevewo_dbgr ) succeeded (hKey == %x).\n", hKey );
+ RegCloseKey( hKey );
+ }
+#endif
+
+ RtlInitUnicodeString( &FileName, L"\\DosDevices\\A:" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ // printf( "TEST: NtOpenFile( %wZ ) should succeed without touching floppy.\n", &FileName );
+ Status = NtOpenFile( &FileHandle,
+ SYNCHRONIZE | FILE_READ_ATTRIBUTES,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
+ );
+ if (!NT_SUCCESS( Status )) {
+ printf( "TEST: Failed - Status == %x\n", Status );
+ }
+ else {
+ NtClose( FileHandle );
+ }
+
+ // printf( "TEST: FindFirstFileW( C:\\*.* should fail.\n" );
+ FindHandle = FindFirstFileW( L"C:\\*.*", &FindFileData );
+ if (FindHandle != INVALID_HANDLE_VALUE) {
+ printf( "TEST: *** oops, it worked.\n" );
+ FindClose( FindHandle );
+ }
+ // printf( "TEST: FindFirstFileW( \\TMP\\*.* should work.\n" );
+ FindHandle = FindFirstFileW( L"\\TMP\\*.*", &FindFileData );
+ if (FindHandle == INVALID_HANDLE_VALUE) {
+ printf( "TEST: *** oops, it failed.\n" );
+ }
+ else {
+ FindClose( FindHandle );
+ }
+
+ // printf( "TEST: opening .\\test0 for write access.\n" );
+ if (File0 = fopen( "test0.", "w" )) {
+ fprintf( File0, "This is test file 0\n" );
+ // printf( "TEST: closing .\\test0\n" );
+ fclose( File0 );
+ // printf( "TEST: deleting .\\test0 using DeleteFile (open, set, close)\n" );
+ DeleteFile( L"test0" );
+ }
+
+ // printf( "TEST: opening .\\test0 for write access.\n" );
+ if (File0 = fopen( "test0.", "w" )) {
+ fprintf( File0, "This is test file 0\n" );
+ // printf( "TEST: closing .\\test0\n" );
+ fclose( File0 );
+
+ TranslationStatus = RtlDosPathNameToNtPathName_U(
+ L"test0",
+ &FileName,
+ NULL,
+ &RelativeName
+ );
+
+ if (TranslationStatus ) {
+ FreeBuffer = FileName.Buffer;
+ if ( RelativeName.RelativeName.Length ) {
+ FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
+ }
+ else {
+ RelativeName.ContainingDirectory = NULL;
+ }
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ RelativeName.ContainingDirectory,
+ NULL
+ );
+ // printf( "TEST: deleting .\\test0 using NtDeleteFile\n" );
+ Status = NtDeleteFile( &ObjectAttributes );
+ RtlFreeHeap( RtlProcessHeap(), 0, FreeBuffer );
+ }
+ }
+
+ // printf( "TEST: opening .\\test1 for write access.\n" );
+ if (File1 = fopen( "test1.", "w" )) {
+ fprintf( File1, "This is test file 1\n" );
+ // printf( "TEST: closing .\\test1\n" );
+ fclose( File1 );
+ }
+
+ // printf( "TEST: opening .\\test2 for write access (Instaler should noticed contents different)\n" );
+ if (File2 = fopen( "test2.", "w" )) {
+ fprintf( File2, "This is test file 2\n" );
+ // printf( "TEST: closing .\\test2\n" );
+ fclose( File2 );
+ }
+
+ // printf( "TEST: opening .\\test0.tmp for write access.\n" );
+ if (File0 = fopen( "test0.tmp", "w" )) {
+ fprintf( File0, "This is test file tmp files\n" );
+ // printf( "TEST: closing .\\test0.tmp\n" );
+ fclose( File0 );
+ // printf( "TEST: deleting .\\test0 using DeleteFile (open, set, close)\n" );
+ rename("test0.tmp", "test0.fin");
+ }
+
+ RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\Software\\Test" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ // printf( "TEST: opening %wZ for write access\n", &KeyName );
+ Status = NtOpenKey( &KeyHandle,
+ KEY_WRITE,
+ &ObjectAttributes
+ );
+ if (NT_SUCCESS( Status )) {
+ RtlInitUnicodeString( &ValueName, L"Test0" );
+ // printf( "TEST: setting %wZ . %wZ value\n", &KeyName, &ValueName );
+ Status = NtSetValueKey( KeyHandle,
+ &ValueName,
+ 0,
+ REG_SZ,
+ "0",
+ 2 * sizeof( WCHAR )
+ );
+
+ RtlInitUnicodeString( &ValueName, L"Test1" );
+ // printf( "TEST: deleting %wZ . %wZ value\n", &KeyName, &ValueName );
+ Status = NtDeleteValueKey( KeyHandle,
+ &ValueName
+ );
+
+ RtlInitUnicodeString( &ValueName, L"Test2" );
+ // printf( "TEST: setting %wZ . %wZ value\n", &KeyName, &ValueName );
+ Status = NtSetValueKey( KeyHandle,
+ &ValueName,
+ 0,
+ REG_DWORD,
+ &ValueDWord,
+ sizeof( ValueDWord )
+ );
+ RtlInitUnicodeString( &SubKeyName, L"Test3" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ KeyHandle,
+ NULL
+ );
+ // printf( "TEST: opening %wZ\\%wZ for write access\n", &KeyName, &SubKeyName );
+ Status = NtOpenKey( &SubKeyHandle,
+ DELETE | KEY_WRITE,
+ &ObjectAttributes
+ );
+ if (NT_SUCCESS( Status )) {
+ // printf( "TEST: deleting %wZ\\%wZ key and values\n", &KeyName, &SubKeyName );
+ Status = NtDeleteKey( SubKeyHandle );
+ NtClose( SubKeyHandle );
+ }
+
+ RtlInitUnicodeString( &SubKeyName, L"Test4" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ KeyHandle,
+ NULL
+ );
+ // printf( "TEST: creating %wZ\\%wZ for write access\n", &KeyName, &SubKeyName );
+ Status = NtCreateKey( &SubKeyHandle,
+ DELETE | KEY_WRITE,
+ &ObjectAttributes,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+ if (NT_SUCCESS( Status )) {
+ RtlInitUnicodeString( &ValueName, L"Test4" );
+ // printf( "TEST: creating %wZ\\%wZ %wZ value\n", &KeyName, &SubKeyName, &ValueName );
+ Status = NtSetValueKey( SubKeyHandle,
+ &ValueName,
+ 0,
+ REG_DWORD,
+ &ValueDWord,
+ sizeof( ValueDWord )
+ );
+ NtClose( SubKeyHandle );
+ }
+
+ RtlInitUnicodeString( &SubKeyName, L"Test5" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ KeyHandle,
+ NULL
+ );
+ // printf( "TEST: creating %wZ\\%wZ for write access\n", &KeyName, &SubKeyName );
+ Status = NtCreateKey( &SubKeyHandle,
+ DELETE | KEY_WRITE,
+ &ObjectAttributes,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+ if (NT_SUCCESS( Status )) {
+ RtlInitUnicodeString( &ValueName, L"Test5" );
+ // printf( "TEST: creating %wZ\\%wZ %wZ value\n", &KeyName, &SubKeyName, &ValueName );
+ Status = NtSetValueKey( SubKeyHandle,
+ &ValueName,
+ 0,
+ REG_DWORD,
+ &ValueDWord,
+ sizeof( ValueDWord )
+ );
+ // printf( "TEST: deleting %wZ\\%wZ key and values\n", &KeyName, &SubKeyName );
+ Status = NtDeleteKey( SubKeyHandle );
+ NtClose( SubKeyHandle );
+ }
+
+ NtClose( KeyHandle );
+ }
+
+ GetPrivateProfileStringA( "test", NULL, "",
+ AnsiBuffer,
+ sizeof( AnsiBuffer ),
+ ".\\test.ini"
+ );
+
+ GetPrivateProfileStringW( L"test", NULL, L"",
+ UnicodeBuffer,
+ sizeof( UnicodeBuffer ),
+ L".\\test.ini"
+ );
+
+
+ if (!SetCurrentDirectoryA( ".." )) {
+ printf( "TEST: SetCurrentDirectory to '..' failed (%u)\n", GetLastError() );
+ }
+
+ WriteProfileString( L"fonts", L"FooBar", L"1" );
+
+ WriteProfileString( L"fonts", L"Rockwell Italic (TrueType)", L"ROCKI.FOT" );
+
+ if (!SetCurrentDirectoryW( L"test" )) {
+ printf( "TEST: SetCurrentDirectory to 'test' failed (%u)\n", GetLastError() );
+ }
+
+ WritePrivateProfileStringA( "test", "test1", "-1", ".\\test.ini" );
+
+ WritePrivateProfileStringW( L"test", L"test1", L"-2", L".\\test.ini" );
+
+ WritePrivateProfileSectionA( "test1", "test0=0\0test1=1\0test2=2\0", ".\\test.ini" );
+
+ WritePrivateProfileSectionW( L"test2", L"test0=0\0test1=1\0test2=2\0", L".\\test.ini" );
+
+ return 0;
+}
diff --git a/private/sdktools/instaler/testins1.rc b/private/sdktools/instaler/testins1.rc
new file mode 100644
index 000000000..75c36422f
--- /dev/null
+++ b/private/sdktools/instaler/testins1.rc
@@ -0,0 +1,12 @@
+#include "instaler.h"
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Windows NT Instaler Test Program"
+#define VER_INTERNALNAME_STR "TESTINST\0"
+#define VER_ORIGINALFILENAME_STR "TESTINST.EXE"
+
+#include "common.ver"
+
+RCINCLUDE errormsg.rc
diff --git a/private/sdktools/instaler/testins2.c b/private/sdktools/instaler/testins2.c
new file mode 100644
index 000000000..23513d751
--- /dev/null
+++ b/private/sdktools/instaler/testins2.c
@@ -0,0 +1,387 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ testins2.c
+
+Abstract:
+
+ This is a test program that issues various Kernel 32 file/registry/INI file
+ API calls so that we can see if the INSTALER program figures out correctly
+ what is being done.
+
+Author:
+
+ Steve Wood (stevewo) 13-Aug-1994
+
+--*/
+
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntdbg.h>
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+
+UCHAR AnsiBuffer[ MAX_PATH ];
+WCHAR UnicodeBuffer[ MAX_PATH ];
+
+
+int
+_CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ FILE *File0;
+ FILE *File1;
+ FILE *File2;
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ BOOLEAN TranslationStatus;
+ UNICODE_STRING FileName;
+ RTL_RELATIVE_NAME RelativeName;
+ PVOID FreeBuffer;
+ UNICODE_STRING KeyName;
+ UNICODE_STRING SubKeyName;
+ UNICODE_STRING ValueName;
+ HANDLE FileHandle, FindHandle, KeyHandle, SubKeyHandle;
+ ULONG ValueDWord = 0x12345679;
+ WIN32_FIND_DATAW FindFileData;
+ LPSTR s1;
+ PWSTR s2;
+ UCHAR AnsiBuffer[ MAX_PATH ];
+ WCHAR UnicodeBuffer[ MAX_PATH ];
+ DWORD dwVersion;
+ OSVERSIONINFO VersionInfo;
+ HKEY hKey;
+
+ //
+ // File operations we want to test:
+ //
+ // Creating a new file.
+ // Deleting that file using DeleteFile (which does NtOpenFile and NtSetInformationFile
+ // with Delete Dispostion). (INSTALER should use this to forget about create).
+ // Creating a new file with the same name.
+ // Deleting that file using NtDeleteFile. (again INSTALER should not care).
+ //
+ // Open the TEST1 file for read access (INSTALER should not care).
+ // Open the TEST2 file for write access (INSTALER should not care).
+ // Open the TEST2 file for write access (INSTALER should not care).
+ // Open the TEST2 file for write access (INSTALER should not care).
+ //
+ //
+
+ // printf( "TEST: GetFileAttributes( .\\test1 )\n" );
+ GetFileAttributesA( ".\\test1" );
+#if 0
+ dwVersion = GetVersion();
+ if ((dwVersion >> 30) ^ 0x2 == VER_PLATFORM_WIN32_WINDOWS) {
+ printf( "GetVersion returns Windows 95\n" );
+ }
+ else
+ if ((dwVersion >> 30) ^ 0x2 == VER_PLATFORM_WIN32_NT) {
+ printf( "GetVersion returns Windows NT\n" );
+ }
+ else {
+ printf( "GetVersion returns %x\n", dwVersion );
+ }
+ fflush(stdout);
+
+ VersionInfo.dwOSVersionInfoSize = sizeof( VersionInfo );
+ GetVersionEx( &VersionInfo );
+ if (VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
+ printf( "GetVersionEx returns Windows 95\n" );
+ }
+ else
+ if (VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ printf( "GetVersionEx returns Windows NT\n" );
+ }
+ else {
+ printf( "GetVersionEx returns %x\n", VersionInfo.dwPlatformId );
+ }
+ fflush(stdout);
+
+ if (RegConnectRegistryA( "\\\\stevewo_dbgr", HKEY_USERS, &hKey )) {
+ printf( "RegConnectRegistryA( \\stevewo_dbgr ) failed (hKey == %x).\n", hKey );
+ }
+ else {
+ printf( "RegConnectRegistryA( \\stevewo_dbgr ) succeeded (hKey == %x).\n", hKey );
+ RegCloseKey( hKey );
+ }
+
+ if (RegConnectRegistryW( L"\\\\stevewo_dbgr", HKEY_USERS, &hKey )) {
+ printf( "RegConnectRegistryW( \\stevewo_dbgr ) failed (hKey == %x).\n", hKey );
+ }
+ else {
+ printf( "RegConnectRegistryW( \\stevewo_dbgr ) succeeded (hKey == %x).\n", hKey );
+ RegCloseKey( hKey );
+ }
+#endif
+
+ RtlInitUnicodeString( &FileName, L"\\DosDevices\\A:" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ // printf( "TEST: NtOpenFile( %wZ ) should succeed without touching floppy.\n", &FileName );
+ Status = NtOpenFile( &FileHandle,
+ SYNCHRONIZE | FILE_READ_ATTRIBUTES,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE
+ );
+ if (!NT_SUCCESS( Status )) {
+ printf( "TEST: Failed - Status == %x\n", Status );
+ }
+ else {
+ NtClose( FileHandle );
+ }
+
+ // printf( "TEST: FindFirstFileW( C:\\*.* should fail.\n" );
+ FindHandle = FindFirstFileW( L"C:\\*.*", &FindFileData );
+ if (FindHandle != INVALID_HANDLE_VALUE) {
+ printf( "TEST: *** oops, it worked.\n" );
+ FindClose( FindHandle );
+ }
+ // printf( "TEST: FindFirstFileW( \\TMP\\*.* should work.\n" );
+ FindHandle = FindFirstFileW( L"\\TMP\\*.*", &FindFileData );
+ if (FindHandle == INVALID_HANDLE_VALUE) {
+ printf( "TEST: *** oops, it failed.\n" );
+ }
+ else {
+ FindClose( FindHandle );
+ }
+
+ // printf( "TEST: opening .\\test0 for write access.\n" );
+ if (File0 = fopen( "test0.", "w" )) {
+ fprintf( File0, "This is test file 0\n" );
+ // printf( "TEST: closing .\\test0\n" );
+ fclose( File0 );
+ // printf( "TEST: deleting .\\test0 using DeleteFile (open, set, close)\n" );
+ DeleteFile( L"test0" );
+ }
+
+ // printf( "TEST: opening .\\test0 for write access.\n" );
+ if (File0 = fopen( "test0.", "w" )) {
+ fprintf( File0, "This is test file 0\n" );
+ // printf( "TEST: closing .\\test0\n" );
+ fclose( File0 );
+#if 0
+ TranslationStatus = RtlDosPathNameToNtPathName_U(
+ L"test0",
+ &FileName,
+ NULL,
+ &RelativeName
+ );
+
+ if (TranslationStatus ) {
+ FreeBuffer = FileName.Buffer;
+ if ( RelativeName.RelativeName.Length ) {
+ FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
+ }
+ else {
+ RelativeName.ContainingDirectory = NULL;
+ }
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ RelativeName.ContainingDirectory,
+ NULL
+ );
+ // printf( "TEST: deleting .\\test0 using NtDeleteFile\n" );
+ Status = NtDeleteFile( &ObjectAttributes );
+ RtlFreeHeap( RtlProcessHeap(), 0, FreeBuffer );
+ }
+#endif
+ }
+
+ // printf( "TEST: opening .\\test1 for write access.\n" );
+ if (File1 = fopen( "test1.", "w" )) {
+ fprintf( File1, "This is test file 1a\n" );
+ // printf( "TEST: closing .\\test1\n" );
+ fclose( File1 );
+ }
+
+ // printf( "TEST: opening .\\test2 for write access (Instaler should noticed contents different)\n" );
+ if (File2 = fopen( "test2.", "w" )) {
+ fprintf( File2, "This is test file 2\n" );
+ // printf( "TEST: closing .\\test2\n" );
+ fclose( File2 );
+ }
+
+ // printf( "TEST: opening .\\test0.tmp for write access.\n" );
+ if (File0 = fopen( "test0.tmp", "w" )) {
+ fprintf( File0, "This is test file tmp files\n" );
+ // printf( "TEST: closing .\\test0.tmp\n" );
+ fclose( File0 );
+ // printf( "TEST: deleting .\\test0 using DeleteFile (open, set, close)\n" );
+ rename("test0.tmp", "test0.fin");
+ }
+ rename("test1.", "test2.fin");
+ rename("test3.", "test3.fin");
+
+ RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\Software\\Test" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ // printf( "TEST: opening %wZ for write access\n", &KeyName );
+ Status = NtOpenKey( &KeyHandle,
+ KEY_WRITE,
+ &ObjectAttributes
+ );
+ if (NT_SUCCESS( Status )) {
+ RtlInitUnicodeString( &ValueName, L"Test0" );
+ // printf( "TEST: setting %wZ . %wZ value\n", &KeyName, &ValueName );
+ Status = NtSetValueKey( KeyHandle,
+ &ValueName,
+ 0,
+ REG_SZ,
+ "1",
+ 2 * sizeof( WCHAR )
+ );
+
+ RtlInitUnicodeString( &ValueName, L"Test1" );
+ // printf( "TEST: deleting %wZ . %wZ value\n", &KeyName, &ValueName );
+ Status = NtDeleteValueKey( KeyHandle,
+ &ValueName
+ );
+
+ RtlInitUnicodeString( &ValueName, L"Test2" );
+ // printf( "TEST: setting %wZ . %wZ value\n", &KeyName, &ValueName );
+ Status = NtSetValueKey( KeyHandle,
+ &ValueName,
+ 0,
+ REG_DWORD,
+ &ValueDWord,
+ sizeof( ValueDWord )
+ );
+ RtlInitUnicodeString( &SubKeyName, L"Test3" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ KeyHandle,
+ NULL
+ );
+ // printf( "TEST: opening %wZ\\%wZ for write access\n", &KeyName, &SubKeyName );
+ Status = NtOpenKey( &SubKeyHandle,
+ DELETE | KEY_WRITE,
+ &ObjectAttributes
+ );
+ if (NT_SUCCESS( Status )) {
+ // printf( "TEST: deleting %wZ\\%wZ key and values\n", &KeyName, &SubKeyName );
+ Status = NtDeleteKey( SubKeyHandle );
+ NtClose( SubKeyHandle );
+ }
+
+ RtlInitUnicodeString( &SubKeyName, L"Test4" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ KeyHandle,
+ NULL
+ );
+ // printf( "TEST: creating %wZ\\%wZ for write access\n", &KeyName, &SubKeyName );
+ Status = NtCreateKey( &SubKeyHandle,
+ DELETE | KEY_WRITE,
+ &ObjectAttributes,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+ if (NT_SUCCESS( Status )) {
+ RtlInitUnicodeString( &ValueName, L"Test4" );
+ // printf( "TEST: creating %wZ\\%wZ %wZ value\n", &KeyName, &SubKeyName, &ValueName );
+ Status = NtSetValueKey( SubKeyHandle,
+ &ValueName,
+ 0,
+ REG_DWORD,
+ &ValueDWord,
+ sizeof( ValueDWord )
+ );
+ NtClose( SubKeyHandle );
+ }
+
+ RtlInitUnicodeString( &SubKeyName, L"Test5" );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ KeyHandle,
+ NULL
+ );
+ // printf( "TEST: creating %wZ\\%wZ for write access\n", &KeyName, &SubKeyName );
+ Status = NtCreateKey( &SubKeyHandle,
+ DELETE | KEY_WRITE,
+ &ObjectAttributes,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+ if (NT_SUCCESS( Status )) {
+ RtlInitUnicodeString( &ValueName, L"Test5" );
+ // printf( "TEST: creating %wZ\\%wZ %wZ value\n", &KeyName, &SubKeyName, &ValueName );
+ Status = NtSetValueKey( SubKeyHandle,
+ &ValueName,
+ 0,
+ REG_DWORD,
+ &ValueDWord,
+ sizeof( ValueDWord )
+ );
+ // printf( "TEST: deleting %wZ\\%wZ key and values\n", &KeyName, &SubKeyName );
+ Status = NtDeleteKey( SubKeyHandle );
+ NtClose( SubKeyHandle );
+ }
+
+ NtClose( KeyHandle );
+ }
+
+ GetPrivateProfileStringA( "test", NULL, "",
+ AnsiBuffer,
+ sizeof( AnsiBuffer ),
+ ".\\test.ini"
+ );
+
+ GetPrivateProfileStringW( L"test", NULL, L"",
+ UnicodeBuffer,
+ sizeof( UnicodeBuffer ),
+ L".\\test.ini"
+ );
+
+
+ if (!SetCurrentDirectoryA( ".." )) {
+ printf( "TEST: SetCurrentDirectory to '..' failed (%u)\n", GetLastError() );
+ }
+
+ WriteProfileString( L"fonts", L"FooBar", L"2" );
+
+ WriteProfileString( L"fonts", L"Rockwell Italic (TrueType)", L"ROCKI.FOT" );
+
+ if (!SetCurrentDirectoryW( L"test" )) {
+ printf( "TEST: SetCurrentDirectory to 'test' failed (%u)\n", GetLastError() );
+ }
+
+ WritePrivateProfileStringA( "test", "test1", "-1", ".\\test.ini" );
+
+ WritePrivateProfileStringW( L"test", L"test1", L"-3", L".\\test.ini" );
+
+ WritePrivateProfileSectionA( "test1", "test0=0\0test1=1\0test2=2\0", ".\\test.ini" );
+
+ WritePrivateProfileSectionW( L"test2", L"test0=0\0test1=2\0test2=2\0", L".\\test.ini" );
+
+ return 0;
+}
diff --git a/private/sdktools/instaler/testins2.rc b/private/sdktools/instaler/testins2.rc
new file mode 100644
index 000000000..75c36422f
--- /dev/null
+++ b/private/sdktools/instaler/testins2.rc
@@ -0,0 +1,12 @@
+#include "instaler.h"
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Windows NT Instaler Test Program"
+#define VER_INTERNALNAME_STR "TESTINST\0"
+#define VER_ORIGINALFILENAME_STR "TESTINST.EXE"
+
+#include "common.ver"
+
+RCINCLUDE errormsg.rc
diff --git a/private/sdktools/instaler/undoinst.c b/private/sdktools/instaler/undoinst.c
new file mode 100644
index 000000000..30e8ace5a
--- /dev/null
+++ b/private/sdktools/instaler/undoinst.c
@@ -0,0 +1,831 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ undoinst.c
+
+Abstract:
+
+ This program undoes the actions described by an Installation Modification Log file
+ created by the INSTALER program
+
+Author:
+
+ Steve Wood (stevewo) 15-Jan-1996
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include "instutil.h"
+#include "iml.h"
+
+BOOLEAN RedoScript;
+BOOLEAN VerboseOutput;
+
+int
+ProcessUndoIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pImlUndo,
+ PINSTALLATION_MODIFICATION_LOGFILE pImlRedo
+ );
+
+int
+_CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ int Result;
+ char *s;
+ PINSTALLATION_MODIFICATION_LOGFILE pImlUndo;
+ PINSTALLATION_MODIFICATION_LOGFILE pImlRedo;
+ USHORT RedoScriptId;
+
+ InitCommonCode( "UNDOINST",
+ "[-r] [-v]",
+ "-r replace contents of input .IML file with redo script to undo the undo\n"
+ "-v verbose output\n"
+ );
+ RedoScript = FALSE;
+ VerboseOutput = FALSE;
+ while (--argc) {
+ s = *++argv;
+ if (*s == '-' || *s == '/') {
+ while (*++s) {
+ switch( tolower( *s ) ) {
+ case 'r':
+ RedoScript = TRUE;
+ break;
+ case 'v':
+ VerboseOutput = TRUE;
+ break;
+ default:
+ CommonSwitchProcessing( &argc, &argv, *s );
+ break;
+ }
+ }
+ }
+ else
+ if (!CommonArgProcessing( &argc, &argv )) {
+ Usage( "Arguments not supported - '%s'", (ULONG)s );
+ }
+ }
+
+ if (ImlPath == NULL) {
+ Usage( "Must specify an installation name as first argument", 0 );
+ }
+
+ if (!SetCurrentDirectory( InstalerDirectory )) {
+ FatalError( "Unable to change to '%ws' directory (%u)",
+ (ULONG)InstalerDirectory,
+ GetLastError()
+ );
+ }
+
+ pImlUndo = LoadIml( ImlPath );
+ if (pImlUndo == NULL) {
+ FatalError( "Unable to open '%ws' (%u)",
+ (ULONG)ImlPath,
+ GetLastError()
+ );
+ }
+ if (RedoScript) {
+ RedoScriptId = 0;
+ if (CreateBackupFileName( &RedoScriptId ) == NULL) {
+ FatalError( "Unable to create temporary file for redo script (%u)\n",
+ GetLastError(),
+ 0
+ );
+ }
+
+ pImlRedo = CreateIml( FormatTempFileName( InstalerDirectory, &RedoScriptId ), TRUE );
+ if (pImlRedo == NULL) {
+ FatalError( "Unable to create redo script '%ws' (%u)\n",
+ (ULONG)FormatTempFileName( InstalerDirectory, &RedoScriptId ),
+ GetLastError()
+ );
+ }
+ }
+ else {
+ pImlRedo = NULL;
+ }
+
+ Result = ProcessUndoIml( pImlUndo, pImlRedo );
+
+ CloseIml( pImlUndo );
+ if (pImlRedo != NULL) {
+ CloseIml( pImlRedo );
+ if (!MoveFileEx( FormatTempFileName( InstalerDirectory, &RedoScriptId ),
+ ImlPath,
+ MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED
+ )
+ ) {
+ FatalError( "Unable to rename redo script '%ws' (%u)\n",
+ (ULONG)FormatTempFileName( InstalerDirectory, &RedoScriptId ),
+ GetLastError()
+ );
+ }
+ }
+
+ exit( Result );
+ return Result;
+}
+
+
+BOOLEAN
+DeleteFileOrDirectory(
+ PWSTR Name
+ )
+{
+ DWORD FileAttributes;
+
+ FileAttributes = GetFileAttributes( Name );
+ if (FileAttributes == 0xFFFFFFFF) {
+ return TRUE;
+ }
+
+ if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ return RemoveDirectory( Name );
+ }
+ else {
+ return DeleteFile( Name );
+ }
+}
+
+BOOLEAN
+ProcessUndoFileIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pImlUndo,
+ PIML_FILE_RECORD pFile
+ )
+{
+ PIML_FILE_RECORD_CONTENTS pOldFile;
+ PIML_FILE_RECORD_CONTENTS pNewFile;
+ USHORT UniqueId = 0;
+ HANDLE FileHandle;
+ PWSTR BackupFileName;
+ DWORD FileAttributes;
+ DWORD BytesWritten;
+
+ pOldFile = MP( PIML_FILE_RECORD_CONTENTS, pImlUndo, pFile->OldFile );
+ pNewFile = MP( PIML_FILE_RECORD_CONTENTS, pImlUndo, pFile->NewFile );
+ printf( " %ws - ", MP( PWSTR, pImlUndo, pFile->Name ) );
+ switch( pFile->Action ) {
+ case CreateNewFile:
+ printf( "deleting" );
+ SetFileAttributes( MP( PWSTR, pImlUndo, pFile->Name ),
+ FILE_ATTRIBUTE_NORMAL
+ );
+ if (!DeleteFileOrDirectory( MP( PWSTR, pImlUndo, pFile->Name ) )) {
+ printf( " - error (%u)", GetLastError() );
+ }
+ printf( "\n" );
+ break;
+
+ case ModifyOldFile:
+ case DeleteOldFile:
+ FileAttributes = GetFileAttributes( MP( PWSTR, pImlUndo, pFile->Name ) );
+ printf( "restoring old file" );
+ if (FileAttributes != 0xFFFFFFFF) {
+ SetFileAttributes( MP( PWSTR, pImlUndo, pFile->Name ),
+ FILE_ATTRIBUTE_NORMAL
+ );
+ BackupFileName = CreateBackupFileName( &UniqueId );
+ if (BackupFileName == NULL) {
+ printf( " - unable to find temporary name for restore\n" );
+ break;
+ }
+ else
+ if (!MoveFile( MP( PWSTR, pImlUndo, pFile->Name ),
+ BackupFileName
+ )
+ ) {
+ printf( " - unable to rename existing to temporary name (%u)\n",
+ GetLastError()
+ );
+ break;
+ }
+ }
+ else {
+ BackupFileName = NULL;
+ }
+
+ if (pOldFile != NULL) {
+ if (pOldFile->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (!CreateDirectory( MP( PWSTR, pImlUndo, pFile->Name ), NULL )) {
+ printf( " - unable to create directory (%u)",
+ GetLastError()
+ );
+ }
+ }
+ else {
+ FileHandle = CreateFile( MP( PWSTR, pImlUndo, pFile->Name ),
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ CREATE_NEW,
+ 0,
+ NULL
+ );
+ if (FileHandle != INVALID_HANDLE_VALUE) {
+ if (WriteFile( FileHandle,
+ MP( PVOID, pImlUndo, pOldFile->Contents ),
+ pOldFile->FileSize,
+ &BytesWritten,
+ NULL
+ ) &&
+ BytesWritten == pOldFile->FileSize
+ ) {
+ if (!SetFileAttributes( MP( PWSTR, pImlUndo, pFile->Name ),
+ pOldFile->FileAttributes
+ )
+ ) {
+ printf( " - unable to restore attributes (%u)",
+ GetLastError()
+ );
+ }
+ else
+ if (!SetFileTime( FileHandle,
+ &pOldFile->LastWriteTime,
+ &pOldFile->LastWriteTime,
+ &pOldFile->LastWriteTime
+ )
+ ) {
+ printf( " - unable to restore last write time (%u)",
+ GetLastError()
+ );
+ }
+ else
+ if (BackupFileName != NULL) {
+ DeleteFile( BackupFileName );
+ BackupFileName = NULL;
+ }
+ }
+ else {
+ printf( " - unable to restore contents (%u)",
+ GetLastError()
+ );
+ }
+
+ CloseHandle( FileHandle );
+ }
+ else {
+ printf( " - unable to create file (%u)",
+ GetLastError()
+ );
+ }
+ }
+ }
+ else {
+ printf( " - old contents missing from .IML file" );
+ }
+
+ if (BackupFileName != NULL) {
+ DeleteFile( MP( PWSTR, pImlUndo, pFile->Name ) );
+ MoveFile( BackupFileName, MP( PWSTR, pImlUndo, pFile->Name ) );
+ }
+ printf( "\n" );
+ break;
+
+ case ModifyFileDateTime:
+ printf( "restoring date/time\n" );
+ FileHandle = CreateFile( MP( PWSTR, pImlUndo, pFile->Name ),
+ FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+ if (FileHandle != INVALID_HANDLE_VALUE) {
+ if (!SetFileTime( FileHandle,
+ &pOldFile->LastWriteTime,
+ &pOldFile->LastWriteTime,
+ &pOldFile->LastWriteTime
+ )
+ ) {
+ printf( " - unable to restore last write time (%u)",
+ GetLastError()
+ );
+ }
+
+ CloseHandle( FileHandle );
+ }
+ else {
+ printf( " - unable to open file (%u)",
+ GetLastError()
+ );
+ }
+ break;
+
+ case ModifyFileAttributes:
+ printf( "restoring attributes" );
+ if (!SetFileAttributes( MP( PWSTR, pImlUndo, pFile->Name ),
+ pOldFile->FileAttributes
+ )
+ ) {
+ printf( " - unable to restore attributes (%u)",
+ GetLastError()
+ );
+ }
+ printf( "\n" );
+ break;
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+ProcessRedoFileIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pImlUndo,
+ PIML_FILE_RECORD pFile,
+ PINSTALLATION_MODIFICATION_LOGFILE pImlRedo
+ )
+{
+ HANDLE FindHandle;
+ WIN32_FIND_DATA FindFileData;
+
+ if (pFile->Action == CreateNewFile) {
+ //
+ // Created a new file. So do the same in the redo
+ // script, with the existing contents of the new file
+ //
+ ImlAddFileRecord( pImlRedo,
+ CreateNewFile,
+ MP( PWSTR, pImlUndo, pFile->Name ),
+ NULL,
+ NULL,
+ 0
+ );
+ }
+ else
+ if (pFile->Action == ModifyOldFile) {
+ //
+ // Modified an existing file. Create a similar record
+ // in the redo script that will hold the new contents
+ //
+ ImlAddFileRecord( pImlRedo,
+ ModifyOldFile,
+ MP( PWSTR, pImlUndo, pFile->Name ),
+ NULL,
+ NULL,
+ 0
+ );
+ }
+ else {
+ //
+ // Modified the file attributes and/or date and time. Get the current
+ // values and save them in the redo script
+ //
+ FindHandle = FindFirstFile( MP( PWSTR, pImlUndo, pFile->Name ),
+ &FindFileData
+ );
+ if (FindHandle != INVALID_HANDLE_VALUE) {
+ ImlAddFileRecord( pImlRedo,
+ ModifyFileDateTime,
+ MP( PWSTR, pImlUndo, pFile->Name ),
+ NULL,
+ &FindFileData.ftLastWriteTime,
+ FindFileData.dwFileAttributes
+ );
+ FindClose( FindHandle );
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+ProcessUndoKeyIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pImlUndo,
+ PIML_KEY_RECORD pKey
+ )
+{
+ PIML_VALUE_RECORD pValue;
+ PIML_VALUE_RECORD_CONTENTS pOldValue;
+ PIML_VALUE_RECORD_CONTENTS pNewValue;
+ NTSTATUS Status;
+ UNICODE_STRING KeyName;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE KeyHandle;
+ UNICODE_STRING ValueName;
+
+ KeyHandle = NULL;
+ if (pKey->Values != 0 || pKey->Action == CreateNewKey) {
+ printf( " %ws - ", MP( PWSTR, pImlUndo, pKey->Name ) );
+ RtlInitUnicodeString( &KeyName, MP( PWSTR, pImlUndo, pKey->Name ) );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &KeyName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ if (pKey->Action != DeleteOldKey) {
+ if (pKey->Action == CreateNewKey) {
+ printf( "deleting" );
+ }
+ else {
+ printf( "modifying" );
+ }
+ Status = NtOpenKey( &KeyHandle, DELETE | GENERIC_WRITE, &ObjectAttributes );
+ }
+ else {
+ printf( "creating" );
+ Status = NtCreateKey( &KeyHandle,
+ GENERIC_WRITE,
+ &ObjectAttributes,
+ 0,
+ NULL,
+ 0,
+ NULL
+ );
+ }
+
+ if (!NT_SUCCESS( Status )) {
+ KeyHandle = NULL;
+ printf( " - failed (0x%08x)", Status );
+ }
+ printf( "\n" );
+
+ if (KeyHandle != NULL) {
+ pValue = MP( PIML_VALUE_RECORD, pImlUndo, pKey->Values );
+ while (pValue != NULL) {
+ pOldValue = MP( PIML_VALUE_RECORD_CONTENTS, pImlUndo, pValue->OldValue );
+ pNewValue = MP( PIML_VALUE_RECORD_CONTENTS, pImlUndo, pValue->NewValue );
+ printf( " %ws - ", MP( PWSTR, pImlUndo, pValue->Name ) );
+ RtlInitUnicodeString( &ValueName,
+ MP( PWSTR, pImlUndo, pValue->Name )
+ );
+ if (pValue->Action == CreateNewValue) {
+ printf( "deleting" );
+ Status = NtDeleteValueKey( KeyHandle, &ValueName );
+ }
+ else {
+ if (pValue->Action == DeleteOldValue) {
+ printf( "creating" );
+ }
+ else {
+ printf( "restoring" );
+ }
+
+ Status = NtSetValueKey( KeyHandle,
+ &ValueName,
+ 0,
+ pOldValue->Type,
+ MP( PWSTR, pImlUndo, pOldValue->Data ),
+ pOldValue->Length
+ );
+ }
+
+ if (!NT_SUCCESS( Status )) {
+ printf( " - failed (0x%08x)", Status );
+ }
+ printf( "\n" );
+
+ pValue = MP( PIML_VALUE_RECORD, pImlUndo, pValue->Next );
+ }
+ }
+ }
+
+ if (KeyHandle != NULL) {
+ if (pKey->Action == CreateNewKey) {
+ Status = NtDeleteKey( KeyHandle );
+ if (!NT_SUCCESS( Status )) {
+ printf( " *** delete of above key failed (0x%08x)", Status );
+ }
+ }
+ NtClose( KeyHandle );
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+ProcessRedoKeyIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pImlUndo,
+ PIML_KEY_RECORD pKey,
+ PINSTALLATION_MODIFICATION_LOGFILE pImlRedo
+ )
+{
+ PIML_VALUE_RECORD pValue;
+ PIML_VALUE_RECORD_CONTENTS pOldValue;
+ PIML_VALUE_RECORD_CONTENTS pNewValue;
+ POFFSET Values;
+
+ Values = 0;
+ if ((pKey->Values != 0 || pKey->Action == CreateNewKey) &&
+ pKey->Action != DeleteOldKey
+ ) {
+ //
+ // Created or modified an existing key and/or values.
+ //
+ pValue = MP( PIML_VALUE_RECORD, pImlUndo, pKey->Values );
+ while (pValue != NULL) {
+ pOldValue = MP( PIML_VALUE_RECORD_CONTENTS, pImlUndo, pValue->OldValue );
+ pNewValue = MP( PIML_VALUE_RECORD_CONTENTS, pImlUndo, pValue->NewValue );
+ if (pValue->Action == CreateNewValue) {
+ if (pNewValue != NULL) {
+ ImlAddValueRecord( pImlRedo,
+ pValue->Action,
+ MP( PWSTR, pImlUndo, pValue->Name ),
+ pNewValue->Type,
+ pNewValue->Length,
+ MP( PWSTR, pImlUndo, pNewValue->Data ),
+ 0,
+ 0,
+ NULL,
+ &Values
+ );
+ }
+ }
+ else
+ if (pValue->Action == ModifyOldValue) {
+ if (pOldValue != NULL && pNewValue != NULL) {
+ ImlAddValueRecord( pImlRedo,
+ pValue->Action,
+ MP( PWSTR, pImlUndo, pValue->Name ),
+ pNewValue->Type,
+ pNewValue->Length,
+ MP( PWSTR, pImlUndo, pNewValue->Data ),
+ pOldValue->Type,
+ pOldValue->Length,
+ MP( PWSTR, pImlUndo, pOldValue->Data ),
+ &Values
+ );
+ }
+ }
+ else
+ if (pValue->Action == DeleteOldValue) {
+ if (pOldValue != NULL) {
+ ImlAddValueRecord( pImlRedo,
+ pValue->Action,
+ MP( PWSTR, pImlUndo, pValue->Name ),
+ 0,
+ 0,
+ NULL,
+ pOldValue->Type,
+ pOldValue->Length,
+ MP( PWSTR, pImlUndo, pOldValue->Data ),
+ &Values
+ );
+ }
+ }
+
+ pValue = MP( PIML_VALUE_RECORD, pImlUndo, pValue->Next );
+ }
+ }
+
+ ImlAddKeyRecord( pImlRedo,
+ pKey->Action,
+ MP( PWSTR, pImlUndo, pKey->Name ),
+ Values
+ );
+ return TRUE;
+}
+
+BOOLEAN
+ProcessUndoIniIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pImlUndo,
+ PIML_INI_RECORD pIni
+ )
+{
+ PIML_INISECTION_RECORD pSection;
+ PIML_INIVARIABLE_RECORD pVariable;
+ HANDLE FileHandle;
+ BOOLEAN FileNameShown;
+ BOOLEAN SectionNameShown;
+
+ FileNameShown = FALSE;
+ pSection = MP( PIML_INISECTION_RECORD, pImlUndo, pIni->Sections );
+ while (pSection != NULL) {
+ SectionNameShown = FALSE;
+ pVariable = MP( PIML_INIVARIABLE_RECORD, pImlUndo, pSection->Variables );
+ while (pVariable != NULL) {
+ if (!FileNameShown) {
+ printf( "%ws\n", MP( PWSTR, pImlUndo, pIni->Name ) );
+ FileNameShown = TRUE;
+ }
+
+ if (!SectionNameShown) {
+ printf( " [%ws]", MP( PWSTR, pImlUndo, pSection->Name ) );
+ if (pSection->Action == DeleteOldSection) {
+ printf( " - deleting" );
+ }
+ printf( "\n" );
+ SectionNameShown = TRUE;
+ }
+
+ printf( " %ws = ", MP( PWSTR, pImlUndo, pVariable->Name ) );
+ if (pVariable->Action == CreateNewVariable) {
+ printf( "deleting" );
+ if (!WritePrivateProfileString( MP( PWSTR, pImlUndo, pSection->Name ),
+ MP( PWSTR, pImlUndo, pVariable->Name ),
+ NULL,
+ MP( PWSTR, pImlUndo, pIni->Name )
+ )
+ ) {
+ printf( " - failed (%u)", GetLastError() );
+ }
+ printf( "\n" );
+ }
+ else {
+ printf( "restoring" );
+ if (!WritePrivateProfileString( MP( PWSTR, pImlUndo, pSection->Name ),
+ MP( PWSTR, pImlUndo, pVariable->Name ),
+ MP( PWSTR, pImlUndo, pVariable->OldValue ),
+ MP( PWSTR, pImlUndo, pIni->Name )
+ )
+ ) {
+ printf( " - failed (%u)", GetLastError() );
+ }
+ printf( "\n" );
+ }
+
+ pVariable = MP( PIML_INIVARIABLE_RECORD, pImlUndo, pVariable->Next );
+ }
+
+ if (pSection->Action == CreateNewSection) {
+ if (!FileNameShown) {
+ printf( "%ws\n", MP( PWSTR, pImlUndo, pIni->Name ) );
+ FileNameShown = TRUE;
+ }
+
+ if (!SectionNameShown) {
+ printf( " [%ws]", MP( PWSTR, pImlUndo, pSection->Name ) );
+ if (pSection->Action == CreateNewSection) {
+ printf( " - deleting" );
+ }
+ SectionNameShown = TRUE;
+ printf( "\n" );
+ }
+
+ if (!WritePrivateProfileSection( MP( PWSTR, pImlUndo, pSection->Name ),
+ NULL,
+ MP( PWSTR, pImlUndo, pIni->Name )
+ )
+ ) {
+ printf( " *** delete of above section name failed (%u)\n",
+ GetLastError()
+ );
+ }
+ }
+
+ pSection = MP( PIML_INISECTION_RECORD, pImlUndo, pSection->Next );
+ }
+
+ if (pIni->Action == CreateNewIniFile) {
+ printf( "%ws - deleting", MP( PWSTR, pImlUndo, pIni->Name ) );
+ if (!DeleteFile( MP( PWSTR, pImlUndo, pIni->Name ) )) {
+ printf( " - failed (%u)", GetLastError() );
+ }
+ printf( "\n" );
+ FileNameShown = TRUE;
+ }
+ else {
+ FileHandle = CreateFile( MP( PWSTR, pImlUndo, pIni->Name ),
+ FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+ if (FileHandle != INVALID_HANDLE_VALUE) {
+ SetFileTime( FileHandle,
+ &pIni->LastWriteTime,
+ &pIni->LastWriteTime,
+ &pIni->LastWriteTime
+ );
+ CloseHandle( FileHandle );
+ }
+ }
+
+ if (FileNameShown) {
+ printf( "\n" );
+ }
+ return TRUE;
+}
+
+
+BOOLEAN
+ProcessRedoIniIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pImlUndo,
+ PIML_INI_RECORD pIni,
+ PINSTALLATION_MODIFICATION_LOGFILE pImlRedo
+ )
+{
+ PIML_INISECTION_RECORD pSection;
+ PIML_INIVARIABLE_RECORD pVariable;
+ POFFSET Variables, Sections;
+
+ pSection = MP( PIML_INISECTION_RECORD, pImlUndo, pIni->Sections );
+ Sections = 0;
+ while (pSection != NULL) {
+ pVariable = MP( PIML_INIVARIABLE_RECORD, pImlUndo, pSection->Variables );
+ Variables = 0;
+ while (pVariable != NULL) {
+ ImlAddIniVariableRecord( pImlRedo,
+ pVariable->Action,
+ MP( PWSTR, pImlUndo, pVariable->Name ),
+ MP( PWSTR, pImlUndo, pVariable->OldValue ),
+ MP( PWSTR, pImlUndo, pVariable->NewValue ),
+ &Variables
+ );
+
+ pVariable = MP( PIML_INIVARIABLE_RECORD, pImlUndo, pVariable->Next );
+ }
+
+ if (Variables != 0) {
+ ImlAddIniSectionRecord( pImlRedo,
+ pSection->Action,
+ MP( PWSTR, pImlUndo, pSection->Name ),
+ Variables,
+ &Sections
+ );
+ }
+
+ pSection = MP( PIML_INISECTION_RECORD, pImlUndo, pSection->Next );
+ }
+
+ if (Sections != 0) {
+ ImlAddIniRecord( pImlRedo,
+ pIni->Action,
+ MP( PWSTR, pImlUndo, pIni->Name ),
+ &pIni->LastWriteTime,
+ Sections
+ );
+ }
+
+ return TRUE;
+}
+
+
+int
+ProcessUndoIml(
+ PINSTALLATION_MODIFICATION_LOGFILE pImlUndo,
+ PINSTALLATION_MODIFICATION_LOGFILE pImlRedo
+ )
+{
+ PIML_FILE_RECORD pFile;
+ PIML_KEY_RECORD pKey;
+ PIML_INI_RECORD pIni;
+
+ if (pImlUndo->NumberOfFileRecords > 0) {
+ printf( "Undoing File Modifications:\n" );
+ pFile = MP( PIML_FILE_RECORD, pImlUndo, pImlUndo->FileRecords );
+ while (pFile != NULL) {
+ if (pFile->Name != 0) {
+ if (pImlRedo != NULL) {
+ ProcessRedoFileIml( pImlUndo, pFile, pImlRedo );
+ }
+
+ ProcessUndoFileIml( pImlUndo, pFile );
+ }
+
+ pFile = MP( PIML_FILE_RECORD, pImlUndo, pFile->Next );
+ }
+
+ printf( "\n" );
+ }
+
+ if (pImlUndo->NumberOfKeyRecords > 0) {
+ printf( "Undoing Registry Modifications:\n" );
+ pKey = MP( PIML_KEY_RECORD, pImlUndo, pImlUndo->KeyRecords );
+ while (pKey != NULL) {
+ if (pImlRedo != NULL) {
+ ProcessRedoKeyIml( pImlUndo, pKey, pImlRedo );
+ }
+
+ ProcessUndoKeyIml( pImlUndo, pKey );
+
+ pKey = MP( PIML_KEY_RECORD, pImlUndo, pKey->Next );
+ }
+
+ printf( "\n" );
+ }
+
+ if (pImlUndo->NumberOfIniRecords > 0) {
+ printf( "Undoing .INI File modifications:\n" );
+ pIni = MP( PIML_INI_RECORD, pImlUndo, pImlUndo->IniRecords );
+ while (pIni != NULL) {
+ if (pImlRedo != NULL) {
+ ProcessRedoIniIml( pImlUndo, pIni, pImlRedo );
+ }
+
+ ProcessUndoIniIml( pImlUndo, pIni);
+
+ pIni = MP( PIML_INI_RECORD, pImlUndo, pIni->Next );
+ }
+ }
+
+ return 0;
+}
diff --git a/private/sdktools/instaler/undoinst.rc b/private/sdktools/instaler/undoinst.rc
new file mode 100644
index 000000000..61608cfe0
--- /dev/null
+++ b/private/sdktools/instaler/undoinst.rc
@@ -0,0 +1,12 @@
+#include "instaler.h"
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Windows NT Instaler Undo Program"
+#define VER_INTERNALNAME_STR "UNDOINST\0"
+#define VER_ORIGINALFILENAME_STR "UNDOINST.EXE"
+
+#include "common.ver"
+
+RCINCLUDE errormsg.rc