1700 lines
54 KiB
C
1700 lines
54 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 2000 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
opk.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Common modules shared by OPK tools. Note: Source Depot requires that we
|
||
|
publish the .h (E:\NT\admin\published\ntsetup\opklib.w) and .lib
|
||
|
(E:\NT\public\internal\admin\lib).
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Brian Ku (briank) 06/20/2000
|
||
|
Stephen Lodwick (stelo) 06/28/2000
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
#include <pch.h>
|
||
|
#include <objbase.h>
|
||
|
#include <tchar.h>
|
||
|
#include <regstr.h>
|
||
|
#include <winbom.h>
|
||
|
|
||
|
|
||
|
//
|
||
|
// Local Define(s):
|
||
|
//
|
||
|
|
||
|
#define FILE_WINBOM_INI _T("WINBOM.INI")
|
||
|
#define MAX_NAME 50
|
||
|
|
||
|
#define REG_KEY_FACTORY _T("SOFTWARE\\Microsoft\\Factory")
|
||
|
#define REG_KEY_FACTORY_STATE REG_KEY_FACTORY _T("\\State")
|
||
|
#define REG_KEY_SETUP_SETUP REGSTR_PATH_SETUP REGSTR_KEY_SETUP
|
||
|
#define REG_VAL_FACTORY_WINBOM _T("WinBOM")
|
||
|
#define REG_VAL_FACTORY_USERNAME _T("UserName")
|
||
|
#define REG_VAL_FACTORY_PASSWORD _T("Password")
|
||
|
#define REG_VAL_FACTORY_DOMAIN _T("Domain")
|
||
|
#define REG_VAL_DEVICEPATH _T("DevicePath")
|
||
|
#define REG_VAL_SOURCEPATH _T("SourcePath")
|
||
|
#define REG_VAL_SPSOURCEPATH _T("ServicePackSourcePath")
|
||
|
|
||
|
#define DIR_SYSTEMROOT _T("%SystemDrive%\\") // This has to have the trailing backslash, don't remove.
|
||
|
|
||
|
#define NET_TIMEOUT 30000 // Time out to wait for net to start in milliseconds.
|
||
|
|
||
|
//
|
||
|
// Local Type Define(s):
|
||
|
//
|
||
|
|
||
|
typedef struct _STRLIST
|
||
|
{
|
||
|
LPTSTR lpszData;
|
||
|
struct _STRLIST * lpNext;
|
||
|
}
|
||
|
STRLIST, *PSTRLIST, *LPSTRLIST;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Local variables
|
||
|
//
|
||
|
static WCHAR NameOrgName[MAX_NAME+1];
|
||
|
static WCHAR NameOrgOrg[MAX_NAME+1];
|
||
|
|
||
|
static LPTSTR CleanupDirs [] =
|
||
|
{
|
||
|
{_T("Win9xmig")},
|
||
|
{_T("Win9xupg")},
|
||
|
{_T("Winntupg")}
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// Internal Fuction Prototype(s):
|
||
|
//
|
||
|
|
||
|
static BOOL CheckWinbomRegKey(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath,
|
||
|
LPTSTR lpszShare, DWORD cbShare,
|
||
|
LPTSTR lpszUser, DWORD cbUser,
|
||
|
LPTSTR lpszPass, DWORD cbPass,
|
||
|
LPTSTR lpFactoryMode, LPTSTR lpKey,
|
||
|
BOOL bNetwork, LPBOOL lpbExists);
|
||
|
static BOOL SearchRemovableDrives(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath, LPTSTR lpFactoryMode, UINT uDriveType);
|
||
|
static BOOL WinBOMExists(LPTSTR lpWinBom, LPTSTR lpMode);
|
||
|
|
||
|
static void SavePathList(HKEY hKeyRoot, LPTSTR lpszSubKey, LPSTRLIST lpStrList, BOOL bWrite);
|
||
|
static BOOL AddPathToList(LPTSTR lpszExpanded, LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted);
|
||
|
static void EnumeratePath(LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted);
|
||
|
static BOOL AddPathsToList(LPTSTR lpszBegin, LPTSTR lpszRoot, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted, BOOL bRecursive);
|
||
|
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Enable or disable a given named privilege.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
PrivilegeName - supplies the name of a system privilege.
|
||
|
|
||
|
Enable - flag indicating whether to enable or disable the privilege.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Boolean value indicating whether the operation was successful.
|
||
|
|
||
|
--*/
|
||
|
BOOL
|
||
|
EnablePrivilege(
|
||
|
IN PCTSTR PrivilegeName,
|
||
|
IN BOOL Enable
|
||
|
)
|
||
|
{
|
||
|
HANDLE Token;
|
||
|
BOOL bRet;
|
||
|
TOKEN_PRIVILEGES NewPrivileges;
|
||
|
LUID Luid;
|
||
|
|
||
|
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&Token)) {
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
if(!LookupPrivilegeValue(NULL,PrivilegeName,&Luid)) {
|
||
|
CloseHandle(Token);
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
NewPrivileges.PrivilegeCount = 1;
|
||
|
NewPrivileges.Privileges[0].Luid = Luid;
|
||
|
NewPrivileges.Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
|
||
|
|
||
|
bRet = AdjustTokenPrivileges(
|
||
|
Token,
|
||
|
FALSE,
|
||
|
&NewPrivileges,
|
||
|
0,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
//
|
||
|
// The return value of AdjustTokenPrivileges() can be true even though we didn't set all
|
||
|
// the privileges that we asked for. We need to call GetLastError() to make sure the call succeeded.
|
||
|
//
|
||
|
bRet = bRet && ( ERROR_SUCCESS == GetLastError() );
|
||
|
|
||
|
CloseHandle(Token);
|
||
|
|
||
|
return(bRet);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*++
|
||
|
===============================================================================
|
||
|
Routine Description:
|
||
|
|
||
|
This routine will clean up any registry changes that we made to facilitate
|
||
|
the factory pre-install process
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
none
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
===============================================================================
|
||
|
--*/
|
||
|
void CleanupRegistry
|
||
|
(
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
HKEY hSetupKey;
|
||
|
DWORD dwResult;
|
||
|
DWORD dwValue = 0;
|
||
|
|
||
|
// Open HKLM\System\Setup
|
||
|
dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
||
|
L"SYSTEM\\Setup",
|
||
|
0,
|
||
|
KEY_ALL_ACCESS,
|
||
|
&hSetupKey);
|
||
|
if (NO_ERROR == dwResult)
|
||
|
{
|
||
|
// Set the SystemSetupInProgress Value to 0.
|
||
|
RegSetValueEx(hSetupKey,
|
||
|
L"SystemSetupInProgress",
|
||
|
0,
|
||
|
REG_DWORD,
|
||
|
(LPBYTE) &dwValue,
|
||
|
sizeof(DWORD));
|
||
|
|
||
|
dwValue = 0;
|
||
|
RegSetValueEx(hSetupKey,
|
||
|
L"SetupType",
|
||
|
0,
|
||
|
REG_DWORD,
|
||
|
(CONST BYTE *)&dwValue,
|
||
|
sizeof(DWORD));
|
||
|
|
||
|
|
||
|
|
||
|
// Delete the FactoryPreInstall value
|
||
|
RegDeleteValue(hSetupKey, L"FactoryPreInstallInProgress");
|
||
|
RegDeleteValue(hSetupKey, L"AuditInProgress");
|
||
|
|
||
|
// Close the setup reg key
|
||
|
RegCloseKey(hSetupKey);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get Organization and Owner names from registry
|
||
|
//
|
||
|
void GetNames(TCHAR szNameOrgOrg[], TCHAR szNameOrgName[])
|
||
|
{
|
||
|
HKEY hKey = NULL;
|
||
|
DWORD dwLen = 0;
|
||
|
if (ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), &hKey)) {
|
||
|
dwLen = MAX_NAME;
|
||
|
RegQueryValueEx(hKey, TEXT("RegisteredOrganization"), 0, 0, (LPBYTE)szNameOrgOrg, &dwLen);
|
||
|
|
||
|
dwLen = MAX_NAME;
|
||
|
RegQueryValueEx(hKey, TEXT("RegisteredOwner"), 0, 0, (LPBYTE)szNameOrgName, &dwLen);
|
||
|
|
||
|
RegCloseKey(hKey);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Strip out non alphabets from guid
|
||
|
//
|
||
|
DWORD StripDash(TCHAR *pszGuid)
|
||
|
{
|
||
|
TCHAR *pszOrg, *pszTemp = pszGuid;
|
||
|
pszOrg = pszGuid;
|
||
|
|
||
|
while (pszGuid && *pszGuid != TEXT('\0')) {
|
||
|
if (*pszTemp != TEXT('-') && *pszTemp != TEXT('{') && *pszTemp != TEXT('}'))
|
||
|
*pszGuid++ = *pszTemp++;
|
||
|
else
|
||
|
pszTemp++;
|
||
|
}
|
||
|
if (pszOrg)
|
||
|
return (DWORD)lstrlen(pszOrg);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// GenUniqueName - Create a random computer name with a base name of 8 chars
|
||
|
//
|
||
|
VOID GenUniqueName(
|
||
|
OUT PWSTR GeneratedString,
|
||
|
IN DWORD DesiredStrLen
|
||
|
)
|
||
|
{
|
||
|
GUID guid;
|
||
|
DWORD total = 0, length = 0;
|
||
|
TCHAR szGuid[MAX_PATH];
|
||
|
|
||
|
// If we have a valid out param
|
||
|
//
|
||
|
if (GeneratedString) {
|
||
|
|
||
|
static PCWSTR UsableChars = L"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||
|
|
||
|
//
|
||
|
// How many characters will come from the org/name string.
|
||
|
//
|
||
|
DWORD BaseLength = 8;
|
||
|
DWORD i,j;
|
||
|
DWORD UsableCount;
|
||
|
|
||
|
if( DesiredStrLen < BaseLength ) {
|
||
|
BaseLength = DesiredStrLen - 1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the Organization and Owner name from registry
|
||
|
//
|
||
|
GetNames(NameOrgOrg, NameOrgName);
|
||
|
|
||
|
if( NameOrgOrg[0] ) {
|
||
|
wcscpy( GeneratedString, NameOrgOrg );
|
||
|
} else if( NameOrgName[0] ) {
|
||
|
wcscpy( GeneratedString, NameOrgName );
|
||
|
} else {
|
||
|
wcscpy( GeneratedString, TEXT("X") );
|
||
|
for( i = 1; i < BaseLength; i++ ) {
|
||
|
wcscat( GeneratedString, TEXT("X") );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get him upper-case for our filter...
|
||
|
//
|
||
|
CharUpper(GeneratedString);
|
||
|
|
||
|
//
|
||
|
// Now we want to put a '-' at the end
|
||
|
// of our GeneratedString. We'd like it to
|
||
|
// be placed in the BASE_LENGTH character, but
|
||
|
// the string may be shorter than that, or may
|
||
|
// even have a ' ' in it. Figure out where to
|
||
|
// put the '-' now.
|
||
|
//
|
||
|
for( i = 0; i <= BaseLength; i++ ) {
|
||
|
|
||
|
//
|
||
|
// Check for a short string.
|
||
|
//
|
||
|
if( (GeneratedString[i] == 0 ) ||
|
||
|
(GeneratedString[i] == L' ') ||
|
||
|
(!wcschr(UsableChars, GeneratedString[i])) ||
|
||
|
(i == BaseLength )
|
||
|
) {
|
||
|
GeneratedString[i] = L'-';
|
||
|
GeneratedString[i+1] = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Special case the scenario where we had no usable
|
||
|
// characters.
|
||
|
//
|
||
|
if( GeneratedString[0] == L'-' ) {
|
||
|
GeneratedString[0] = 0;
|
||
|
}
|
||
|
|
||
|
total = lstrlen(GeneratedString);
|
||
|
|
||
|
// Loop until we have meet the desired string length
|
||
|
//
|
||
|
while (total < DesiredStrLen) {
|
||
|
|
||
|
// Create a unique guid to be used in the string
|
||
|
//
|
||
|
CoCreateGuid(&guid);
|
||
|
StringFromGUID2(&guid, szGuid, AS(szGuid));
|
||
|
|
||
|
// Remove the curly brace and dashes to generate the string
|
||
|
//
|
||
|
length = StripDash(szGuid);
|
||
|
total += length;
|
||
|
if (!lstrlen(GeneratedString)) {
|
||
|
if (DesiredStrLen < total)
|
||
|
lstrcpyn(GeneratedString, szGuid, DesiredStrLen+1); /* +1 for NULL */
|
||
|
else
|
||
|
lstrcpy(GeneratedString, szGuid);
|
||
|
}
|
||
|
else if (total < DesiredStrLen)
|
||
|
lstrcat(GeneratedString, szGuid);
|
||
|
else
|
||
|
_tcsncat(GeneratedString, szGuid, (length - (total - DesiredStrLen)));
|
||
|
}
|
||
|
|
||
|
CharUpper(GeneratedString);
|
||
|
|
||
|
// Assert if (total != DesiredStrLen)
|
||
|
//
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************\
|
||
|
|
||
|
BOOL // Returns TRUE if any credentials are found and
|
||
|
// are going to be returned.
|
||
|
|
||
|
GetCredentials( // Tries to get user credentials from a few
|
||
|
// different places.
|
||
|
|
||
|
LPTSTR lpszUsername, // Pointer to a string buffer that will recieve
|
||
|
// the user name for the credentials found.
|
||
|
|
||
|
DWORD cbUsername, // Size, in characters, of the lpszUsername
|
||
|
// string buffer.
|
||
|
|
||
|
LPTSTR lpszPassword, // Pointer to a string buffer that will recieve
|
||
|
// the password for the credentials found.
|
||
|
|
||
|
DWORD cbPassword, // Size, in characters, of the lpszPassword
|
||
|
// string buffer.
|
||
|
|
||
|
LPTSTR lpFileName, // Optional pointer to the file name that will
|
||
|
// contain the credentials. If this is NULL or
|
||
|
// an empty string, the know registry key will
|
||
|
// be checked instead for the credentials.
|
||
|
|
||
|
LPTSTR lpAlternate // Optional pointer to the alternate section to
|
||
|
// check first if lpFileName is vallid, or the
|
||
|
// the optional registry key to check instead of
|
||
|
// the normal known one.
|
||
|
|
||
|
);
|
||
|
|
||
|
\****************************************************************************/
|
||
|
|
||
|
BOOL GetCredentials(LPTSTR lpszUsername, DWORD cbUsername, LPTSTR lpszPassword, DWORD cbPassword, LPTSTR lpFileName, LPTSTR lpAlternate)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
TCHAR szUsername[UNLEN + 1] = NULLSTR,
|
||
|
szPassword[PWLEN + 1] = NULLSTR,
|
||
|
szDomain[DNLEN + 1] = NULLSTR;
|
||
|
LPSTR lpUserName;
|
||
|
HKEY hKey;
|
||
|
DWORD dwType,
|
||
|
dwSize;
|
||
|
BOOL bAlternate = ( lpAlternate && *lpAlternate );
|
||
|
|
||
|
// Make sure there is a filename, otherwise we check the registry.
|
||
|
//
|
||
|
if ( lpFileName && *lpFileName )
|
||
|
{
|
||
|
// First try the alternate key for a user name.
|
||
|
//
|
||
|
if ( bAlternate )
|
||
|
{
|
||
|
GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_USERNAME, NULLSTR, szUsername, AS(szUsername), lpFileName);
|
||
|
}
|
||
|
|
||
|
// If none found, try the normal section. If they happen
|
||
|
// to pass the normal section in as the alternate section then
|
||
|
// we will check it twice if no key exists, no big deal.
|
||
|
//
|
||
|
if ( NULLCHR == szUsername[0] )
|
||
|
{
|
||
|
lpAlternate = WBOM_FACTORY_SECTION;
|
||
|
GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_USERNAME, NULLSTR, szUsername, AS(szUsername), lpFileName);
|
||
|
}
|
||
|
|
||
|
// Make sure we found a user name.
|
||
|
//
|
||
|
if ( szUsername[0] )
|
||
|
{
|
||
|
// If there is now backslash in the username, and there is a domain key, use that as the domain
|
||
|
//
|
||
|
if ( ((StrChr( szUsername, CHR_BACKSLASH )) == NULL) &&
|
||
|
(GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_DOMAIN, NULLSTR, szDomain, AS(szDomain), lpFileName)) && szDomain[0]
|
||
|
)
|
||
|
{
|
||
|
// Copy the "domain\username" string into the returning buffer
|
||
|
//
|
||
|
lstrcpyn(lpszUsername, szDomain, cbUsername);
|
||
|
AddPathN(lpszUsername, szUsername, cbUsername);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Copy the username into the returning buffer
|
||
|
//
|
||
|
lstrcpyn(lpszUsername, szUsername, cbUsername);
|
||
|
}
|
||
|
|
||
|
// We found the credentials
|
||
|
//
|
||
|
bRet = TRUE;
|
||
|
|
||
|
// Get the password
|
||
|
//
|
||
|
if ( GetPrivateProfileString(lpAlternate, INI_VAL_WBOM_PASSWORD, NULLSTR, szPassword, AS(szPassword), lpFileName) )
|
||
|
{
|
||
|
// Copy the password into the returning buffer
|
||
|
//
|
||
|
lstrcpyn(lpszPassword, szPassword, cbPassword);
|
||
|
}
|
||
|
else
|
||
|
*lpszPassword = NULLCHR;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
else if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, bAlternate ? lpAlternate : REG_KEY_FACTORY, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS )
|
||
|
{
|
||
|
// Check the registry key to see if it has user credentials.
|
||
|
//
|
||
|
dwSize = sizeof(szUsername);
|
||
|
if ( ( RegQueryValueEx(hKey, REG_VAL_FACTORY_USERNAME, NULL, &dwType, (LPBYTE) szUsername, &dwSize) == ERROR_SUCCESS ) &&
|
||
|
( dwType == REG_SZ ) &&
|
||
|
( szUsername[0] ) )
|
||
|
{
|
||
|
// Check the registry key to see if it has user credentials.
|
||
|
//
|
||
|
dwSize = sizeof(szDomain);
|
||
|
if ( ( StrChr(szUsername, CHR_BACKSLASH) == NULL ) &&
|
||
|
( RegQueryValueEx(hKey, REG_VAL_FACTORY_DOMAIN, NULL, &dwType, (LPBYTE) szDomain, &dwSize) == ERROR_SUCCESS ) &&
|
||
|
( dwType == REG_SZ ) &&
|
||
|
( szDomain[0] ) )
|
||
|
{
|
||
|
// Copy the domain and username into the returning buffer.
|
||
|
//
|
||
|
AddPathN(szDomain, szUsername, AS(szDomain));
|
||
|
lstrcpyn(lpszUsername, szDomain, cbUsername);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Copy the username into the returning buffer.
|
||
|
//
|
||
|
lstrcpyn(lpszUsername, szUsername, cbUsername);
|
||
|
}
|
||
|
|
||
|
// We found the credentials
|
||
|
//
|
||
|
bRet = TRUE;
|
||
|
|
||
|
// Check the registry key to see if it has user credentials.
|
||
|
//
|
||
|
dwSize = sizeof(szPassword);
|
||
|
if ( ( RegQueryValueEx(hKey, REG_VAL_FACTORY_PASSWORD, NULL, &dwType, (LPBYTE) szPassword, &dwSize) == ERROR_SUCCESS ) &&
|
||
|
( dwType == REG_SZ ) )
|
||
|
{
|
||
|
// Copy the password into the returning buffer
|
||
|
//
|
||
|
lstrcpyn(lpszPassword, szPassword, cbPassword);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No password specified, just return and empty string.
|
||
|
//
|
||
|
*lpszPassword = NULLCHR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Always remember to close the key.
|
||
|
//
|
||
|
RegCloseKey(hKey);
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
NET_API_STATUS FactoryNetworkConnectEx(LPTSTR lpszPath, LPTSTR lpszWinBOMPath, LPTSTR lpAlternateSection, LPTSTR lpszUsername, DWORD cbUsername, LPTSTR lpszPassword, DWORD cbPassword, BOOL bState)
|
||
|
{
|
||
|
NET_API_STATUS nErr,
|
||
|
nRet = 0;
|
||
|
static BOOL bFirst = TRUE;
|
||
|
BOOL bJustStarted = FALSE;
|
||
|
TCHAR szUsername[UNLEN + DNLEN + 2] = NULLSTR,
|
||
|
szPassword[PWLEN + 1] = NULLSTR,
|
||
|
szBuffer[MAX_PATH],
|
||
|
szWinbomShare[MAX_PATH] = NULLSTR;
|
||
|
LPTSTR lpSearch;
|
||
|
DWORD dwStart;
|
||
|
|
||
|
// Get the credentials for the current section
|
||
|
//
|
||
|
if ( bState )
|
||
|
{
|
||
|
// Make sure we pass in a username buffer big enough to hold the "domain\username" string.
|
||
|
//
|
||
|
GetCredentials(szUsername, AS(szUsername), szPassword, AS(szPassword), lpszWinBOMPath, lpAlternateSection);
|
||
|
}
|
||
|
|
||
|
// Get just the share pare of the winbom path if it is a UNC.
|
||
|
//
|
||
|
if ( lpszWinBOMPath )
|
||
|
{
|
||
|
GetUncShare(lpszWinBOMPath, szWinbomShare, AS(szWinbomShare));
|
||
|
}
|
||
|
|
||
|
// Determine all of the UNC paths in the string supplied
|
||
|
//
|
||
|
lpSearch = lpszPath;
|
||
|
while ( lpSearch = StrStr(lpSearch, _T("\\\\")) )
|
||
|
{
|
||
|
// See if this is a UNC share.
|
||
|
//
|
||
|
if ( GetUncShare(lpSearch, szBuffer, AS(szBuffer)) && szBuffer[0] )
|
||
|
{
|
||
|
// We can not connect or disconnect from the share where the winbom is.
|
||
|
//
|
||
|
if ( ( NULLCHR == szWinbomShare[0] ) ||
|
||
|
( lstrcmpi(szBuffer, szWinbomShare) != 0 ) )
|
||
|
{
|
||
|
// Connect/disconnect from the share and
|
||
|
//
|
||
|
nErr = 0;
|
||
|
dwStart = GetTickCount();
|
||
|
do
|
||
|
{
|
||
|
if ( nErr )
|
||
|
{
|
||
|
Sleep(100);
|
||
|
}
|
||
|
if ( NERR_WkstaNotStarted == (nErr = ConnectNetworkResource(szBuffer, szUsername, szPassword, bState)) )
|
||
|
{
|
||
|
// Wierd bug here we are hacking around. If we just wait till the network starts, sometimes the first
|
||
|
// call it gives us a wierd error. So if we run into the not started error, then we keep retrying on
|
||
|
// any error until we time out.
|
||
|
//
|
||
|
bJustStarted = TRUE;
|
||
|
}
|
||
|
#ifdef DBG
|
||
|
LogFileStr(_T("c:\\sysprep\\winbom.log"), _T("FactoryNetworkConnect(%s)=%d [%d,%d]\n"), szBuffer, nErr, dwStart, GetTickCount());
|
||
|
#endif // DBG
|
||
|
}
|
||
|
while ( ( bFirst && bJustStarted && nErr ) &&
|
||
|
( (GetTickCount() - dwStart) < NET_TIMEOUT ) );
|
||
|
|
||
|
// If we hit an error and it is the first one,
|
||
|
// return it.
|
||
|
//
|
||
|
if ( nErr && ( 0 == nRet ) )
|
||
|
{
|
||
|
nRet = nErr;
|
||
|
}
|
||
|
|
||
|
// Once we have tried to connect to a network resource, we set
|
||
|
// this so we don't ever time out again as long as we are still
|
||
|
// running.
|
||
|
//
|
||
|
bFirst = FALSE;
|
||
|
}
|
||
|
|
||
|
// Move the pointer past the share name.
|
||
|
//
|
||
|
lpSearch += lstrlen(szBuffer);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Go past the double backslash even though it is not a UNC path.
|
||
|
//
|
||
|
lpSearch += 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Might need to return the credentials we used.
|
||
|
//
|
||
|
if ( lpszUsername && cbUsername )
|
||
|
{
|
||
|
lstrcpyn(lpszUsername, szUsername, cbUsername);
|
||
|
}
|
||
|
if ( lpszPassword && cbPassword )
|
||
|
{
|
||
|
lstrcpyn(lpszPassword, szPassword, cbPassword);
|
||
|
}
|
||
|
|
||
|
// Return the net error, or 0 if everything worked.
|
||
|
//
|
||
|
return nRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
NET_API_STATUS FactoryNetworkConnect(LPTSTR lpszPath, LPTSTR lpszWinBOMPath, LPTSTR lpAlternateSection, BOOL bState)
|
||
|
{
|
||
|
return FactoryNetworkConnectEx(lpszPath, lpszWinBOMPath, lpAlternateSection, NULL, 0, NULL, 0, bState);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*++
|
||
|
===============================================================================
|
||
|
Routine Description:
|
||
|
|
||
|
This routine will locate the WINBOM.INI file. The search algorithm will be:
|
||
|
* Check the registry key
|
||
|
* Check local floppy drives
|
||
|
* Check local CD-ROM drives
|
||
|
* Check the sysprep folder
|
||
|
* Check the root of the boot volume
|
||
|
Arguments:
|
||
|
|
||
|
lpWinBOMPath - return buffer where the winbom path will be copied.
|
||
|
cbWinbBOMPath - size of return buffer in characters.
|
||
|
lpFactoryPath - the sysprep folder where factory.exe is.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE - WINBOM.INI was found
|
||
|
|
||
|
FALSE - WINBOM.INI could not be found
|
||
|
|
||
|
===============================================================================
|
||
|
--*/
|
||
|
|
||
|
BOOL LocateWinBom(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath, LPTSTR lpFactoryPath, LPTSTR lpFactoryMode, DWORD dwFlags)
|
||
|
{
|
||
|
BOOL bFound = FALSE,
|
||
|
bRunningFromCd,
|
||
|
bNetwork = !(GET_FLAG(dwFlags, LOCATE_NONET));
|
||
|
TCHAR szWinBom[MAX_PATH] = NULLSTR;
|
||
|
TCHAR szNewShare[MAX_PATH],
|
||
|
szNewUser[256],
|
||
|
szNewPass[256],
|
||
|
szCurShare[MAX_PATH] = NULLSTR,
|
||
|
szCurUser[256] = NULLSTR,
|
||
|
szCurPass[256] = NULLSTR;
|
||
|
|
||
|
// Set the error mode so no drives display error messages ("please insert floppy")
|
||
|
//
|
||
|
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||
|
|
||
|
// Always set the return buffer to an empty string first.
|
||
|
//
|
||
|
*lpWinBOMPath = NULLCHR;
|
||
|
|
||
|
// This neat little flag is used when we think that we already found a
|
||
|
// winbom sometime this boot (like when factory runs from the run key) and
|
||
|
// we want to make sure we use the same winbom instead of searching again
|
||
|
// and maybe getting a different one.
|
||
|
//
|
||
|
if ( GET_FLAG(dwFlags, LOCATE_AGAIN) )
|
||
|
{
|
||
|
BOOL bDone;
|
||
|
|
||
|
// Try our state winbom key to see if we have a winbom that we are already using.
|
||
|
//
|
||
|
bFound = CheckWinbomRegKey(szWinBom, AS(szWinBom), szCurShare, AS(szCurShare), szCurUser, AS(szCurUser), szCurPass, AS(szCurPass), lpFactoryMode, REG_KEY_FACTORY_STATE, bNetwork, &bDone);
|
||
|
|
||
|
// Now if the registry key existed, we don't want to do anything more. Just use
|
||
|
// what we got right now and be done with it.
|
||
|
//
|
||
|
if ( bDone )
|
||
|
{
|
||
|
// Check to see if we have anything to return.
|
||
|
//
|
||
|
if ( bFound )
|
||
|
{
|
||
|
// Copy the path we found into the return buffer.
|
||
|
//
|
||
|
lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath);
|
||
|
}
|
||
|
|
||
|
// Set the error mode back to system default
|
||
|
//
|
||
|
SetErrorMode(0);
|
||
|
|
||
|
// Return right now if we found it or not.
|
||
|
//
|
||
|
return bFound;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check to see if the system drive is a CD-ROM (which pretty much
|
||
|
// means that we are running in WinPE and we should search this drive
|
||
|
// last).
|
||
|
//
|
||
|
ExpandEnvironmentStrings(DIR_SYSTEMROOT, szWinBom, AS(szWinBom));
|
||
|
bRunningFromCd = ( GetDriveType(szWinBom) == DRIVE_CDROM );
|
||
|
szWinBom[0] = NULLCHR;
|
||
|
|
||
|
// Check the magic registry key first as an option to override were the winbom is.
|
||
|
//
|
||
|
bFound = CheckWinbomRegKey(szWinBom, AS(szWinBom), szCurShare, AS(szCurShare), szCurUser, AS(szCurUser), szCurPass, AS(szCurPass), lpFactoryMode, REG_KEY_FACTORY, bNetwork, NULL);
|
||
|
|
||
|
// Walk through the drives first checking if the drive is removeable and NOT CDROM.
|
||
|
// Check for the presence of the WinBOM file and quit if it is found.
|
||
|
//
|
||
|
if ( !bFound )
|
||
|
{
|
||
|
bFound = SearchRemovableDrives(szWinBom, AS(szWinBom), lpFactoryMode, DRIVE_REMOVABLE);
|
||
|
}
|
||
|
|
||
|
// Walk through the drives again this time checking if the drive IS a CD-ROM.
|
||
|
// Check for the presence of the WinBOM file and quit if it is found. Also
|
||
|
// only do this if the OS is not running from a CD. This is so in the WinPE
|
||
|
// case the person can put a winbom on the hard drive and it will be used
|
||
|
// before the one that is always on the CD-ROM that WinPE boots from.
|
||
|
//
|
||
|
if ( !bFound )
|
||
|
{
|
||
|
bFound = SearchRemovableDrives(szWinBom, AS(szWinBom), lpFactoryMode, bRunningFromCd ? DRIVE_FIXED : DRIVE_CDROM);
|
||
|
}
|
||
|
|
||
|
// Now if still not found, check the same directory as factory.
|
||
|
//
|
||
|
if ( !bFound )
|
||
|
{
|
||
|
lstrcpyn(szWinBom, lpFactoryPath, AS(szWinBom));
|
||
|
AddPath(szWinBom, FILE_WINBOM_INI);
|
||
|
bFound = WinBOMExists(szWinBom, lpFactoryMode);
|
||
|
}
|
||
|
|
||
|
// Now if still not found, check the root of the system drive.
|
||
|
//
|
||
|
if ( !bFound )
|
||
|
{
|
||
|
ExpandEnvironmentStrings(DIR_SYSTEMROOT, szWinBom, AS(szWinBom));
|
||
|
lstrcat(szWinBom, FILE_WINBOM_INI);
|
||
|
bFound = WinBOMExists(szWinBom, lpFactoryMode);
|
||
|
}
|
||
|
|
||
|
// Now if we skipped the CD-ROM search above, do it now if we still
|
||
|
// don't have a winbom.
|
||
|
//
|
||
|
if ( !bFound && bRunningFromCd )
|
||
|
{
|
||
|
bFound = SearchRemovableDrives(szWinBom, AS(szWinBom), lpFactoryMode, DRIVE_CDROM);
|
||
|
}
|
||
|
|
||
|
// Make sure we found a WinBOM and look for a NewWinbom key.
|
||
|
//
|
||
|
if ( bFound )
|
||
|
{
|
||
|
DWORD dwLimit = 10; // Must be greater than zero.
|
||
|
BOOL bAgain;
|
||
|
LPTSTR lpszNewWinbom;
|
||
|
|
||
|
// Copy the path we found into the return buffer.
|
||
|
//
|
||
|
lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath);
|
||
|
|
||
|
// Now do the loop to search for possible NewWinBom keys.
|
||
|
//
|
||
|
do
|
||
|
{
|
||
|
// Reset the bool so we can check for alternate WinBOMs.
|
||
|
//
|
||
|
bAgain = FALSE;
|
||
|
|
||
|
// See if the NewWinBom key exists in the winbom we found.
|
||
|
//
|
||
|
if ( lpszNewWinbom = IniGetExpand(lpWinBOMPath, INI_SEC_WBOM_FACTORY, INI_KEY_WBOM_FACTORY_NEWWINBOM, NULL) )
|
||
|
{
|
||
|
LPTSTR lpShareRemove;
|
||
|
BOOL bSame = FALSE;
|
||
|
|
||
|
// The NewWinBom key might be a UNC, so see if we need to connect
|
||
|
// to the share.
|
||
|
//
|
||
|
szNewShare[0] = NULLCHR;
|
||
|
szNewUser[0] = NULLCHR;
|
||
|
szNewPass[0] = NULLCHR;
|
||
|
if ( bNetwork && GetUncShare(lpszNewWinbom, szNewShare, AS(szNewShare)) && szNewShare[0] )
|
||
|
{
|
||
|
// Only really need to connect if the we are not already
|
||
|
// connected.
|
||
|
//
|
||
|
if ( lstrcmpi(szNewShare, szCurShare) != 0 )
|
||
|
{
|
||
|
FactoryNetworkConnectEx(szNewShare, lpWinBOMPath, NULL, szNewUser, AS(szNewUser), szNewPass, AS(szNewPass), TRUE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bSame = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now make sure the winbom we found really exists and is a vallid
|
||
|
// winbom we can use.
|
||
|
//
|
||
|
if ( WinBOMExists(lpszNewWinbom, lpFactoryMode) )
|
||
|
{
|
||
|
// Copy the new winbom path we found into the return buffer.
|
||
|
//
|
||
|
lstrcpyn(lpWinBOMPath, lpszNewWinbom, cbWinbBOMPath);
|
||
|
|
||
|
bAgain = TRUE;
|
||
|
lpShareRemove = szCurShare;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lpShareRemove = szNewShare;
|
||
|
}
|
||
|
|
||
|
// Do any share cleanup that needs to happen.
|
||
|
//
|
||
|
if ( bNetwork && *lpShareRemove && !bSame )
|
||
|
{
|
||
|
FactoryNetworkConnect(lpShareRemove, NULL, NULL, FALSE);
|
||
|
*lpShareRemove = NULLCHR;
|
||
|
}
|
||
|
|
||
|
// Also save the share info if there was one so we can cleanup
|
||
|
// later if we find another winbom.
|
||
|
//
|
||
|
if ( bAgain )
|
||
|
{
|
||
|
lstrcpyn(szCurShare, szNewShare, AS(szCurShare));
|
||
|
lstrcpyn(szCurUser, szNewUser, AS(szCurUser));
|
||
|
lstrcpyn(szCurPass, szNewPass, AS(szCurPass));
|
||
|
}
|
||
|
|
||
|
// Clean up the ini key we allocated.
|
||
|
//
|
||
|
FREE(lpszNewWinbom);
|
||
|
}
|
||
|
}
|
||
|
while ( --dwLimit && bAgain );
|
||
|
}
|
||
|
|
||
|
// Save the winbom we are using (or empty string if not using one) to our state
|
||
|
// key so other programs or instances of factory running this boot know what winbom
|
||
|
// to use.
|
||
|
//
|
||
|
RegSetString(HKLM, REG_KEY_FACTORY_STATE, REG_VAL_FACTORY_WINBOM, lpWinBOMPath);
|
||
|
|
||
|
// We may want to save the credentials that we used to get to this winbom in our
|
||
|
// state key so we can get them back and reconnect, but for now if we just don't
|
||
|
// ever disconnect from the share where the winbom is, we should be fine. Only need
|
||
|
// to worry about this if the caller of the function wanted to disconnect this
|
||
|
// network resource when they were done. If we do that, these keys will have to
|
||
|
// be written. But we will have to put in a bunch of code so that CheckWinbomRegKey()
|
||
|
// returns the credentials used, and when doing our NewWinBom search that we also
|
||
|
// save the credentials we finally end up using. This is a lot of work, and I see
|
||
|
// no need for this now. Just make sure if you call this function that you do NOT
|
||
|
// call FactoryNetworkConnect() to remove the net connection.
|
||
|
//
|
||
|
// This didn't work, because when we log on we loose our net connection so we need
|
||
|
// the credentials to reconnect after logon. So I went through all the work I mentioned
|
||
|
// above to make this work.
|
||
|
//
|
||
|
RegSetString(HKLM, REG_KEY_FACTORY_STATE, REG_VAL_FACTORY_USERNAME, szCurUser);
|
||
|
RegSetString(HKLM, REG_KEY_FACTORY_STATE, REG_VAL_FACTORY_PASSWORD, szCurPass);
|
||
|
|
||
|
// Set the error mode back to system default
|
||
|
//
|
||
|
SetErrorMode(0);
|
||
|
|
||
|
// Return if we found it or not.
|
||
|
//
|
||
|
return bFound;
|
||
|
}
|
||
|
|
||
|
BOOL SetFactoryStartup(LPCTSTR lpFactory)
|
||
|
{
|
||
|
HKEY hKey;
|
||
|
BOOL bRet = TRUE;
|
||
|
|
||
|
// Now make sure we are also setup as a setup program to run before we log on.
|
||
|
//
|
||
|
if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SYSTEM\\Setup"), 0, KEY_ALL_ACCESS, &hKey ) == ERROR_SUCCESS )
|
||
|
{
|
||
|
TCHAR szFileName[MAX_PATH + 32] = NULLSTR;
|
||
|
DWORD dwVal;
|
||
|
|
||
|
//
|
||
|
// Setup the control flags for the SETUP key
|
||
|
// The Setting used are:
|
||
|
// CmdLine = c:\sysprep\factory.exe -setup
|
||
|
// SetupType = 2 (No reboot)
|
||
|
// SystemSetupInProgress = 0 (no service restrictions)
|
||
|
// MiniSetupInProgress = 0 (Not doing a mini setup)
|
||
|
// FactoryPreInstallInProgress = 1 (Delay pnp driver installs)
|
||
|
// AuditInProgress = 1 (general key to determine if the OEM is auditing the machine)
|
||
|
//
|
||
|
|
||
|
lstrcpyn(szFileName, lpFactory, AS(szFileName));
|
||
|
lstrcat(szFileName, _T(" -setup"));
|
||
|
if ( RegSetValueEx(hKey, _T("CmdLine"), 0, REG_SZ, (CONST LPBYTE) szFileName, ( lstrlen(szFileName) + 1 ) * sizeof(TCHAR)) != ERROR_SUCCESS )
|
||
|
bRet = FALSE;
|
||
|
|
||
|
dwVal = SETUPTYPE_NOREBOOT;
|
||
|
if ( RegSetValueEx(hKey, TEXT("SetupType"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS )
|
||
|
bRet = FALSE;
|
||
|
|
||
|
dwVal = 0;
|
||
|
if ( RegSetValueEx(hKey, TEXT("SystemSetupInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS )
|
||
|
bRet = FALSE;
|
||
|
|
||
|
dwVal = 0;
|
||
|
if ( RegSetValueEx(hKey, TEXT("MiniSetupInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS )
|
||
|
bRet = FALSE;
|
||
|
|
||
|
dwVal = 1;
|
||
|
if ( RegSetValueEx(hKey, TEXT("FactoryPreInstallInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS )
|
||
|
bRet = FALSE;
|
||
|
|
||
|
dwVal = 1;
|
||
|
if ( RegSetValueEx(hKey, TEXT("AuditInProgress"), 0, REG_DWORD, (CONST LPBYTE) &dwVal, sizeof(DWORD)) != ERROR_SUCCESS )
|
||
|
bRet = FALSE;
|
||
|
|
||
|
RegCloseKey(hKey);
|
||
|
}
|
||
|
else
|
||
|
bRet = FALSE;
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
BOOL UpdateDevicePathEx(HKEY hKeyRoot, LPTSTR lpszSubKey, LPTSTR lpszNewPath, LPTSTR lpszRoot, BOOL bRecursive)
|
||
|
{
|
||
|
LPSTRLIST lpSorted = NULL,
|
||
|
lpUnsorted = NULL;
|
||
|
LPTSTR lpszDevicePath;
|
||
|
|
||
|
// First add any paths already in the registry to the lists.
|
||
|
//
|
||
|
if ( lpszDevicePath = RegGetString(hKeyRoot, lpszSubKey, REG_VAL_DEVICEPATH) )
|
||
|
{
|
||
|
AddPathsToList(lpszDevicePath, NULL, &lpSorted, &lpUnsorted, FALSE);
|
||
|
FREE(lpszDevicePath);
|
||
|
}
|
||
|
|
||
|
// Now add any they wanted to the list.
|
||
|
//
|
||
|
AddPathsToList(lpszNewPath, lpszRoot, &lpSorted, &lpUnsorted, bRecursive);
|
||
|
|
||
|
// Now that we are done, we can free our sorted list.
|
||
|
//
|
||
|
SavePathList(hKeyRoot, lpszSubKey, lpSorted, FALSE);
|
||
|
|
||
|
// Now save our final list back to the registry and free
|
||
|
// it.
|
||
|
//
|
||
|
SavePathList(hKeyRoot, lpszSubKey, lpUnsorted, TRUE);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL UpdateDevicePath(LPTSTR lpszNewPath, LPTSTR lpszRoot, BOOL bRecursive)
|
||
|
{
|
||
|
return ( UpdateDevicePathEx( HKLM,
|
||
|
REGSTR_PATH_SETUP,
|
||
|
lpszNewPath,
|
||
|
lpszRoot ? lpszRoot : DIR_SYSTEMROOT,
|
||
|
bRecursive ) );
|
||
|
}
|
||
|
|
||
|
BOOL UpdateSourcePath(LPTSTR lpszSourcePath)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
if ( lpszSourcePath && *lpszSourcePath )
|
||
|
{
|
||
|
if (bRet = RegSetExpand(HKLM, REG_KEY_SETUP_SETUP, REG_VAL_SOURCEPATH, lpszSourcePath))
|
||
|
{
|
||
|
bRet = RegSetExpand(HKLM, REG_KEY_SETUP_SETUP, REG_VAL_SPSOURCEPATH, lpszSourcePath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Internal Function(s):
|
||
|
//
|
||
|
|
||
|
static BOOL CheckWinbomRegKey(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath,
|
||
|
LPTSTR lpszShare, DWORD cbShare,
|
||
|
LPTSTR lpszUser, DWORD cbUser,
|
||
|
LPTSTR lpszPass, DWORD cbPass,
|
||
|
LPTSTR lpFactoryMode, LPTSTR lpKey,
|
||
|
BOOL bNetwork, LPBOOL lpbExists)
|
||
|
{
|
||
|
HKEY hKey;
|
||
|
BOOL bFound = FALSE,
|
||
|
bExists = FALSE;
|
||
|
TCHAR szWinBom[MAX_PATH] = NULLSTR;
|
||
|
|
||
|
// Check the registry key to see if it knows about a winbom to use.
|
||
|
//
|
||
|
if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpKey, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS )
|
||
|
{
|
||
|
DWORD dwType,
|
||
|
dwSize = sizeof(szWinBom);
|
||
|
|
||
|
// Try to get the value.
|
||
|
//
|
||
|
if ( ( RegQueryValueEx(hKey, REG_VAL_FACTORY_WINBOM, NULL, &dwType, (LPBYTE) szWinBom, &dwSize) == ERROR_SUCCESS ) &&
|
||
|
( dwType == REG_SZ ) )
|
||
|
{
|
||
|
// Now the key must have existed. If there was something
|
||
|
// in the key, lets try to use it.
|
||
|
//
|
||
|
if ( szWinBom[0] )
|
||
|
{
|
||
|
TCHAR szShare[MAX_PATH] = NULLSTR,
|
||
|
szUser[256] = NULLSTR,
|
||
|
szPass[256] = NULLSTR;
|
||
|
|
||
|
// Throw some networking support in here.
|
||
|
//
|
||
|
if ( bNetwork && GetUncShare(szWinBom, szShare, AS(szShare)) && szShare[0] )
|
||
|
{
|
||
|
FactoryNetworkConnectEx(szShare, NULL, lpKey, szUser, AS(szUser), szPass, AS(szPass), TRUE);
|
||
|
}
|
||
|
|
||
|
// Check to see if the winbom is actually there still.
|
||
|
// If not, then it is bad and we should just act like
|
||
|
// the key didn't even exist.
|
||
|
//
|
||
|
if ( WinBOMExists(szWinBom, lpFactoryMode) )
|
||
|
{
|
||
|
// If found, return the winbom in the supplied buffer.
|
||
|
//
|
||
|
lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath);
|
||
|
bFound = bExists = TRUE;
|
||
|
|
||
|
// See what we might need to return.
|
||
|
//
|
||
|
if ( lpszShare && cbShare )
|
||
|
{
|
||
|
lstrcpyn(lpszShare, szShare, cbShare);
|
||
|
}
|
||
|
if ( lpszUser && cbUser )
|
||
|
{
|
||
|
lstrcpyn(lpszUser, szUser, cbUser);
|
||
|
}
|
||
|
if ( lpszPass && cbPass )
|
||
|
{
|
||
|
lstrcpyn(lpszPass, szPass, cbPass);
|
||
|
}
|
||
|
}
|
||
|
else if ( bNetwork && szShare[0] )
|
||
|
{
|
||
|
// Clean up our net connection.
|
||
|
//
|
||
|
FactoryNetworkConnect(szShare, NULL, NULL, FALSE);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// There wasn't anything in the key, but it did exist.
|
||
|
// This most likely means we didn't find the winbom the
|
||
|
// first time around, so we may need to know that now.
|
||
|
//
|
||
|
bExists = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Always remember to close the key.
|
||
|
//
|
||
|
RegCloseKey(hKey);
|
||
|
}
|
||
|
|
||
|
// If they want to know if the key existed, then return it to them.
|
||
|
//
|
||
|
if ( lpbExists )
|
||
|
{
|
||
|
*lpbExists = bExists;
|
||
|
}
|
||
|
|
||
|
// If we found a winbom, return true.
|
||
|
//
|
||
|
return bFound;
|
||
|
}
|
||
|
|
||
|
static BOOL SearchRemovableDrives(LPTSTR lpWinBOMPath, DWORD cbWinbBOMPath, LPTSTR lpFactoryMode, UINT uDriveType)
|
||
|
{
|
||
|
DWORD dwDrives;
|
||
|
TCHAR szWinBom[MAX_PATH],
|
||
|
szDrive[] = _T("_:\\");
|
||
|
BOOL bFound = FALSE;
|
||
|
|
||
|
// Loop through all the dirves on the system.
|
||
|
//
|
||
|
for ( szDrive[0] = _T('A'), dwDrives = GetLogicalDrives();
|
||
|
( szDrive[0] <= _T('Z') ) && dwDrives && !bFound;
|
||
|
szDrive[0]++, dwDrives >>= 1 )
|
||
|
{
|
||
|
// First check to see if the first bit is set (which means
|
||
|
// this drive exists in the system). Then make sure it is
|
||
|
// a drive type that we want to check for a winbom.
|
||
|
//
|
||
|
if ( ( dwDrives & 0x1 ) &&
|
||
|
( GetDriveType(szDrive) == uDriveType ) )
|
||
|
{
|
||
|
// See if there is a wINBOM.INI file on the drive.
|
||
|
//
|
||
|
lstrcpyn(szWinBom, szDrive, AS(szWinBom));
|
||
|
lstrcat(szWinBom, FILE_WINBOM_INI);
|
||
|
if ( WinBOMExists(szWinBom, lpFactoryMode) )
|
||
|
{
|
||
|
// Return the path to the winbom in the supplied buffer.
|
||
|
//
|
||
|
lstrcpyn(lpWinBOMPath, szWinBom, cbWinbBOMPath);
|
||
|
bFound = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bFound;
|
||
|
}
|
||
|
|
||
|
static BOOL WinBOMExists(LPTSTR lpWinBom, LPTSTR lpMode)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
// First the file must exists.
|
||
|
//
|
||
|
if ( FileExists(lpWinBom) )
|
||
|
{
|
||
|
TCHAR szModes[256] = NULLSTR;
|
||
|
|
||
|
// See if there is even a mode string in this winbom (has to be or
|
||
|
// we will automatically use it).
|
||
|
//
|
||
|
if ( lpMode &&
|
||
|
*lpMode &&
|
||
|
GetPrivateProfileString(WBOM_FACTORY_SECTION, INI_KEY_WBOM_FACTORY_TYPE, NULLSTR, szModes, AS(szModes), lpWinBom) &&
|
||
|
szModes[0] )
|
||
|
{
|
||
|
LPTSTR lpCheck = szModes,
|
||
|
lpNext;
|
||
|
|
||
|
// Loop through ever comma delimited field in the value we got
|
||
|
// from the winbom (there is always at least one).
|
||
|
//
|
||
|
do
|
||
|
{
|
||
|
// See if there is another mode field in this string.
|
||
|
//
|
||
|
if ( lpNext = StrChr(lpCheck, _T(',')) )
|
||
|
*lpNext++ = NULLCHR;
|
||
|
|
||
|
// Make sure there are no spaces around the field.
|
||
|
//
|
||
|
StrTrm(lpCheck, _T(' '));
|
||
|
|
||
|
// If the mode we are in matches the one in the winbom, then
|
||
|
// we are good to go.
|
||
|
//
|
||
|
if ( lstrcmpi(lpMode, lpCheck) == 0 )
|
||
|
bRet = TRUE;
|
||
|
|
||
|
// Set the check pointer to the next
|
||
|
// field.
|
||
|
//
|
||
|
lpCheck = lpNext;
|
||
|
}
|
||
|
while ( !bRet && lpCheck );
|
||
|
|
||
|
// It would be nice to log if we don't use this winbom because of this
|
||
|
// setting, but we can't really do that because we need to winbom to
|
||
|
// init logging.
|
||
|
//
|
||
|
/*
|
||
|
if ( !bRet )
|
||
|
{
|
||
|
// Log here.
|
||
|
}
|
||
|
*/
|
||
|
}
|
||
|
else
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
static void SavePathList(HKEY hKeyRoot, LPTSTR lpszSubKey, LPSTRLIST lpStrList, BOOL bWrite)
|
||
|
{
|
||
|
LPSTRLIST lpStrListNode;
|
||
|
DWORD cbDevicePath = 256,
|
||
|
dwLength = 0,
|
||
|
dwOldSize;
|
||
|
LPTSTR lpszDevicePath;
|
||
|
|
||
|
// Initialize our intial buffer we are going to use to
|
||
|
// write to the registry.
|
||
|
//
|
||
|
if ( bWrite )
|
||
|
{
|
||
|
lpszDevicePath = (LPTSTR) MALLOC(cbDevicePath * sizeof(TCHAR));
|
||
|
}
|
||
|
|
||
|
// Loop through the list.
|
||
|
//
|
||
|
while ( lpStrList )
|
||
|
{
|
||
|
// Save a pointer to the current node.
|
||
|
//
|
||
|
lpStrListNode = lpStrList;
|
||
|
|
||
|
// Advanced to the next node.
|
||
|
//
|
||
|
lpStrList = lpStrList->lpNext;
|
||
|
|
||
|
// If we are saving this list to the registry, then
|
||
|
// we need to add to our buffer.
|
||
|
//
|
||
|
if ( bWrite && lpszDevicePath )
|
||
|
{
|
||
|
// Make sure our buffer is still big enough.
|
||
|
// The two extra are for the possible semi-colon
|
||
|
// we might add and one more to be safe. We
|
||
|
// don't have to worry about the null terminator
|
||
|
// because we do less than or equal to our current
|
||
|
// buffer size.
|
||
|
//
|
||
|
dwOldSize = cbDevicePath;
|
||
|
dwLength += lstrlen(lpStrListNode->lpszData);
|
||
|
while ( cbDevicePath <= (dwLength + 2) )
|
||
|
{
|
||
|
cbDevicePath *= 2;
|
||
|
}
|
||
|
|
||
|
// If it wasn't big enough, we need to reallocate it.
|
||
|
//
|
||
|
if ( cbDevicePath > dwOldSize )
|
||
|
{
|
||
|
LPTSTR lpszTmpDevicePath = (LPTSTR) REALLOC(lpszDevicePath, cbDevicePath * sizeof(TCHAR));
|
||
|
|
||
|
//
|
||
|
// Make sure the REALLOC succeeded before reassigning the memory
|
||
|
//
|
||
|
if ( lpszTmpDevicePath )
|
||
|
{
|
||
|
lpszDevicePath = lpszTmpDevicePath;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Make sure we still have a buffer.
|
||
|
//
|
||
|
if ( lpszDevicePath )
|
||
|
{
|
||
|
// If we already have added a path, tack on a semicolon.
|
||
|
//
|
||
|
if ( *lpszDevicePath )
|
||
|
{
|
||
|
lstrcat(lpszDevicePath, _T(";"));
|
||
|
dwLength++;
|
||
|
}
|
||
|
|
||
|
// Now add our path.
|
||
|
//
|
||
|
lstrcat(lpszDevicePath, lpStrListNode->lpszData);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Free the data in this node.
|
||
|
//
|
||
|
FREE(lpStrListNode->lpszData);
|
||
|
|
||
|
// Free the node itself.
|
||
|
//
|
||
|
FREE(lpStrListNode);
|
||
|
}
|
||
|
|
||
|
// If we have the data, save it to the registry.
|
||
|
//
|
||
|
if ( bWrite && lpszDevicePath )
|
||
|
{
|
||
|
RegSetExpand(hKeyRoot, lpszSubKey, REG_VAL_DEVICEPATH, lpszDevicePath);
|
||
|
FREE(lpszDevicePath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static BOOL AddPathToList(LPTSTR lpszExpanded, LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted)
|
||
|
{
|
||
|
LPSTRLIST lpSortedNode,
|
||
|
lpUnsortedNode;
|
||
|
BOOL bQuit = FALSE;
|
||
|
|
||
|
// Loop until we get to the end or find a string that is bigger than
|
||
|
// ours.
|
||
|
//
|
||
|
while ( *lplpSorted && !bQuit )
|
||
|
{
|
||
|
// If we do this, we don't have to do the complicated
|
||
|
// indirection.
|
||
|
//
|
||
|
lpSortedNode = *lplpSorted;
|
||
|
|
||
|
// Compare the nodes string to the one we want to add.
|
||
|
//
|
||
|
switch ( CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, lpszExpanded, -1, lpSortedNode->lpszData, -1) )
|
||
|
{
|
||
|
case CSTR_EQUAL:
|
||
|
|
||
|
// If the are the same, we just return FALSE because we do
|
||
|
// not need to add it.
|
||
|
//
|
||
|
return FALSE;
|
||
|
|
||
|
case CSTR_LESS_THAN:
|
||
|
|
||
|
// If our string is less than the one in this node, we need
|
||
|
// to stop so we can insert ourself before it.
|
||
|
//
|
||
|
bQuit = TRUE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
|
||
|
// Default, just try the next item in the list.
|
||
|
//
|
||
|
lplpSorted = &(lpSortedNode->lpNext);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now we need to advance the pointer of the unsorted list to the
|
||
|
// end so we can add ours.
|
||
|
//
|
||
|
while ( *lplpUnsorted )
|
||
|
{
|
||
|
lpUnsortedNode = *lplpUnsorted;
|
||
|
lplpUnsorted = &(lpUnsortedNode->lpNext);
|
||
|
}
|
||
|
|
||
|
// Allocate our nodes. If anything fails, we have to return false.
|
||
|
//
|
||
|
if ( NULL == (lpSortedNode = (LPSTRLIST) MALLOC(sizeof(STRLIST))) )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
if ( NULL == (lpUnsortedNode = (LPSTRLIST) MALLOC(sizeof(STRLIST))) )
|
||
|
{
|
||
|
FREE(lpSortedNode);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Set the data in the sorted node and insert the list since we
|
||
|
// know where that goes right now.
|
||
|
//
|
||
|
lpSortedNode->lpszData = lpszExpanded;
|
||
|
lpSortedNode->lpNext = *lplpSorted;
|
||
|
*lplpSorted = lpSortedNode;
|
||
|
|
||
|
// Now set the data in the unsorted node and insert it at the end
|
||
|
// of that list.
|
||
|
//
|
||
|
lpUnsortedNode->lpszData = lpszPath;
|
||
|
lpUnsortedNode->lpNext = NULL;
|
||
|
*lplpUnsorted = lpUnsortedNode;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
static void EnumeratePath(LPTSTR lpszPath, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted)
|
||
|
{
|
||
|
WIN32_FIND_DATA FileFound;
|
||
|
HANDLE hFile;
|
||
|
LPTSTR lpszNewPath,
|
||
|
lpszExpandedPath;
|
||
|
DWORD cbNewPath;
|
||
|
BOOL bAdded = FALSE;
|
||
|
|
||
|
// Process all the files and directories in the directory passed in.
|
||
|
//
|
||
|
if ( (hFile = FindFirstFile(_T("*"), &FileFound)) != INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
// First check to see if this is a a directory.
|
||
|
//
|
||
|
if ( ( FileFound.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) &&
|
||
|
( lstrcmp(FileFound.cFileName, _T(".")) != 0 ) &&
|
||
|
( lstrcmp(FileFound.cFileName, _T("..")) != 0 ) &&
|
||
|
( SetCurrentDirectory(FileFound.cFileName) ) )
|
||
|
{
|
||
|
// Need the size for the new path... which is the length of the
|
||
|
// old path, plus new path, and 3 extra for the joining backslash,
|
||
|
// null terminator, and another one more to be safe.
|
||
|
//
|
||
|
cbNewPath = lstrlen(lpszPath) + lstrlen(FileFound.cFileName) + 3;
|
||
|
if ( lpszNewPath = (LPTSTR) MALLOC(cbNewPath * sizeof(TCHAR)) )
|
||
|
{
|
||
|
// Create our new path (note this one is not expanded out,
|
||
|
// it may contain environment variables.
|
||
|
//
|
||
|
lstrcpyn(lpszNewPath, lpszPath, cbNewPath);
|
||
|
AddPathN(lpszNewPath, FileFound.cFileName, cbNewPath);
|
||
|
|
||
|
// Make sure we can expand out the buffer.
|
||
|
//
|
||
|
if ( lpszExpandedPath = AllocateExpand(lpszNewPath) )
|
||
|
{
|
||
|
// Now add the path to the list.
|
||
|
//
|
||
|
bAdded = AddPathToList(lpszExpandedPath, lpszNewPath, lplpSorted, lplpUnsorted);
|
||
|
|
||
|
// If the path didn't get added, we have to free the buffer.
|
||
|
//
|
||
|
if ( !bAdded )
|
||
|
{
|
||
|
FREE(lpszExpandedPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Continue the recursive search
|
||
|
//
|
||
|
EnumeratePath(lpszNewPath, lplpSorted, lplpUnsorted);
|
||
|
|
||
|
// If the path didn't get added, we have to free the buffer.
|
||
|
//
|
||
|
if ( !bAdded )
|
||
|
{
|
||
|
FREE(lpszNewPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Set the current directory to parent, to continue.
|
||
|
//
|
||
|
SetCurrentDirectory(_T(".."));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
while ( FindNextFile(hFile, &FileFound) );
|
||
|
|
||
|
FindClose(hFile);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static BOOL AddPathsToList(LPTSTR lpszBegin, LPTSTR lpszRoot, LPSTRLIST * lplpSorted, LPSTRLIST * lplpUnsorted, BOOL bRecursive)
|
||
|
{
|
||
|
BOOL bRet = TRUE,
|
||
|
bAddBackslash = FALSE,
|
||
|
bAdded;
|
||
|
LPTSTR lpszEnd,
|
||
|
lpszPath,
|
||
|
lpszExpanded,
|
||
|
lpszCat;
|
||
|
DWORD dwSize,
|
||
|
dwBackslash;
|
||
|
|
||
|
// If they don't pass in the root we don't do anything.
|
||
|
//
|
||
|
if ( lpszRoot )
|
||
|
{
|
||
|
if ( NULLCHR == *lpszRoot )
|
||
|
{
|
||
|
lpszRoot = NULL;
|
||
|
}
|
||
|
else if ( _T('\\') != *CharPrev(lpszRoot, lpszRoot + lstrlen(lpszRoot)) )
|
||
|
{
|
||
|
// The root path passed in doesn't have a backslash at
|
||
|
// the end so we set this so that we know we have to add
|
||
|
// one each time we add a path.
|
||
|
//
|
||
|
bAddBackslash = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Loop through all the semicolon separated paths in the
|
||
|
// buffer passed to use.
|
||
|
//
|
||
|
do
|
||
|
{
|
||
|
// Find the beginning of the path past all
|
||
|
// the semicolons.
|
||
|
//
|
||
|
while ( _T(';') == *lpszBegin )
|
||
|
{
|
||
|
lpszBegin++;
|
||
|
}
|
||
|
|
||
|
if ( *lpszBegin )
|
||
|
{
|
||
|
// Find the end of the path which is the next
|
||
|
// semicolon or the end of the string, whichever
|
||
|
// comes first.
|
||
|
//
|
||
|
lpszEnd = lpszBegin;
|
||
|
while ( *lpszEnd && ( _T(';') != *lpszEnd ) )
|
||
|
{
|
||
|
lpszEnd++;
|
||
|
}
|
||
|
|
||
|
// See if our new path has a backslash at the
|
||
|
// beginning of it.
|
||
|
//
|
||
|
dwBackslash = 0;
|
||
|
if ( _T('\\') == *lpszBegin )
|
||
|
{
|
||
|
// If it does and we don't want to add one,
|
||
|
// then advance the pointer past it.
|
||
|
//
|
||
|
if ( !bAddBackslash )
|
||
|
{
|
||
|
lpszBegin++;
|
||
|
}
|
||
|
}
|
||
|
else if ( bAddBackslash )
|
||
|
{
|
||
|
// Set this so we know to add the backslash and
|
||
|
// allocate the extra space for it.
|
||
|
//
|
||
|
dwBackslash = 1;
|
||
|
}
|
||
|
|
||
|
// Figure out the size we need for the path we are going
|
||
|
// to create. It is the length of the new string, plus
|
||
|
// the root if one was passed in, plus 1 for the backslash
|
||
|
// if we need to add one, plus 2 extra (one for the null
|
||
|
// terminator and one just to be safe).
|
||
|
//
|
||
|
dwSize = ((int) (lpszEnd - lpszBegin)) + dwBackslash + 2;
|
||
|
if ( lpszRoot )
|
||
|
{
|
||
|
dwSize += lstrlen(lpszRoot);
|
||
|
}
|
||
|
|
||
|
// Now allocate our path buffer.
|
||
|
//
|
||
|
if ( lpszPath = (LPTSTR) MALLOC(dwSize * sizeof(TCHAR)) )
|
||
|
{
|
||
|
// Reset this so if anything doesn't work we know to
|
||
|
// free our allocated memory.
|
||
|
//
|
||
|
bAdded = FALSE;
|
||
|
|
||
|
// Copy the path into our buffer.
|
||
|
//
|
||
|
lpszCat = lpszPath;
|
||
|
if ( lpszRoot )
|
||
|
{
|
||
|
lstrcpy(lpszCat, lpszRoot);
|
||
|
lpszCat += lstrlen(lpszCat);
|
||
|
}
|
||
|
if ( dwBackslash )
|
||
|
{
|
||
|
*lpszCat++ = _T('\\');
|
||
|
}
|
||
|
lstrcpyn(lpszCat, lpszBegin, (int) (lpszEnd - lpszBegin) + 1);
|
||
|
|
||
|
if ( lpszExpanded = AllocateExpand(lpszPath) )
|
||
|
{
|
||
|
// Add it to our lists.
|
||
|
//
|
||
|
bAdded = AddPathToList(lpszExpanded, lpszPath, lplpSorted, lplpUnsorted);
|
||
|
|
||
|
// If this is a recursive add, we try to enumerate all the
|
||
|
// sub directories and add them as well.
|
||
|
//
|
||
|
if ( ( bRecursive ) &&
|
||
|
( DirectoryExists(lpszExpanded) ) &&
|
||
|
( SetCurrentDirectory(lpszExpanded) ) )
|
||
|
{
|
||
|
EnumeratePath(lpszPath, lplpSorted, lplpUnsorted);
|
||
|
}
|
||
|
|
||
|
// If it wasn't added to the list, then free the memory.
|
||
|
//
|
||
|
if ( !bAdded )
|
||
|
{
|
||
|
FREE(lpszExpanded);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If it wasn't added to the list, then free the memory.
|
||
|
//
|
||
|
if ( !bAdded )
|
||
|
{
|
||
|
FREE(lpszPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Reset the beginning to the next string.
|
||
|
//
|
||
|
lpszBegin = lpszEnd;
|
||
|
}
|
||
|
}
|
||
|
while ( *lpszBegin );
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
VOID CleanupSourcesDir(LPTSTR lpszSourcesDir)
|
||
|
{
|
||
|
UINT i = 0;
|
||
|
LPTSTR lpEnd = NULL;
|
||
|
|
||
|
// If we have a valid sources
|
||
|
if ( lpszSourcesDir &&
|
||
|
*lpszSourcesDir &&
|
||
|
DirectoryExists(lpszSourcesDir)
|
||
|
)
|
||
|
|
||
|
{
|
||
|
lpEnd = lpszSourcesDir + lstrlen(lpszSourcesDir);
|
||
|
|
||
|
for (i = 0; ( i < AS(CleanupDirs) ); i++)
|
||
|
{
|
||
|
AddPath(lpszSourcesDir, CleanupDirs[i]);
|
||
|
DeletePath(lpszSourcesDir);
|
||
|
*lpEnd = NULLCHR;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// External functions
|
||
|
//
|
||
|
typedef BOOL ( *OpkCheckVersion ) ( DWORD dwMajorVersion, DWORD dwQFEVersion );
|
||
|
|
||
|
//
|
||
|
// Wrapper around the syssetup OPKCheckVersion() function.
|
||
|
//
|
||
|
BOOL OpklibCheckVersion( DWORD dwMajorVersion, DWORD dwQFEVersion )
|
||
|
{
|
||
|
BOOL bRet = TRUE; // Allow tool to run by default, in case we can't load syssetup or find the entry point.
|
||
|
HINSTANCE hInstSysSetup = NULL;
|
||
|
OpkCheckVersion pOpkCheckVersion = NULL;
|
||
|
|
||
|
hInstSysSetup = LoadLibrary( _T("syssetup.dll") );
|
||
|
|
||
|
if ( hInstSysSetup )
|
||
|
{
|
||
|
pOpkCheckVersion = (OpkCheckVersion) GetProcAddress( hInstSysSetup, "OpkCheckVersion" );
|
||
|
if ( pOpkCheckVersion )
|
||
|
{
|
||
|
bRet = pOpkCheckVersion( dwMajorVersion, dwQFEVersion );
|
||
|
}
|
||
|
|
||
|
FreeLibrary( hInstSysSetup );
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|