563 lines
17 KiB
C++
563 lines
17 KiB
C++
//*********************************************************************
|
|
//* Microsoft Windows **
|
|
//* Copyright (c) 1994-1998 Microsoft Corporation
|
|
//*********************************************************************
|
|
|
|
//
|
|
// UTIL.C - common utility functions
|
|
//
|
|
|
|
// HISTORY:
|
|
//
|
|
// 96/05/22 markdu Created (from inetcfg.dll)
|
|
//
|
|
|
|
#include "pch.hpp"
|
|
#if 0
|
|
#include "string.h"
|
|
#endif
|
|
|
|
#define MAX_MSG_PARAM 8
|
|
|
|
// function prototypes
|
|
VOID _cdecl FormatErrorMessage(CHAR * pszMsg,DWORD cbMsg,CHAR * pszFmt,va_list ArgList);
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: MsgBox
|
|
|
|
SYNOPSIS: Displays a message box with the specified string ID
|
|
|
|
********************************************************************/
|
|
int MsgBox(HWND hWnd,UINT nMsgID,UINT uIcon,UINT uButtons)
|
|
{
|
|
CHAR szMsgBuf[MAX_RES_LEN+1];
|
|
CHAR szSmallBuf[SMALL_BUF_LEN+1];
|
|
|
|
LoadSz(IDS_APPNAME,szSmallBuf,sizeof(szSmallBuf));
|
|
LoadSz(nMsgID,szMsgBuf,sizeof(szMsgBuf));
|
|
|
|
MessageBeep(uIcon);
|
|
return (MessageBox(hWnd,szMsgBuf,szSmallBuf,uIcon | uButtons));
|
|
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: MsgBoxSz
|
|
|
|
SYNOPSIS: Displays a message box with the specified text
|
|
|
|
********************************************************************/
|
|
int MsgBoxSz(HWND hWnd,LPSTR szText,UINT uIcon,UINT uButtons)
|
|
{
|
|
CHAR szSmallBuf[SMALL_BUF_LEN+1];
|
|
LoadSz(IDS_APPNAME,szSmallBuf,sizeof(szSmallBuf));
|
|
|
|
MessageBeep(uIcon);
|
|
return (MessageBox(hWnd,szText,szSmallBuf,uIcon | uButtons));
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: MsgBoxParam
|
|
|
|
SYNOPSIS: Displays a message box with the specified string ID
|
|
|
|
NOTES: extra parameters are string pointers inserted into nMsgID.
|
|
|
|
********************************************************************/
|
|
int _cdecl MsgBoxParam(HWND hWnd,UINT nMsgID,UINT uIcon,UINT uButtons,...)
|
|
{
|
|
BUFFER Msg(3*MAX_RES_LEN+1); // nice n' big for room for inserts
|
|
BUFFER MsgFmt(MAX_RES_LEN+1);
|
|
|
|
if (!Msg || !MsgFmt) {
|
|
return MsgBox(hWnd,IDS_ERROutOfMemory,MB_ICONSTOP,MB_OK);
|
|
}
|
|
|
|
LoadSz(nMsgID,MsgFmt.QueryPtr(),MsgFmt.QuerySize());
|
|
|
|
FormatErrorMessage(Msg.QueryPtr(),Msg.QuerySize(),
|
|
MsgFmt.QueryPtr(),((CHAR *) &uButtons) + sizeof(uButtons));
|
|
|
|
return MsgBoxSz(hWnd,Msg.QueryPtr(),uIcon,uButtons);
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: LoadSz
|
|
|
|
SYNOPSIS: Loads specified string resource into buffer
|
|
|
|
EXIT: returns a pointer to the passed-in buffer
|
|
|
|
NOTES: If this function fails (most likely due to low
|
|
memory), the returned buffer will have a leading NULL
|
|
so it is generally safe to use this without checking for
|
|
failure.
|
|
|
|
********************************************************************/
|
|
LPSTR LoadSz(UINT idString,LPSTR lpszBuf,UINT cbBuf)
|
|
{
|
|
ASSERT(lpszBuf);
|
|
|
|
// Clear the buffer and load the string
|
|
if ( lpszBuf )
|
|
{
|
|
*lpszBuf = '\0';
|
|
LoadString( ghInstance, idString, lpszBuf, cbBuf );
|
|
}
|
|
return lpszBuf;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: GetErrorDescription
|
|
|
|
SYNOPSIS: Retrieves the text description for a given error code
|
|
and class of error (standard, setupx)
|
|
|
|
********************************************************************/
|
|
VOID GetErrorDescription(CHAR * pszErrorDesc,UINT cbErrorDesc,
|
|
UINT uError,UINT uErrorClass)
|
|
{
|
|
ASSERT(pszErrorDesc);
|
|
|
|
// set a leading null in error description
|
|
*pszErrorDesc = '\0';
|
|
|
|
switch (uErrorClass) {
|
|
|
|
case ERRCLS_STANDARD:
|
|
|
|
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,NULL,
|
|
uError,0,pszErrorDesc,cbErrorDesc,NULL)) {
|
|
// if getting system text fails, make a string a la
|
|
// "error <n> occurred"
|
|
CHAR szFmt[SMALL_BUF_LEN+1];
|
|
LoadSz(IDS_ERRFORMAT,szFmt,sizeof(szFmt));
|
|
wsprintf(pszErrorDesc,szFmt,uError);
|
|
}
|
|
|
|
break;
|
|
|
|
case ERRCLS_SETUPX:
|
|
|
|
GetSETUPXErrorText(uError,pszErrorDesc,cbErrorDesc);
|
|
break;
|
|
|
|
default:
|
|
|
|
DEBUGTRAP("Unknown error class %lu in GetErrorDescription",
|
|
uErrorClass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: FormatErrorMessage
|
|
|
|
SYNOPSIS: Builds an error message by calling FormatMessage
|
|
|
|
NOTES: Worker function for PrepareErrorMessage
|
|
|
|
********************************************************************/
|
|
VOID _cdecl FormatErrorMessage(CHAR * pszMsg,DWORD cbMsg,CHAR * pszFmt,va_list ArgList)
|
|
{
|
|
ASSERT(pszMsg);
|
|
ASSERT(pszFmt);
|
|
|
|
// build the message into the pszMsg buffer
|
|
DWORD dwCount = FormatMessage(FORMAT_MESSAGE_FROM_STRING,
|
|
pszFmt,0,0,pszMsg,cbMsg,&ArgList);
|
|
ASSERT(dwCount > 0);
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: PrepareErrorMessage
|
|
|
|
SYNOPSIS: Displays an error message for given error
|
|
|
|
ENTRY: hWnd - parent window
|
|
uStrID - ID of string resource with message format.
|
|
Should contain %1 to be replaced by error text,
|
|
additional parameters can be specified as well.
|
|
uError - error code for error to display
|
|
uErrorClass - ERRCLS_xxx ID of class of error that
|
|
uError belongs to (standard, setupx)
|
|
uIcon - icon to display
|
|
... - additional parameters to be inserted in string
|
|
specified by uStrID
|
|
|
|
********************************************************************/
|
|
VOID _cdecl PrepareErrorMessage(UINT uStrID,UINT uError,
|
|
UINT uErrorClass,UINT uIcon,...)
|
|
{
|
|
// dynamically allocate buffers for messages
|
|
BUFFER ErrorDesc(MAX_RES_LEN+1);
|
|
BUFFER ErrorFmt(MAX_RES_LEN+1);
|
|
|
|
if (!ErrorDesc || !ErrorFmt)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// get a text description based on the error code and the class
|
|
// of error it is
|
|
GetErrorDescription(ErrorDesc.QueryPtr(),
|
|
ErrorDesc.QuerySize(),uError,uErrorClass);
|
|
|
|
// load the string for the message format
|
|
LoadSz(uStrID,ErrorFmt.QueryPtr(),ErrorFmt.QuerySize());
|
|
|
|
LPSTR args[MAX_MSG_PARAM];
|
|
args[0] = (LPSTR) ErrorDesc.QueryPtr();
|
|
memcpy(&args[1],((CHAR *) &uIcon) + sizeof(uIcon),(MAX_MSG_PARAM - 1) * sizeof(LPSTR));
|
|
|
|
FormatErrorMessage(gpszLastErrorText, MAX_ERROR_TEXT,
|
|
ErrorFmt.QueryPtr(),(va_list) &args[0]);
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: RunMlsetExe
|
|
|
|
SYNOPSIS: Runs mlset32.exe, an Exchange app that needs to be
|
|
run after files are installed otherwise Exchange
|
|
barfs
|
|
|
|
NOTES: We look in registry to find path to mlset32.exe
|
|
|
|
********************************************************************/
|
|
DWORD RunMlsetExe(HWND hwndOwner)
|
|
{
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
|
|
// get path to mlset32 out of registry
|
|
RegEntry re(szRegPathSoftwareMicrosoft,HKEY_LOCAL_MACHINE);
|
|
|
|
CHAR szAppPath[MAX_PATH+1];
|
|
if (re.GetString(szRegValMlSet,szAppPath,sizeof(szAppPath))) {
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFO sti;
|
|
|
|
// set "SilentRunning" registry switch to make mlset32
|
|
// not display the Exchange wizard
|
|
RegEntry reSilent(szRegPathExchangeClientOpt,HKEY_LOCAL_MACHINE);
|
|
reSilent.SetValue(szRegValSilentRunning,(DWORD) 1);
|
|
|
|
ZeroMemory(&sti,sizeof(STARTUPINFO));
|
|
sti.cb = sizeof(STARTUPINFO);
|
|
|
|
// launch mlset32.exe
|
|
BOOL fRet = CreateProcess(NULL, (LPSTR) szAppPath,
|
|
NULL, NULL, FALSE, 0, NULL, NULL,
|
|
&sti, &pi);
|
|
if (fRet) {
|
|
CloseHandle(pi.hThread);
|
|
|
|
// wait for mlset to complete
|
|
MsgWaitForMultipleObjectsLoop(pi.hProcess);
|
|
|
|
CloseHandle(pi.hProcess);
|
|
} else {
|
|
dwRet = GetLastError();
|
|
}
|
|
|
|
// put our window in front of mlset32's
|
|
SetForegroundWindow(hwndOwner);
|
|
|
|
} else {
|
|
dwRet = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: RemoveRunOnceEntry
|
|
|
|
SYNOPSIS: Removes the specified value from setup runonce key
|
|
|
|
ENTRY: uResourceID - ID of value name in resource
|
|
(may be localized)
|
|
|
|
********************************************************************/
|
|
VOID RemoveRunOnceEntry(UINT uResourceID)
|
|
{
|
|
RegEntry re(szRegPathSetupRunOnce,HKEY_LOCAL_MACHINE);
|
|
CHAR szValueName[SMALL_BUF_LEN+1];
|
|
ASSERT(re.GetError() == ERROR_SUCCESS);
|
|
re.DeleteValue(LoadSz(uResourceID,
|
|
szValueName,sizeof(szValueName)));
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: GenerateComputerNameIfNeeded
|
|
|
|
SYNOPSIS: Makes up and stores in the registry a computer and/or
|
|
workgroup name if not already set.
|
|
|
|
NOTES: If we don't do this, user will get prompted for computer
|
|
name and workgroup. These aren't meaningful to the user
|
|
so we'll just make something up if these aren't set.
|
|
|
|
********************************************************************/
|
|
BOOL GenerateComputerNameIfNeeded(VOID)
|
|
{
|
|
CHAR szComputerName[CNLEN+1]="";
|
|
CHAR szWorkgroupName[DNLEN+1]="";
|
|
BOOL fNeedToSetComputerName = FALSE;
|
|
|
|
// get the computer name out of the registry
|
|
RegEntry reCompName(szRegPathComputerName,HKEY_LOCAL_MACHINE);
|
|
if (reCompName.GetError() == ERROR_SUCCESS) {
|
|
reCompName.GetString(szRegValComputerName,szComputerName,
|
|
sizeof(szComputerName));
|
|
if (!lstrlen(szComputerName)) {
|
|
// no computer name set! make one up
|
|
GenerateDefaultName(szComputerName,sizeof(szComputerName),
|
|
(CHAR *) szRegValOwner,IDS_DEF_COMPUTER_NAME);
|
|
// store the generated computer name in the registry
|
|
reCompName.SetValue(szRegValComputerName,szComputerName);
|
|
|
|
// also need to store the computer name in the workgroup key
|
|
// which we will open below... set a flag so we know to do this.
|
|
// (don't ask me why they store the computer name in two places...
|
|
// but we need to set both.)
|
|
fNeedToSetComputerName = TRUE;
|
|
}
|
|
}
|
|
|
|
// get the workgroup name out of the registry
|
|
RegEntry reWorkgroup(szRegPathWorkgroup,HKEY_LOCAL_MACHINE);
|
|
if (reWorkgroup.GetError() == ERROR_SUCCESS) {
|
|
|
|
// if we set a new computer name up above, then we have to set
|
|
// a 2nd copy of the new name now, in the workgroup key
|
|
if (fNeedToSetComputerName) {
|
|
reWorkgroup.SetValue(szRegValComputerName,szComputerName);
|
|
}
|
|
|
|
|
|
reWorkgroup.GetString(szRegValWorkgroup,szWorkgroupName,
|
|
sizeof(szWorkgroupName));
|
|
if (!lstrlen(szWorkgroupName)) {
|
|
// no workgroup name set! make one up
|
|
GenerateDefaultName(szWorkgroupName,sizeof(szWorkgroupName),
|
|
(CHAR *) szRegValOrganization,IDS_DEF_WORKGROUP_NAME);
|
|
// store the generated workgroup name in the registry
|
|
reWorkgroup.SetValue(szRegValWorkgroup,szWorkgroupName);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: GenerateDefaultName
|
|
|
|
SYNOPSIS: Generates default computer or workgroup name
|
|
|
|
ENTRY: pszName - buffer to be filled in with name
|
|
cbName - size of cbName buffer
|
|
pszRegValName - name of registry value in ...Windows\CurrentVersion
|
|
key to generate name from
|
|
uIDDefName - ID of string resource to use if no value is
|
|
present in registry to generate name from
|
|
|
|
********************************************************************/
|
|
BOOL GenerateDefaultName(CHAR * pszName,DWORD cbName,CHAR * pszRegValName,
|
|
UINT uIDDefName)
|
|
{
|
|
ASSERT(pszName);
|
|
ASSERT(pszRegValName);
|
|
|
|
*pszName = '\0'; // NULL-terminate buffer
|
|
|
|
// look for registered owner/organization name in registry
|
|
RegEntry reSetup(szRegPathSetup,HKEY_LOCAL_MACHINE);
|
|
if (reSetup.GetError() == ERROR_SUCCESS) {
|
|
if (reSetup.GetString(pszRegValName,pszName,cbName) &&
|
|
lstrlen(pszName)) {
|
|
// got string from registry... now terminate at first whitespace
|
|
CHAR * pch = pszName;
|
|
while (*pch) {
|
|
if (*pch == ' ') {
|
|
// found a space, terminate here and stop
|
|
*pch = '\0';
|
|
} else {
|
|
// advance to next char, keep going
|
|
pch = CharNext(pch);
|
|
}
|
|
}
|
|
// all done!
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// couldn't get this name from registry, go for our fallback name
|
|
// from resource
|
|
|
|
LoadSz(uIDDefName,pszName,cbName);
|
|
return TRUE;
|
|
}
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: MsgWaitForMultipleObjectsLoop
|
|
|
|
SYNOPSIS: Blocks until the specified object is signaled, while
|
|
still dispatching messages to the main thread.
|
|
|
|
********************************************************************/
|
|
DWORD MsgWaitForMultipleObjectsLoop(HANDLE hEvent)
|
|
{
|
|
MSG msg;
|
|
DWORD dwObject;
|
|
while (1)
|
|
{
|
|
// NB We need to let the run dialog become active so we have to half handle sent
|
|
// messages but we don't want to handle any input events or we'll swallow the
|
|
// type-ahead.
|
|
dwObject = MsgWaitForMultipleObjects(1, &hEvent, FALSE,INFINITE, QS_ALLINPUT);
|
|
// Are we done waiting?
|
|
switch (dwObject) {
|
|
case WAIT_OBJECT_0:
|
|
case WAIT_FAILED:
|
|
return dwObject;
|
|
|
|
case WAIT_OBJECT_0 + 1:
|
|
// got a message, dispatch it and wait again
|
|
while (PeekMessage(&msg, NULL,0, 0, PM_REMOVE)) {
|
|
DispatchMessage(&msg);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
// never gets here
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
// 10/24/96 jmazner Normandy 6968
|
|
// No longer neccessary thanks to Valdon's hooks for invoking ICW.
|
|
|
|
|
|
NAME: SetDesktopInternetIconToBrowser
|
|
|
|
SYNOPSIS: "Points" The Internet desktop icon to web browser
|
|
(Internet Explorer)
|
|
|
|
NOTES: The Internet icon may initially "point" at this wizard,
|
|
we need to set it to launch web browser once we complete
|
|
successfully.
|
|
|
|
********************************************************************/
|
|
/********BOOL SetDesktopInternetIconToBrowser(VOID)
|
|
{
|
|
CHAR szAppPath[MAX_PATH+1]="";
|
|
BOOL fRet = FALSE;
|
|
|
|
// look in the app path section in registry to get path to internet
|
|
// explorer
|
|
|
|
RegEntry reAppPath(szRegPathIexploreAppPath,HKEY_LOCAL_MACHINE);
|
|
ASSERT(reAppPath.GetError() == ERROR_SUCCESS);
|
|
if (reAppPath.GetError() == ERROR_SUCCESS) {
|
|
|
|
reAppPath.GetString(szNull,szAppPath,sizeof(szAppPath));
|
|
ASSERT(reAppPath.GetError() == ERROR_SUCCESS);
|
|
|
|
}
|
|
|
|
// set the path to internet explorer as the open command for the
|
|
// internet desktop icon
|
|
if (lstrlen(szAppPath)) {
|
|
RegEntry reIconOpenCmd(szRegPathInternetIconCommand,HKEY_CLASSES_ROOT);
|
|
ASSERT(reIconOpenCmd.GetError() == ERROR_SUCCESS);
|
|
if (reIconOpenCmd.GetError() == ERROR_SUCCESS) {
|
|
UINT uErr = reIconOpenCmd.SetValue(szNull,szAppPath);
|
|
ASSERT(uErr == ERROR_SUCCESS);
|
|
|
|
fRet = (uErr == ERROR_SUCCESS);
|
|
}
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
******/
|
|
|
|
/*******************************************************************
|
|
|
|
NAME: PrepareForRunOnceApp
|
|
|
|
SYNOPSIS: Copies wallpaper value in registry to make the runonce
|
|
app happy
|
|
|
|
NOTES: The runonce app (the app that displays a list of apps
|
|
that are run once at startup) has a bug. At first boot,
|
|
it wants to change the wallpaper from the setup wallpaper
|
|
to what the user had before running setup. Setup tucks
|
|
the "old" wallpaper away in a private key, then changes
|
|
the wallpaper to the setup wallpaper. After the runonce
|
|
app finishes, it looks in the private key to get the old
|
|
wallpaper and sets that to be the current wallpaper.
|
|
However, it does this all the time, not just at first boot!
|
|
The end effect is that whenever you do anything that
|
|
causes runonce.exe to run (add stuff thru add/remove
|
|
programs control panel), your wallpaper gets set back to
|
|
whatever it was when you installed win 95. This is
|
|
especially bad for Plus!, since wallpaper settings are an
|
|
important part of the product.
|
|
|
|
To work around this bug, we copy the current wallpaper settings
|
|
(which we want preserved) to setup's private key. When
|
|
runonce runs it will say "aha!" and copy those values back
|
|
to the current settings.
|
|
|
|
********************************************************************/
|
|
VOID PrepareForRunOnceApp(VOID)
|
|
{
|
|
// open a key to the current wallpaper settings
|
|
RegEntry reDesktop(szRegPathDesktop,HKEY_CURRENT_USER);
|
|
ASSERT(reDesktop.GetError() == ERROR_SUCCESS);
|
|
|
|
// open a key to the private setup section
|
|
RegEntry reSetup(szRegPathSetupWallpaper,HKEY_LOCAL_MACHINE);
|
|
ASSERT(reSetup.GetError() == ERROR_SUCCESS);
|
|
|
|
if (reDesktop.GetError() == ERROR_SUCCESS &&
|
|
reSetup.GetError() == ERROR_SUCCESS) {
|
|
CHAR szWallpaper[MAX_PATH+1]="";
|
|
CHAR szTiled[10]=""; // big enough for "1" + slop
|
|
|
|
// get the current wallpaper name
|
|
if (reDesktop.GetString(szRegValWallpaper,szWallpaper,
|
|
sizeof(szWallpaper))) {
|
|
|
|
// set the current wallpaper name in setup's private section
|
|
UINT uRet=reSetup.SetValue(szRegValWallpaper,szWallpaper);
|
|
ASSERT(uRet == ERROR_SUCCESS);
|
|
|
|
// get the current 'tiled' value.
|
|
reDesktop.GetString(szRegValTileWallpaper,szTiled,
|
|
sizeof(szTiled));
|
|
|
|
// set the 'tiled' value in setup's section
|
|
if (lstrlen(szTiled)) {
|
|
uRet=reSetup.SetValue(szRegValTileWallpaper,szTiled);
|
|
ASSERT(uRet == ERROR_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
}
|