/****************************************************************************\ MAIN.C / OPK Wizard (OPKWIZ.EXE) Microsoft Confidential Copyright (c) Microsoft Corporation 1999 All rights reserved Main source file for the OPK Wizard. Contains WinMain() and global variable declarations. 4/99 - Jason Cohen (JCOHEN) Added this new main source file for the OPK Wizard as part of the Millennium rewrite. 09/2000 - Stephen Lodwick (STELO) Ported OPK Wizard to Whistler \****************************************************************************/ // // Pre-include Defined Value(s): // // Needed to define this so we don't include // the extern declarations of the globals that // are declared in this file. // #define _MAIN_C_ // // Include File(s): // #include "setupmgr.h" #include "allres.h" // // Global Variable(s): // GAPP g_App; // // Internal Defined Value(s): // // Tag files // #ifndef DBCS #define FILE_DBCS_TAG _T("dbcs.tag") #endif // DBCS // Directories off the root of the tool instal location. // #define DIR_LANG _T("lang") #define DIR_CONFIGSETS _T("cfgsets") #define DIR_DOCS _T("docs") #define FILE_HELPCONTENT_CHM _T("opk.chm") #define STR_TAGFILE _T("HIJPP 1.0") #define STR_VERSION _T("Version") #define REGSTR_IEXPLORER _T("Software\\Microsoft\\Internet Explorer") // Unique string. // #define OPKWIZ_MUTEX _T("OPKWIZ-MUTEX-5c9fbbd0-ee0e-11d2-9a21-0000f81edacc") // // Internal Function Prototype(s): // static BOOL ParseCmdLine(LPTSTR); static BOOL FileWritable(LPTSTR lpszFile); BOOL CALLBACK HelpDlgProc(HWND, UINT, WPARAM, LPARAM); static BOOL CheckIEVersion(); static BOOL ParseVersionString(TCHAR* pszVersion, DWORD* pdwMajor, DWORD* pdwMinor, DWORD* pdwBuild, DWORD* pdwSubbuild); static VOID SetWizardHelpFile(LPTSTR lpszHelpFilePath); // // Main Function: // int StartWizard(HINSTANCE hInstance, LPSTR lpCmdLine) { HANDLE hMutex = NULL; int nReturn = 0; TCHAR szCmdLine[MAX_PATH] = NULLSTR; // Convert cmdline from char to wchar // MultiByteToWideChar(CP_ACP, 0, lpCmdLine, -1, szCmdLine, AS(szCmdLine)); // Check for another instance of the OPK wizard. // SetLastError(ERROR_SUCCESS); if ( ( hMutex = CreateMutex(NULL, TRUE, OPKWIZ_MUTEX) ) && ( GetLastError() == ERROR_ALREADY_EXISTS ) ) { HWND hwndWizard; LPTSTR lpAppName = AllocateString(NULL, IDS_APPNAME); // Find the window, set it to the forground, and return. // if ( ( lpAppName ) && ( hwndWizard = FindWindow(NULL, lpAppName) ) ) SetForegroundWindow(hwndWizard); FREE(lpAppName); } else if ( CheckIEVersion() ) { TCHAR szBuffer[MAX_PATH]; LPTSTR lpBuffer; // Init some more of the global data. // g_App.hInstance = hInstance; // Get the path to where the EXE is. // szBuffer[0] = NULLCHR; lpBuffer = NULL; // ISSUE-2002/02/27-stelo,swamip - Check return Value and make sure that szBuffer has data in it. GetModuleFileName(hInstance, szBuffer, STRSIZE(szBuffer)); if ( GetFullPathName(szBuffer, STRSIZE(g_App.szOpkDir), g_App.szOpkDir, &lpBuffer) && g_App.szOpkDir[0] && lpBuffer ) { // Chop off the exe name from the path we want. // *lpBuffer = NULLCHR; StrRTrm(g_App.szOpkDir, CHR_BACKSLASH); } // Setup the full path to the ini file for the wizard. // lstrcpyn(g_App.szSetupMgrIniFile, g_App.szOpkDir,AS(g_App.szSetupMgrIniFile)); AddPathN(g_App.szSetupMgrIniFile, FILE_SETUPMGR_INI,AS(g_App.szSetupMgrIniFile)); // Need to know where the root of the folder where wizard files are installed. // lstrcpyn(g_App.szWizardDir, g_App.szOpkDir,AS(g_App.szWizardDir)); AddPathN(g_App.szWizardDir, DIR_WIZARDFILES,AS(g_App.szWizardDir)); // Need to know where the configuration set folder is. // lstrcpyn(g_App.szConfigSetsDir, g_App.szOpkDir, AS(g_App.szConfigSetsDir)); AddPathN(g_App.szConfigSetsDir, DIR_CONFIGSETS,AS(g_App.szConfigSetsDir)); // Need to know where the lang directory is. // lstrcpyn(g_App.szLangDir, g_App.szOpkDir,AS(g_App.szLangDir)); AddPathN(g_App.szLangDir, DIR_LANG,AS(g_App.szLangDir)); // Setup the full paths to the help files. // SetWizardHelpFile(g_App.szHelpFile); lstrcpyn(g_App.szHelpContentFile, g_App.szOpkDir,AS(g_App.szHelpContentFile)); AddPathN(g_App.szHelpContentFile, DIR_DOCS,AS(g_App.szHelpContentFile)); AddPathN(g_App.szHelpContentFile, FILE_HELPCONTENT_CHM,AS(g_App.szHelpContentFile)); // Setup the full path to the OPK input file. // lstrcpyn(g_App.szOpkInputInfFile, g_App.szWizardDir,AS(g_App.szOpkInputInfFile)); AddPathN(g_App.szOpkInputInfFile, FILE_OPKINPUT_INF,AS(g_App.szOpkInputInfFile)); // First check for the OEM tag file in the same folder // as the exe, just in case they are running it right // off the CD or network share. We need to catch this // case so we can stop them from running in corp mode // by accidentally. // lstrcpyn(szBuffer, g_App.szOpkDir,AS(szBuffer)); AddPathN(szBuffer, FILE_OEM_TAG,AS(szBuffer)); if ( FileExists(szBuffer) ) SET_FLAG(OPK_OEM, TRUE); // Get a pointer to the end of a buffer with the wizard // directory in it. // lstrcpyn(szBuffer, g_App.szWizardDir,AS(szBuffer)); AddPathN(szBuffer, NULLSTR,AS(szBuffer)); lpBuffer = szBuffer + lstrlen(szBuffer); // Check to see if this is the DBCS version. // // NTRAID#NTBUG9-547380-2002/02/27-stelo,swamip - We need to base the DBCS descisions (conditions) at runtime not at compile time. Since an English OPK // can deploy a variety of languages, the compile time tag does not make sense. #ifdef DBCS SET_FLAG(OPK_DBCS, TRUE); #else // DBCS lstrcpyn(lpBuffer, FILE_DBCS_TAG, (AS(szBuffer)-lstrlen(szBuffer))); SET_FLAG(OPK_DBCS, FileExists(szBuffer)); #endif // DBCS // Check for the OEM tag file. // lstrcpyn(lpBuffer, FILE_OEM_TAG, (AS(szBuffer)-lstrlen(szBuffer))); if ( FileExists(szBuffer) ) SET_FLAG(OPK_OEM, TRUE); // // Make sure that szBuffer is pointing to the OEM tag file at this point, // because we are going to try and write to it in the next check. // // The OPK input file must exist to run the wizard if this // is running in OEM mode. // if ( ( g_App.szOpkDir[0] ) && ( ( !GET_FLAG(OPK_OEM) ) || ( FileExists(g_App.szOpkInputInfFile) && FileWritable(szBuffer) ) ) ) { // Check out the command line options. // if ( ParseCmdLine(szCmdLine) ) { // Set this so on the very first wizard page, we can cancel with // out getting the confirmation dialog. // SET_FLAG(OPK_EXIT, TRUE); // Now create the wizard. // nReturn = CreateMaintenanceWizard(hInstance, NULL); // Clean up the temporary directory used if we didn't finish. // if ( g_App.szTempDir[0] ) { // Make sure the temp dir and the wizard dir have trailing backslashes. // AddPathN(g_App.szWizardDir, NULLSTR,AS(g_App.szWizardDir)); AddPathN(g_App.szTempDir, NULLSTR,AS(g_App.szTempDir)); if ( lstrcmpi(g_App.szWizardDir, g_App.szTempDir) != 0 ) DeletePath(g_App.szTempDir); #ifdef DBG else { DBGOUT(NULL, _T("OPKWIZ: Temp and Wizard directory are the same on exit (%s).\n"), g_App.szTempDir); DBGMSGBOX(NULL, _T("Temp and Wizard directory are the same on exit (%s)."), _T("OPKWIZ Debug Message"), MB_ERRORBOX, g_App.szTempDir); } #endif // DBG } } } else MsgBox(NULL, IDS_ERR_WIZBAD, IDS_APPNAME, MB_ERRORBOX); } else MsgBox(NULL, IDS_ERR_IE5, IDS_APPNAME, MB_ERRORBOX); // Do the final cleanup before exiting. // if ( hMutex ) CloseHandle(hMutex); return nReturn; } // // Internal Function(s): // static BOOL ParseCmdLine(LPTSTR lpszCmdLineOrg) { DWORD dwArgs; LPTSTR *lpArgs; BOOL bRet = TRUE, bError = FALSE; // ISSUE-2002/02/27-stelo,swamip - lpszCmdLineOrg is not being used any where, and before calling this function we are // doing some MultibytetoWideChar stuff on the buffer, those can be removed as well. if ( (dwArgs = GetCommandLineArgs(&lpArgs) ) && lpArgs ) { LPTSTR lpArg; DWORD dwArg; // We want to skip over the first argument (it is the path // to the command being executed. // if ( dwArgs > 1 ) { dwArg = 1; lpArg = *(lpArgs + dwArg); } else lpArg = NULL; // Loop through all the arguments. // while ( lpArg && !bError ) { // Now we check to see if the first char is a dash or not. // if ( ( *lpArg == _T('-') ) || ( *lpArg == _T('/') ) ) { LPTSTR lpOption = CharNext(lpArg); BOOL bOption; // // This is where you add command line options that start with a dash (-). // // Set bError if you don't recognize the command line option (unless you // want to just ignore it and continue). // switch( UPPER(*lpOption) ) { case _T('M'): // Maintanence mode. // if ( ( *(++lpOption) == _T(':') ) && *(++lpOption) ) { LPTSTR lpConfigName; lstrcpyn(g_App.szTempDir, g_App.szConfigSetsDir,AS(g_App.szTempDir)); AddPathN(g_App.szTempDir, NULLSTR,AS(g_App.szTempDir)); lpConfigName = g_App.szTempDir + lstrlen(g_App.szTempDir); // ISSUE-2002/02/27-stelo,swamip - will never hit this conditional code? // if ( *lpOption == _T('"') ) lpOption++; lstrcpyn(lpConfigName, lpOption, (AS(g_App.szTempDir)-lstrlen(g_App.szTempDir))); StrTrm(lpConfigName, CHR_SPACE); StrTrm(lpConfigName, CHR_QUOTE); lstrcpyn(g_App.szConfigName, lpConfigName,AS(g_App.szConfigName)); AddPathN(g_App.szTempDir, NULLSTR,AS(g_App.szTempDir)); SET_FLAG(OPK_MAINTMODE, TRUE); SET_FLAG(OPK_CMDMM, TRUE); // Now make sure that the directory actually exists. // if ( !DirectoryExists(g_App.szTempDir) ) { MsgBox(NULL, IDS_ERR_BADCONFIG, IDS_APPNAME, MB_ERRORBOX, g_App.szConfigName); bRet = FALSE; } } else bError = TRUE; break; case _T('?'): // Help. // DialogBox(g_App.hInstance, MAKEINTRESOURCE(IDD_HELP), NULL, (DLGPROC) HelpDlgProc); bRet = FALSE; break; case _T('A'): // Set the flag for the autorun feature // SET_FLAG(OPK_AUTORUN, TRUE); break; case _T('B'): case _T('I'): //Going into batch/INS mode // Set bOption if it is a batch file, otherwise it // is the install ins file. // bOption = ( _T('B') == UPPER(*lpOption) ); //Check to see that there's a file name if ( ( *(++lpOption) == _T(':') ) && *(++lpOption) ) { LPTSTR lpFileName, lpFilePart = NULL; TCHAR szFullPath[MAX_PATH] = NULLSTR, szBuf[MAX_URL]; // Set the lpFileName based on the command line // // ISSUE-2002/02/27-stelo,swamip - will never hit this conditional code? // if ( *lpOption == _T('"') ) lpOption++; // Strip off the spaces and quotes from parameter // StrTrm(lpOption, CHR_SPACE); StrTrm(lpOption, CHR_QUOTE); // Grab the full path of the batch/INS file // if (( GetFullPathName(lpOption, STRSIZE(szFullPath), szFullPath, &lpFilePart) )) { // Verify the the batch/INS file exists // if ( !FileExists(szFullPath)) { MsgBox(NULL, bOption ? IDS_ERR_BADBATCH : IDS_ERR_BADINS, IDS_APPNAME, MB_ERRORBOX, szFullPath); bRet = FALSE; } else { // The file exists, we're ready to start, Set the batch/INS mode flag to TRUE // Set the global batch file to the given batch/INS file name if (bOption) lstrcpyn(g_App.szOpkWizIniFile, szFullPath, AS(g_App.szOpkWizIniFile)); else lstrcpyn(g_App.szInstallInsFile, szFullPath, AS(g_App.szInstallInsFile)); SET_FLAG(bOption ? OPK_BATCHMODE : OPK_INSMODE, TRUE); bRet = TRUE; } // Set the configuration set name // szBuf[0] = NULLCHR; // ISSUE-2002/02/27-stelo,swamip - Need to check the return value of GetPrivateProfileString. Also check for possible buffer overflow // as szBuf is MAX_URL and ConfigName is MAX_PATH GetPrivateProfileString( INI_SEC_CONFIGSET, INI_SEC_CONFIG, NULLSTR, szBuf, STRSIZE(szBuf), g_App.szOpkWizIniFile ); lstrcpyn(g_App.szConfigName, szBuf, AS(g_App.szConfigName)); } else bRet = FALSE; } else bError = TRUE; break; default: bError = TRUE; break; } } else if ( *lpArg ) { // // This is where you would read any command line parameters that are just passed // in on the command line w/o any proceeding characters (like - or /). // // Set bError if you don't have any of these types of parameters (unless you // want to just ignore it and continue). // bError = TRUE; } // Setup the pointer to the next argument in the command line. // if ( ++dwArg < dwArgs ) lpArg = *(lpArgs + dwArg); else lpArg = NULL; } // Make sure to free the two buffers allocated by the GetCommandLineArgs() function. // FREE(*lpArgs); FREE(lpArgs); } //Check to see if the arguments provided are valid and that we have not already hit an error if (((GET_FLAG(OPK_BATCHMODE) && GET_FLAG(OPK_MAINTMODE)) || (!(GET_FLAG(OPK_BATCHMODE)) && GET_FLAG(OPK_INSMODE)) || (!(GET_FLAG(OPK_BATCHMODE)) && GET_FLAG(OPK_AUTORUN)) || (GET_FLAG(OPK_MAINTMODE) && GET_FLAG(OPK_INSMODE))) && bRet && !bError) { MsgBox(NULL, IDS_ERR_INVCMD, IDS_APPNAME, MB_OK); bRet = FALSE; } // If we hit an error, display the error and show the help. // if ( bError ) { MsgBox(NULL, IDS_ERR_BADCMDLINE, IDS_APPNAME, MB_ERRORBOX); bRet = FALSE; } return bRet; } static BOOL FileWritable(LPTSTR lpszFile) { BOOL bRet = TRUE; DWORD dwAttr = GetFileAttributes(lpszFile); // ISSUE-2002/02/27-stelo,swamip - The logic appears to incorrect, we should check READ_ONLY attribute if ( ( dwAttr != 0xFFFFFFFF ) && ( SetFileAttributes(lpszFile, dwAttr) == 0 ) ) { bRet = FALSE; } return bRet; } void SetConfigPath(LPCTSTR lpDirectory) { HINF hInf; INFCONTEXT InfContext; BOOL bLoop; DWORD dwErr; // ISSUE-2002/02/27-stelo,swamip - Make sure lpdirectory is a valid pointer if ( (hInf = SetupOpenInfFile(g_App.szOpkInputInfFile, NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, &dwErr)) != INVALID_HANDLE_VALUE ) { for ( bLoop = SetupFindFirstLine(hInf, INF_SEC_COPYFILES, NULL, &InfContext); bLoop; bLoop = SetupFindNextLine(&InfContext, &InfContext) ) { TCHAR szFile[MAX_PATH] = NULLSTR, szSubDir[MAX_PATH] = NULLSTR; LPTSTR lpBuffer; int iBufferLen; // Get the source filename. // if ( SetupGetStringField(&InfContext, 1, szFile, AS(szFile), NULL) && szFile[0] ) { // Now find out if this is a file we care about. // if ( LSTRCMPI(szFile, FILE_INSTALL_INS) == 0 ) { lpBuffer = g_App.szInstallInsFile; iBufferLen= AS(g_App.szInstallInsFile); } else if ( LSTRCMPI(szFile, FILE_OPKWIZ_INI) == 0 ) { lpBuffer = g_App.szOpkWizIniFile; iBufferLen= AS(g_App.szOpkWizIniFile); } else if ( LSTRCMPI(szFile, FILE_OOBEINFO_INI) == 0 ) { lpBuffer = g_App.szOobeInfoIniFile; iBufferLen= AS(g_App.szOobeInfoIniFile); } else if ( LSTRCMPI(szFile, FILE_OEMINFO_INI) == 0 ) { lpBuffer = g_App.szOemInfoIniFile; iBufferLen= AS(g_App.szOemInfoIniFile); } else if ( LSTRCMPI(szFile, FILE_WINBOM_INI) == 0 ) { lpBuffer = g_App.szWinBomIniFile; iBufferLen= AS(g_App.szWinBomIniFile); } else if ( LSTRCMPI(szFile, FILE_UNATTEND_TXT) == 0 ) { lpBuffer = g_App.szUnattendTxtFile; iBufferLen= AS(g_App.szUnattendTxtFile); } else { lpBuffer = NULL; iBufferLen=0; } // Get the full path to the file if this is one we are saving. // if ( lpBuffer ) { lstrcpyn(lpBuffer, lpDirectory,iBufferLen); // Get the optional destination sub directory and add it // if it is there. // if ( SetupGetStringField(&InfContext, 3, szSubDir, AS(szSubDir), NULL) && szSubDir[0] ) { AddPathN(lpBuffer, szSubDir, iBufferLen); if ( !DirectoryExists(lpBuffer) ) { // ISSUE-2002/02/27-stelo,swamip - We should check the return value of CreatePath and pass up to SetConfigPath CreatePath(lpBuffer); } } AddPathN(lpBuffer, szFile, iBufferLen); } } } } } BOOL CALLBACK HelpDlgProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch (uMsg) { case WM_COMMAND: switch ( LOWORD(wParam) ) { case IDOK: EndDialog(hwnd, LOWORD(wParam)); break; } return FALSE; default: return FALSE; } return FALSE; } // Get the IE version from the registry, return TRUE if IE > 5 BOOL CheckIEVersion() { DWORD dwSize = 255; TCHAR szVersion[255]; HKEY hKey = 0; DWORD dwType = 0; BOOL bRet = FALSE; if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, REGSTR_IEXPLORER, &hKey)) { // Version for IE DWORD dwMajor, dwMinor, dwBuild, dwSubbuild; if (ERROR_SUCCESS == RegQueryValueEx(hKey, STR_VERSION, 0, &dwType, (LPBYTE)szVersion, &dwSize)) { // Get the major version number if (ParseVersionString(szVersion, &dwMajor, &dwMinor, &dwBuild, &dwSubbuild) && (dwMajor >= 5)) { bRet = TRUE; } } RegCloseKey(hKey); } return bRet; } // Parses 5.00.0518.10 into dwMajor = 5, dwMinor = 0 // ... BOOL ParseVersionString(TCHAR* pszVersion, DWORD* pdwMajor, DWORD* pdwMinor, DWORD* pdwBuild, DWORD* pdwSubbuild) { TCHAR szTemp[255]; int i = 0; if (!pdwMajor || !pdwMinor || !pdwBuild || !pdwSubbuild) return FALSE; // ISSUE-2002/02/27-stelo,swamip - Check for the end of the string condition during while loops. // Major version while (pszVersion && *pszVersion != TEXT('.')) szTemp[i++] = *pszVersion++; *pdwMajor = _tcstoul(szTemp, 0, 10); pszVersion++; // Minor version i = 0; while (pszVersion && *pszVersion != TEXT('.')) szTemp[i++] = *pszVersion++; *pdwMinor = _tcstoul(szTemp, 0, 10); pszVersion++; // Build version i = 0; while (pszVersion && *pszVersion != TEXT('.')) szTemp[i++] = *pszVersion++; *pdwBuild = _tcstoul(szTemp, 0, 10); pszVersion++; // Sub build version i = 0; while (pszVersion && *pszVersion != TEXT('\0')) szTemp[i++] = *pszVersion++; *pdwSubbuild = _tcstoul(szTemp, 0, 10); return TRUE; } // Saves us from making sure we check if we use this function we always check if batch mode // BOOL OpkGetPrivateProfileSection(LPCTSTR pszAppName, LPTSTR pszSection, INT cchSectionMax, LPCTSTR pszFileName) { if (!pszAppName || !pszSection || !pszFileName) return FALSE; return GetPrivateProfileSection(pszAppName, pszSection, cchSectionMax, GET_FLAG(OPK_BATCHMODE) ? g_App.szOpkWizIniFile : pszFileName); } // Saves us from making two calls when writing and also so we don't forget about // writing to batch mode inf // BOOL OpkWritePrivateProfileSection(LPCTSTR pszAppName, LPCTSTR pszKeyName, LPCTSTR pszFileName) { if (!pszAppName || !pszFileName) return FALSE; // Write to batch inf // if (FALSE == WritePrivateProfileSection(pszAppName, pszKeyName, g_App.szOpkWizIniFile)) return FALSE; // Write to user inf // return WritePrivateProfileSection(pszAppName, pszKeyName, pszFileName); } // Saves us from making two calls when writing and also so we don't forget about // writing to batch mode inf // BOOL OpkWritePrivateProfileString(LPCTSTR pszAppName, LPCTSTR pszKeyName, LPCTSTR pszValue, LPCTSTR pszFileName) { BOOL fRet = FALSE; if (!pszAppName || !pszFileName) return FALSE; // Write to batch inf // if (FALSE == WritePrivateProfileString(pszAppName, pszKeyName, pszValue, g_App.szOpkWizIniFile)) return FALSE; // Write to user inf // return WritePrivateProfileString(pszAppName, pszKeyName, pszValue, pszFileName); } // Saves us from making sure we check if we use this function we always check if batch mode // BOOL OpkGetPrivateProfileString(LPCTSTR pszAppName, LPCTSTR pszKeyName, LPCTSTR pszDefault, LPTSTR pszValue, INT cchValue, LPCTSTR pszFileName) { if (!pszAppName || !pszKeyName || !pszDefault || !pszValue || !pszFileName) return FALSE; return GetPrivateProfileString(pszAppName, pszKeyName, pszDefault, pszValue, cchValue, GET_FLAG(OPK_BATCHMODE) ? g_App.szOpkWizIniFile : pszFileName); } // Note: pszHelpFilePath must be at least size MAX_PATH VOID SetWizardHelpFile(LPTSTR pszHelpFilePath) { // The help file can be in two location, in the docs folder or // the current directory. Check the docs folder first. // TCHAR szDocsFolder[MAX_PATH] = NULLSTR; // Build the docs folder path // lstrcpyn(szDocsFolder, g_App.szOpkDir,AS(szDocsFolder)); AddPathN(szDocsFolder, DIR_DOCS,AS(szDocsFolder)); // Test if help file exists in docs folder // if (pszHelpFilePath) { if (DirectoryExists(szDocsFolder)) { lstrcpyn(pszHelpFilePath, szDocsFolder, MAX_PATH); AddPathN(pszHelpFilePath, FILE_OPKWIZ_HLP, MAX_PATH); if (!FileExists(pszHelpFilePath)) { lstrcpyn(pszHelpFilePath, g_App.szOpkDir, MAX_PATH); AddPathN(pszHelpFilePath, FILE_OPKWIZ_HLP, MAX_PATH); } } else { lstrcpyn(pszHelpFilePath, g_App.szOpkDir, MAX_PATH); AddPathN(pszHelpFilePath, FILE_OPKWIZ_HLP, MAX_PATH); } } }