599 lines
17 KiB
C
599 lines
17 KiB
C
|
|
/****************************************************************************\
|
|
|
|
PRODKEY.C / OPK Wizard (SETUPMGR.EXE)
|
|
|
|
Microsoft Confidential
|
|
Copyright (c) Microsoft Corporation 2001-2002
|
|
All rights reserved
|
|
|
|
Source file for the OPK Wizard that contains the external and internal
|
|
functions used by the "Product Key" wizard page.
|
|
|
|
09/2000 - Stephen Lodwick (STELO)
|
|
Ported OPK Wizard to Whistler
|
|
|
|
\****************************************************************************/
|
|
|
|
|
|
//
|
|
// Include File(s):
|
|
//
|
|
|
|
#include "pch.h"
|
|
#include "wizard.h"
|
|
#include "resource.h"
|
|
|
|
|
|
//
|
|
// Internal Defined Value(s):
|
|
//
|
|
|
|
#define INI_SEC_USERDATA _T("UserData")
|
|
#define INI_KEY_PRODUCTKEY _T("ProductKey")
|
|
#define INI_KEY_PRODUCTKEY_OLD _T("ProductID")
|
|
#define MAX_PID_FIELD 5
|
|
#define STR_DASH _T("-")
|
|
#define STR_VALID_KEYCHARS _T("23456789BCDFGHJKMPQRTVWXY")
|
|
|
|
//
|
|
// Internal Function Prototype(s):
|
|
//
|
|
|
|
static BOOL OnInit(HWND, HWND, LPARAM);
|
|
static void OnCommand(HWND, INT, HWND, UINT);
|
|
static BOOL ValidData(HWND);
|
|
static void SaveData(HWND);
|
|
LONG CALLBACK PidEditSubWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
static void PidChar(HWND hwnd, INT id, HWND hwndCtl, WPARAM wParam, LPARAM lParam);
|
|
static int PidPrev(int id);
|
|
static int PidNext(int id);
|
|
static BOOL PidPaste(HWND hwnd, INT id, HWND hwndCtl);
|
|
|
|
|
|
//
|
|
// External Function(s):
|
|
//
|
|
|
|
LRESULT CALLBACK ProductKeyDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwnd, WM_INITDIALOG, OnInit);
|
|
HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
|
|
|
|
case WM_NOTIFY:
|
|
|
|
switch ( ((NMHDR FAR *) lParam)->code )
|
|
{
|
|
case PSN_KILLACTIVE:
|
|
case PSN_RESET:
|
|
case PSN_WIZBACK:
|
|
case PSN_WIZFINISH:
|
|
break;
|
|
|
|
case PSN_WIZNEXT:
|
|
if ( ValidData(hwnd) )
|
|
{
|
|
SaveData(hwnd);
|
|
|
|
// If we are currently in the wizard, press the finish button
|
|
//
|
|
if ( GET_FLAG(OPK_ACTIVEWIZ) )
|
|
WIZ_PRESS(hwnd, PSBTN_FINISH);
|
|
}
|
|
else
|
|
WIZ_FAIL(hwnd);
|
|
break;
|
|
case PSN_QUERYCANCEL:
|
|
WIZ_CANCEL(hwnd);
|
|
break;
|
|
|
|
case PSN_HELP:
|
|
WIZ_HELP();
|
|
break;
|
|
|
|
case PSN_SETACTIVE:
|
|
g_App.dwCurrentHelp = IDH_OEMINFO;
|
|
|
|
WIZ_BUTTONS(hwnd, GET_FLAG(OPK_OEM) ? (PSWIZB_BACK | PSWIZB_NEXT) : PSWIZB_NEXT);
|
|
|
|
// Press next if the user is in auto mode
|
|
//
|
|
WIZ_NEXTONAUTO(hwnd, PSBTN_NEXT);
|
|
|
|
// We should continue on to maint. wizard if in maintenance mode
|
|
//
|
|
//if ( GET_FLAG(OPK_MAINTMODE) )
|
|
// WIZ_PRESS(hwnd, PSBTN_NEXT);
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Internal Function(s):
|
|
//
|
|
|
|
static BOOL OnInit(HWND hwnd, HWND hwndFocus, LPARAM lParam)
|
|
{
|
|
TCHAR szBuf[MAX_URL];
|
|
LPTSTR lpCurrent,
|
|
lpIndex;
|
|
DWORD dwIndex = IDC_PRODUCT_KEY1;
|
|
|
|
// Set the text limits, disable IME, and replace window proc for each edit box.
|
|
//
|
|
for ( dwIndex = IDC_PRODUCT_KEY1; dwIndex <= IDC_PRODUCT_KEY5; dwIndex++)
|
|
{
|
|
// Limit the edit box text
|
|
//
|
|
SendDlgItemMessage(hwnd, dwIndex, EM_LIMITTEXT, MAX_PID_FIELD, 0);
|
|
|
|
// Turn off the IME
|
|
//
|
|
ImmAssociateContext(GetDlgItem(hwnd, dwIndex), NULL);
|
|
|
|
// Replace the wndproc for the pid edit boxes.
|
|
//
|
|
PidEditSubWndProc(GetDlgItem(hwnd, dwIndex), WM_SUBWNDPROC, 0, 0L);
|
|
}
|
|
|
|
// Populate the Product Key fields
|
|
//
|
|
szBuf[0] = NULLCHR;
|
|
GetPrivateProfileString(INI_SEC_USERDATA, INI_KEY_PRODUCTKEY, NULLSTR, szBuf, MAX_INFOLEN, GET_FLAG(OPK_BATCHMODE) ? g_App.szOpkWizIniFile : g_App.szUnattendTxtFile);
|
|
|
|
// Check for the old ProductID as well
|
|
//
|
|
if ( szBuf[0] == NULLCHR )
|
|
{
|
|
GetPrivateProfileString(INI_SEC_USERDATA, INI_KEY_PRODUCTKEY_OLD, NULLSTR, szBuf, MAX_INFOLEN, GET_FLAG(OPK_BATCHMODE) ? g_App.szOpkWizIniFile : g_App.szUnattendTxtFile);
|
|
}
|
|
|
|
lpCurrent = szBuf;
|
|
lpIndex = lpCurrent;
|
|
|
|
|
|
// Reset the index for the next loop
|
|
//
|
|
dwIndex = IDC_PRODUCT_KEY1;
|
|
|
|
|
|
// If we have not reached the end of the string and we haven't exceded the number of fields, then continue
|
|
//
|
|
while ( *lpCurrent && dwIndex < (IDC_PRODUCT_KEY1 + 5) )
|
|
{
|
|
// If we've reached a dash, then we have the next field in the product key
|
|
//
|
|
if ( *lpCurrent == _T('-') )
|
|
{
|
|
// Set the current char to null so lpIndex is a string
|
|
//
|
|
*lpCurrent = NULLCHR;
|
|
|
|
// Set the proper Product Key field
|
|
//
|
|
SetWindowText(GetDlgItem(hwnd, dwIndex++), lpIndex);
|
|
|
|
// Move lpIndex past the NULLCHR
|
|
//
|
|
lpIndex = lpCurrent + 1;
|
|
}
|
|
|
|
// Move to the next character
|
|
//
|
|
lpCurrent++;
|
|
|
|
// We have to special case the last field because lpCurrent==NULLCHR and we would fall
|
|
// through without populating the last field
|
|
//
|
|
if ( (*lpCurrent == NULLCHR) && *lpIndex)
|
|
SetWindowText(GetDlgItem(hwnd, dwIndex++), lpIndex);
|
|
}
|
|
|
|
// Always return false to WM_INITDIALOG.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
static void OnCommand(HWND hwnd, INT id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
if ( ( codeNotify == EN_CHANGE ) &&
|
|
( MAX_PID_FIELD == GetWindowTextLength(hwndCtl) ) )
|
|
{
|
|
if ( IDC_PRODUCT_KEY5 == id )
|
|
{
|
|
id = ID_MAINT_NEXT;
|
|
hwnd = GetParent(hwnd);
|
|
}
|
|
else
|
|
id = PidNext(id);
|
|
|
|
if ( id )
|
|
{
|
|
hwndCtl = GetDlgItem(hwnd, id);
|
|
SetFocus(hwndCtl);
|
|
SendMessage(hwndCtl, EM_SETSEL, 0, (LPARAM) MAX_PID_FIELD);
|
|
}
|
|
}
|
|
}
|
|
|
|
static BOOL ValidData(HWND hwnd)
|
|
{
|
|
TCHAR szBuffer[MAX_PATH];
|
|
BOOL bProductKey = FALSE;
|
|
LPTSTR lpCurrent;
|
|
DWORD dwIndex = IDC_PRODUCT_KEY1;
|
|
UINT cb;
|
|
|
|
//
|
|
// Validate the product key
|
|
//
|
|
|
|
// Check to see if there was a key entered
|
|
//
|
|
while ( dwIndex < (IDC_PRODUCT_KEY1 + 5) && !bProductKey)
|
|
{
|
|
if ( (cb = GetDlgItemText(hwnd, dwIndex, szBuffer, STRSIZE(szBuffer)) != 0) )
|
|
bProductKey = TRUE;
|
|
|
|
dwIndex++;
|
|
}
|
|
|
|
// Make sure that each field has the proper number of characters
|
|
//
|
|
while ( dwIndex < (IDC_PRODUCT_KEY1 + 5) && bProductKey)
|
|
{
|
|
// Check to make sure that each field is five characters in length
|
|
//
|
|
if ( (cb = GetDlgItemText(hwnd, dwIndex, szBuffer, STRSIZE(szBuffer)) != 5) )
|
|
{
|
|
MsgBox(GetParent(hwnd), IDS_ERROR_PRODKEY_LEN, IDS_APPNAME, MB_ERRORBOX);
|
|
SetFocus( GetDlgItem(hwnd, dwIndex) );
|
|
return FALSE;
|
|
}
|
|
|
|
// Go to the next field
|
|
//
|
|
dwIndex++;
|
|
}
|
|
|
|
// Check to make sure that there are no invalid characters
|
|
//
|
|
dwIndex = IDC_PRODUCT_KEY1;
|
|
|
|
while ( dwIndex < (IDC_PRODUCT_KEY1 + 5) && bProductKey)
|
|
{
|
|
// Check for invalid characters in the strings
|
|
//
|
|
GetDlgItemText(hwnd, dwIndex, szBuffer, STRSIZE(szBuffer));
|
|
|
|
for ( lpCurrent = szBuffer; *lpCurrent; lpCurrent++)
|
|
{
|
|
if ( !(_tcschr(STR_VALID_KEYCHARS, *lpCurrent)) )
|
|
{
|
|
MsgBox(GetParent(hwnd), IDS_ERROR_PRODKEY_INV, IDS_APPNAME, MB_ERRORBOX);
|
|
SetFocus( GetDlgItem(hwnd, dwIndex) );
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
// Go to the next field
|
|
//
|
|
dwIndex++;
|
|
}
|
|
|
|
//
|
|
// Return our search result.
|
|
//
|
|
return TRUE;
|
|
}
|
|
|
|
static void SaveData(HWND hwnd)
|
|
{
|
|
TCHAR szKeyBuf[32],
|
|
szProductKey[MAX_PATH];
|
|
HRESULT hrCat;
|
|
|
|
// Save the product ID information
|
|
//
|
|
GetDlgItemText(hwnd, IDC_PRODUCT_KEY1, szKeyBuf, STRSIZE(szKeyBuf));
|
|
lstrcpyn(szProductKey, szKeyBuf,AS(szProductKey));
|
|
hrCat=StringCchCat(szProductKey, AS(szProductKey), STR_DASH);
|
|
|
|
GetDlgItemText(hwnd, IDC_PRODUCT_KEY2, szKeyBuf, STRSIZE(szKeyBuf));
|
|
hrCat=StringCchCat(szProductKey, AS(szProductKey), szKeyBuf);
|
|
hrCat=StringCchCat(szProductKey, AS(szProductKey), STR_DASH);
|
|
|
|
GetDlgItemText(hwnd, IDC_PRODUCT_KEY3, szKeyBuf, STRSIZE(szKeyBuf));
|
|
hrCat=StringCchCat(szProductKey, AS(szProductKey), szKeyBuf);
|
|
hrCat=StringCchCat(szProductKey, AS(szProductKey), STR_DASH);
|
|
|
|
GetDlgItemText(hwnd, IDC_PRODUCT_KEY4, szKeyBuf, STRSIZE(szKeyBuf));
|
|
hrCat=StringCchCat(szProductKey, AS(szProductKey), szKeyBuf);
|
|
hrCat=StringCchCat(szProductKey, AS(szProductKey), STR_DASH);
|
|
|
|
GetDlgItemText(hwnd, IDC_PRODUCT_KEY5, szKeyBuf, STRSIZE(szKeyBuf));
|
|
hrCat=StringCchCat(szProductKey, AS(szProductKey), szKeyBuf);
|
|
|
|
if ( lstrlen(szProductKey) <= 4 )
|
|
szProductKey[0] = NULLCHR;
|
|
|
|
// Write out the product key, if the user is not an OEM write NULL to the section just in case they populated the field in the inf
|
|
//
|
|
WritePrivateProfileString(INI_SEC_USERDATA, INI_KEY_PRODUCTKEY, ( szProductKey[0] ? szProductKey : NULL ), g_App.szUnattendTxtFile);
|
|
WritePrivateProfileString(INI_SEC_USERDATA, INI_KEY_PRODUCTKEY, ( szProductKey[0] ? szProductKey : NULL ), g_App.szOpkWizIniFile);
|
|
|
|
// if Product key specified, delete old ProductId
|
|
if (szProductKey[0]) {
|
|
WritePrivateProfileString(INI_SEC_USERDATA, INI_KEY_PRODUCTKEY_OLD, NULL, g_App.szUnattendTxtFile);
|
|
WritePrivateProfileString(INI_SEC_USERDATA, INI_KEY_PRODUCTKEY_OLD, NULL, g_App.szOpkWizIniFile);
|
|
}
|
|
}
|
|
|
|
LONG CALLBACK PidEditSubWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static FARPROC lpfnOldProc = NULL;
|
|
|
|
switch ( msg )
|
|
{
|
|
case WM_SUBWNDPROC:
|
|
lpfnOldProc = (FARPROC) GetWindowLongPtr(hwnd, GWLP_WNDPROC);
|
|
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) PidEditSubWndProc);
|
|
return 1;
|
|
|
|
case WM_KEYDOWN:
|
|
|
|
// We want to let the VK_LEFT and VK_RIGHT WM_KEYDOWN messages to call the pid char
|
|
// function because they don't generate a WM_CHAR message.
|
|
//
|
|
switch ( (TCHAR) wParam )
|
|
{
|
|
case VK_LEFT:
|
|
case VK_RIGHT:
|
|
PidChar(GetParent(hwnd), GetDlgCtrlID(hwnd), hwnd, (TCHAR) wParam, (DWORD) lParam);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_CHAR:
|
|
|
|
// Only need to do this for VK_BACK right now.
|
|
//
|
|
switch ( (TCHAR) wParam )
|
|
{
|
|
case VK_BACK:
|
|
PidChar(GetParent(hwnd), GetDlgCtrlID(hwnd), hwnd, (TCHAR) wParam, (DWORD) lParam);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_PASTE:
|
|
if ( PidPaste(GetParent(hwnd), GetDlgCtrlID(hwnd), hwnd) )
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
if ( lpfnOldProc )
|
|
return (LONG) CallWindowProc((WNDPROC) lpfnOldProc, hwnd, msg, wParam, lParam);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static void PidChar(HWND hwnd, INT id, HWND hwndCtl, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
DWORD dwPos = 0,
|
|
dwLast;
|
|
|
|
switch ( (TCHAR) wParam )
|
|
{
|
|
case VK_BACK:
|
|
|
|
// Only if we are at the beginning of the box do
|
|
// we want to switch to the previous one and delete
|
|
// a character from the end of that one.
|
|
//
|
|
SendMessage(hwndCtl, EM_GETSEL, (WPARAM) &dwPos, 0L);
|
|
if ( ( 0 == dwPos ) &&
|
|
( id = PidPrev(id) ) )
|
|
{
|
|
// First set the focus to the previous pid edit control.
|
|
//
|
|
hwndCtl = GetDlgItem(hwnd, id);
|
|
SetFocus(hwndCtl);
|
|
|
|
// Need to reset the caret to the end of the text box, or we will
|
|
// do weird things.
|
|
//
|
|
SendMessage(hwndCtl, EM_SETSEL, MAX_PID_FIELD, (LPARAM) MAX_PID_FIELD);
|
|
|
|
// Now pass the backspace key to the previous edit box.
|
|
//
|
|
PostMessage(hwndCtl, WM_CHAR, wParam, lParam);
|
|
}
|
|
break;
|
|
|
|
case VK_LEFT:
|
|
|
|
// Only if we are at the beginning of the box do
|
|
// we want to switch to the previous one.
|
|
//
|
|
SendMessage(hwndCtl, EM_GETSEL, (WPARAM) &dwPos, 0L);
|
|
if ( ( 0 == dwPos ) &&
|
|
( id = PidPrev(id) ) )
|
|
{
|
|
// First set the focus to the previous pid edit control.
|
|
//
|
|
hwndCtl = GetDlgItem(hwnd, id);
|
|
SetFocus(hwndCtl);
|
|
|
|
// Now make sure the caret is at the end of this edit box
|
|
// if at MAX_PID_FIELD, or 2nd to last if it isn't and the
|
|
// shift key isn't down.
|
|
//
|
|
if ( ( MAX_PID_FIELD <= (dwLast = (DWORD) GetWindowTextLength(hwndCtl)) ) &&
|
|
( 0 == (0XFF00 & GetKeyState(VK_SHIFT)) ) )
|
|
{
|
|
dwLast--;
|
|
}
|
|
SendMessage(hwndCtl, EM_SETSEL, dwLast, (LPARAM) dwLast);
|
|
}
|
|
break;
|
|
|
|
case VK_RIGHT:
|
|
|
|
// Need to first know where the caret is in the edit box.
|
|
//
|
|
SendMessage(hwndCtl, EM_GETSEL, 0, (LPARAM) &dwPos);
|
|
|
|
// Now we need to know how much text is in the buffer now. If the numer
|
|
// of characters is already at the max, we subtract one so that you can
|
|
// we arror to the next box instead of the end of the string. Unless the
|
|
// shift key is down, then we want them to be able to select the whole string.
|
|
//
|
|
dwLast = (DWORD) GetWindowTextLength(hwndCtl);
|
|
if ( ( MAX_PID_FIELD == GetWindowTextLength(hwndCtl) ) &&
|
|
( 0 == (0XFF00 & GetKeyState(VK_SHIFT)) ) )
|
|
{
|
|
dwLast--;
|
|
}
|
|
|
|
// Now only if this is the last character do we switch to the next pid
|
|
// edit box.
|
|
//
|
|
if ( ( dwLast <= dwPos ) &&
|
|
( id = PidNext(id) ) )
|
|
{
|
|
// First set the focus to the next pid edit control.
|
|
//
|
|
hwndCtl = GetDlgItem(hwnd, id);
|
|
SetFocus(hwndCtl);
|
|
|
|
// Now make sure the caret is at the beginning of this
|
|
// edit box.
|
|
//
|
|
SendMessage(hwndCtl, EM_SETSEL, 0, 0L);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int PidPrev(int id)
|
|
{
|
|
switch ( id )
|
|
{
|
|
case IDC_PRODUCT_KEY2:
|
|
id = IDC_PRODUCT_KEY1;
|
|
break;
|
|
|
|
case IDC_PRODUCT_KEY3:
|
|
id = IDC_PRODUCT_KEY2;
|
|
break;
|
|
|
|
case IDC_PRODUCT_KEY4:
|
|
id = IDC_PRODUCT_KEY3;
|
|
break;
|
|
|
|
case IDC_PRODUCT_KEY5:
|
|
id = IDC_PRODUCT_KEY4;
|
|
break;
|
|
|
|
default:
|
|
id = 0;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
static int PidNext(int id)
|
|
{
|
|
switch ( id )
|
|
{
|
|
case IDC_PRODUCT_KEY1:
|
|
id = IDC_PRODUCT_KEY2;
|
|
break;
|
|
|
|
case IDC_PRODUCT_KEY2:
|
|
id = IDC_PRODUCT_KEY3;
|
|
break;
|
|
|
|
case IDC_PRODUCT_KEY3:
|
|
id = IDC_PRODUCT_KEY4;
|
|
break;
|
|
|
|
case IDC_PRODUCT_KEY4:
|
|
id = IDC_PRODUCT_KEY5;
|
|
break;
|
|
|
|
default:
|
|
id = 0;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
static BOOL PidPaste(HWND hwnd, INT id, HWND hwndCtl)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
#ifdef _UNICODE
|
|
UINT uFormat = CF_UNICODETEXT;
|
|
#else // _UNICODE
|
|
UINT uFormat = CF_TEXT;
|
|
#endif // _UNICODE
|
|
|
|
if ( IsClipboardFormatAvailable(uFormat) &&
|
|
OpenClipboard(NULL) )
|
|
{
|
|
HGLOBAL hClip;
|
|
LPTSTR lpText;
|
|
DWORD dwFirst,
|
|
dwLast,
|
|
dwLength;
|
|
|
|
SendMessage(hwndCtl, EM_GETSEL, (WPARAM) &dwFirst, (LPARAM) &dwLast);
|
|
dwLength = (DWORD) GetWindowTextLength(hwndCtl);
|
|
|
|
if ( ( dwLength <= (dwLast - dwFirst) ) &&
|
|
( hClip = GetClipboardData(uFormat) ) &&
|
|
( lpText = (LPTSTR) GlobalLock(hClip) ) )
|
|
{
|
|
LPTSTR lpSearch = lpText;
|
|
TCHAR szPaste[MAX_PID_FIELD + 1];
|
|
|
|
bRet = TRUE;
|
|
|
|
do
|
|
{
|
|
hwndCtl = GetDlgItem(hwnd, id);
|
|
SetFocus(hwndCtl);
|
|
lstrcpyn(szPaste, lpSearch, AS(szPaste));
|
|
SetWindowText(hwndCtl, szPaste);
|
|
lpSearch = lpSearch + lstrlen(szPaste);
|
|
if ( ( _T('-') == *lpSearch ) ||
|
|
( _T(' ') == *lpSearch ) )
|
|
{
|
|
lpSearch++;
|
|
}
|
|
}
|
|
while ( *lpSearch && ( id = PidNext(id) ) );
|
|
|
|
GlobalUnlock(hClip);
|
|
}
|
|
CloseClipboard();
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|