/*++ Copyright (c) 1995 Microsoft Corporation Module Name: pid.c Abstract: Product id routines. Author: Ted Miller (tedm) 6-Feb-1995 Revision History: 13-Sep-1995 (t-stepl) - Check for unattended install --*/ #include "setupp.h" #include #include #pragma hdrstop CDTYPE CdType; // // Constants used for logging that are specific to this source file. // PCWSTR szPidKeyName = L"SYSTEM\\Setup\\Pid"; PCWSTR szPidListKeyName = L"SYSTEM\\Setup\\PidList"; PCWSTR szPidValueName = L"Pid"; PCWSTR szPidSelectId = L"270"; #if 0 // msdn no longer exists. PCWSTR szPidMsdnId = L"335"; #endif PCWSTR szPidOemId = L"OEM"; PCWSTR szFinalPidKeyName = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"; PCWSTR szFinalPidValueName = L"ProductId"; PCWSTR szSkuProfessionalFPP = L"B23-00079"; PCWSTR szSkuProfessionalCCP = L"B23-00082"; PCWSTR szSkuProfessionalSelect = L"B23-00305"; PCWSTR szSkuProfessionalEval = L"B23-00084"; PCWSTR szSkuServerFPP = L"C11-00016"; PCWSTR szSkuServerCCP = L"C11-00027"; PCWSTR szSkuServerSelect = L"C11-00222"; PCWSTR szSkuServerEval = L"C11-00026"; PCWSTR szSkuServerNFR = L"C11-00025"; PCWSTR szSkuAdvServerFPP = L"C10-00010"; PCWSTR szSkuAdvServerCCP = L"C10-00015"; PCWSTR szSkuAdvServerSelect = L"C10-00098"; PCWSTR szSkuAdvServerEval = L"C10-00014"; PCWSTR szSkuAdvServerNFR = L"C10-00013"; PCWSTR szSkuDTCFPP = L"C49-00001"; PCWSTR szSkuDTCSelect = L"C49-00023"; PCWSTR szSkuUnknown = L"A22-00001"; PCWSTR szSkuOEM = L"OEM-93523"; PCWSTR szMSG_LOG_PID_CANT_WRITE_PID = L"Setup was unable to save the Product Id in the registry. Error code = %1!u!."; // // Flag indicating whether to display the product id dialog. // BOOL DisplayPidDialog = TRUE; // // Product ID. // WCHAR ProductId[MAX_PRODUCT_ID+1]; PWSTR* Pid20Array = NULL; // // pid 30 product id // WCHAR Pid30Text[5][MAX_PID30_EDIT+1]; WCHAR ProductId20FromProductId30[MAX_PRODUCT_ID+1]; WCHAR Pid30Rpc[MAX_PID30_RPC+1]; WCHAR Pid30Site[MAX_PID30_SITE+1]; BYTE DigitalProductId[DIGITALPIDMAXLEN]; // // global variable used for subclassing. // WNDPROC OldPidEditProc[5]; // // Pid related flags // // BOOL DisplayPidCdDialog; // BOOL DisplayPidOemDialog; // // forward declarations // CDTYPE MiniSetupGetCdType( LPCWSTR Value ) /*++ Routine Description: Get the right CD type during Mini-Setup. PidGen changes the channel ID for the value at HKLM\Software\Microsoft\Windows NT\CurrentVersion!ProductId, we have to preserve and rely on the value at HKLM\SYSTEM\Setup\Pid!Pid Return Value: the CdType. --*/ { CDTYPE RetVal; WCHAR TmpPid30Site[MAX_PID30_SITE+1]; HKEY Key = NULL; DWORD cbData; WCHAR Data[ MAX_PATH + 1]; DWORD Type; cbData = sizeof(Data); if ( ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, szPidKeyName, 0, KEY_READ, &Key ) == ERROR_SUCCESS ) && ( RegQueryValueEx( Key, szPidValueName, 0, &Type, ( LPBYTE )Data, &cbData ) == ERROR_SUCCESS ) ) { wcsncpy(TmpPid30Site, Data + MAX_PID30_RPC, MAX_PID30_SITE+1); } else { if (Value != NULL) { wcsncpy(TmpPid30Site, Value, MAX_PID30_SITE+1); } else { TmpPid30Site[0] = L'\0'; } } TmpPid30Site[MAX_PID30_SITE] = (WCHAR)'\0'; if (_wcsicmp( TmpPid30Site, szPidSelectId ) == 0) { RetVal = CDSelect; } else if( _wcsicmp( TmpPid30Site, szPidOemId ) == 0 ) { RetVal = CDOem; } else { RetVal = CDRetail; } if (Key != NULL) { RegCloseKey(Key); } return RetVal; } BOOL ValidateAndSetPid30( VOID ) /*++ Routine Description: Using the Pid30Text global variables, check if we have a valid id. This generates the pid30 digital product id and pid20 string id, which we set into DigitalProductId and ProductId20FromProductId30 globals Arguments: None. Return Value: TRUE if pid was valid. Set's the globals correctly on success, zero's them out on failure --*/ { WCHAR tmpPid30String[5+ 5*MAX_PID30_EDIT]; BOOL rc; PCWSTR pszSkuCode; // Since we require a PID in the Select media too, we need to fill the string /* if (CDSelect == CdType){ tmpPid30String[0] = L'\0'; } else */ { wsprintf(tmpPid30String, L"%s-%s-%s-%s-%s", Pid30Text[0],Pid30Text[1],Pid30Text[2],Pid30Text[3],Pid30Text[4]); } // check for eval if (!_wcsicmp(Pid30Rpc,L"82503")){ // this is eval media ... if (ProductType == PRODUCT_WORKSTATION){ pszSkuCode = szSkuProfessionalEval; goto HaveSku; } // else // else it is server or advanced server. I don't think that at this point // we can easily tell the difference. Since it's been said that having the // correct sku is not critically important, I shall give them both the sku // code of server pszSkuCode = szSkuServerEval; goto HaveSku; } // check for NFR if (!_wcsicmp(Pid30Rpc,L"51883")){ pszSkuCode = szSkuServerNFR; goto HaveSku; } // else if (!_wcsicmp(Pid30Rpc,L"51882")){ pszSkuCode = szSkuAdvServerNFR; goto HaveSku; } // else if (CdType == CDRetail) { if (!_wcsicmp(Pid30Rpc,L"51873")){ pszSkuCode = szSkuProfessionalFPP; goto HaveSku; } if (!_wcsicmp(Pid30Rpc,L"51874")){ pszSkuCode = szSkuProfessionalCCP; goto HaveSku; } if (!_wcsicmp(Pid30Rpc,L"51876")){ pszSkuCode = szSkuServerFPP; goto HaveSku; } if (!_wcsicmp(Pid30Rpc,L"51877")){ pszSkuCode = szSkuServerCCP; goto HaveSku; } if (!_wcsicmp(Pid30Rpc,L"51879")){ pszSkuCode = szSkuAdvServerFPP; goto HaveSku; } if (!_wcsicmp(Pid30Rpc,L"51880")){ pszSkuCode = szSkuAdvServerCCP; goto HaveSku; } if (!_wcsicmp(Pid30Rpc,L"51891")){ pszSkuCode = szSkuDTCFPP; goto HaveSku; } } else if (CdType == CDOem) { pszSkuCode = szSkuUnknown; } else if (CdType == CDSelect) { if (!_wcsicmp(Pid30Rpc,L"51873")){ pszSkuCode = szSkuProfessionalSelect; goto HaveSku; } if (!_wcsicmp(Pid30Rpc,L"51876")){ pszSkuCode = szSkuServerSelect; goto HaveSku; } if (!_wcsicmp(Pid30Rpc,L"51879")){ pszSkuCode = szSkuAdvServerSelect; goto HaveSku; } if (!_wcsicmp(Pid30Rpc,L"51891")){ pszSkuCode = szSkuDTCSelect; goto HaveSku; } } pszSkuCode = szSkuUnknown; HaveSku: *(LPDWORD)DigitalProductId = sizeof(DigitalProductId); rc = SetupPIDGenW( tmpPid30String, // [IN] 25-character Secure CD-Key (gets U-Cased) Pid30Rpc, // [IN] 5-character Release Product Code pszSkuCode, // [IN] Stock Keeping Unit (formatted like 123-12345) (CdType == CDOem), // [IN] is this an OEM install? ProductId20FromProductId30, // [OUT] PID 2.0, pass in ptr to 24 character array DigitalProductId, // [OUT] pointer to binary PID3 buffer. First DWORD is the length NULL); // [OUT] optional ptr to Compliance Checking flag (can be NULL) #ifdef PRERELEASE SetupDebugPrint2(L"Pidgen returns for PID:%s and MPC:%s\n", tmpPid30String, Pid30Rpc); #endif if (!rc) { #ifdef PRERELEASE SetupDebugPrint1(L"Pidgen returns %d for PID.n", rc); #endif ZeroMemory(Pid30Text[0],5*(MAX_PID30_EDIT+1)); } else { if (*ProductId20FromProductId30 == L'\0') { SetupDebugPrint(L"ProductId20FromProductId30 is empty after call into pidgen and pidgen returns OK\n"); } if (*DigitalProductId == 0) { SetupDebugPrint(L"DigitalProductId is empty after call into pidgen and pidgen returns OK\n"); } } return rc; } LRESULT CALLBACK PidEditSubProc( IN HWND hwnd, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: Edit control subclass routine, sets the focus to the correct edit box when the user enters text. This routine assumes that the pid controls ids are in sequential order. Arguments: Standard window proc arguments. Returns: Message-dependent value. --*/ { DWORD len, id; // // eat spaces // if ((msg == WM_CHAR) && (wParam == VK_SPACE)) { return(0); } if ((msg == WM_CHAR)) { // // First override: if we have the max characters in the current edit // box, let's post the character to the next box and set focus to that // control. // if ( ( (len = (DWORD)SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0)) == MAX_PID30_EDIT) && ((wParam != VK_DELETE) && (wParam != VK_BACK)) ) { // // set the focus to the next edit control and post the character // to that edit control // if ((id = GetDlgCtrlID(hwnd)) < IDT_EDIT_PID5 ) { DWORD start, end; SendMessage(hwnd, EM_GETSEL, (WPARAM)&start,(LPARAM)&end); if (start == end) { HWND hNext = GetDlgItem(GetParent(hwnd),id+1); SetFocus(hNext); SendMessage(hNext, EM_SETSEL, (WPARAM)-1,(LPARAM)-1); PostMessage( GetDlgItem(GetParent(hwnd),id+1), WM_CHAR, wParam, lParam ); return(0); } } // // Second override: if the user hit's a delete key and they are at the // the start of an edit box, then post the delete to the previous edit // box. // } else if ( (len == 0) && ((id = GetDlgCtrlID(hwnd)) > IDT_EDIT_PID1) && ((wParam == VK_DELETE) || (wParam == VK_BACK) )) { // // set the focus to the previous edit control and post the command // to that edit control // HWND hPrev = GetDlgItem(GetParent(hwnd),id-1); SetFocus(hPrev); SendMessage(hPrev, EM_SETSEL, (WPARAM)MAX_PID30_EDIT-1,(LPARAM)MAX_PID30_EDIT); PostMessage( hPrev, WM_CHAR, wParam, lParam ); return(0); // // Third override: if posting this message will give us the maximum // characters in our in the current edit box, let's post the character // to the next box and set focus to that control. // } else if ( (len == MAX_PID30_EDIT-1) && ((wParam != VK_DELETE) && (wParam != VK_BACK)) && ((id = GetDlgCtrlID(hwnd)) < IDT_EDIT_PID5) ) { DWORD start, end; SendMessage(hwnd, EM_GETSEL, (WPARAM)&start,(LPARAM)&end); if (start == end) { HWND hNext = GetDlgItem(GetParent(hwnd),id+1); // // post the message to the edit box // CallWindowProc(OldPidEditProc[GetDlgCtrlID(hwnd)-IDT_EDIT_PID1],hwnd,msg,wParam,lParam); // // now set the focus to the next edit control // SetFocus(hNext); SendMessage(hNext, EM_SETSEL, (WPARAM)-1,(LPARAM)-1); return(0); } } } return(CallWindowProc(OldPidEditProc[GetDlgCtrlID(hwnd)-IDT_EDIT_PID1],hwnd,msg,wParam,lParam)); } INT_PTR CALLBACK Pid30CDDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: Dialog procedure for the CD Retail Pid dialog. Arguments: hWnd - a handle to the dialog proceedure. msg - the message passed from Windows. wParam - extra message dependent data. lParam - extra message dependent data. Return Value: TRUE if the value was edited. FALSE if cancelled or if no changes were made. --*/ { NMHDR *NotifyParams; DWORD i,dwRet; switch(msg) { case WM_INITDIALOG: { if( UiTest ) { // // If testing the wizard, make sure that the PidOEM page is // displayed // CdType = CDRetail; DisplayPidDialog = TRUE; } // Disable the IME on the PID edit controls for (i = 0; i < 5;i++) { ImmAssociateContext(GetDlgItem(hdlg, IDT_EDIT_PID1+i), (HIMC)NULL); } // // subclass the edit controls and limit the number of characters // for (i = 0; i < 5;i++) { SendDlgItemMessage(hdlg,IDT_EDIT_PID1+i,EM_LIMITTEXT,MAX_PID30_EDIT,0); OldPidEditProc[i] = (WNDPROC)GetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC); SetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC,(LONG_PTR)PidEditSubProc); } break; } case WM_IAMVISIBLE: MessageBoxFromMessage(hdlg,MSG_PID_IS_INVALID,NULL, IDS_ERROR,MB_OK|MB_ICONSTOP); break; case WM_SIMULATENEXT: // Simulate the next button somehow PropSheet_PressButton( GetParent(hdlg), PSBTN_NEXT); break; case WM_NOTIFY: NotifyParams = (NMHDR *)lParam; switch(NotifyParams->code) { case PSN_SETACTIVE: TESTHOOK(506); BEGIN_SECTION(L"Your (Retail) Product Key Page"); if(DisplayPidDialog && CdType == CDRetail) { // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); SetWizardButtons(hdlg,WizPageProductIdCd); SendDlgItemMessage(hdlg,IDT_EDIT_PID1,EM_SETSEL,0,-1); SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); } else { SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1); END_SECTION(L"Your (Retail) Product Key Page"); break; } if(Unattended) { if (UnattendSetActiveDlg(hdlg,IDD_PID_CD)) { // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); } } break; case PSN_WIZNEXT: case PSN_WIZFINISH: for (i = 0; i<5; i++) { GetDlgItemText(hdlg,IDT_EDIT_PID1+i,Pid30Text[i],MAX_PID30_EDIT+1); } if (!ValidateAndSetPid30()) { // failure // Tell user that the Pid is not valid, and // don't allow next page to be activated. // if (Unattended) { UnattendErrorDlg( hdlg, IDD_PID_CD ); } MessageBoxFromMessage(hdlg,MSG_PID_IS_INVALID,NULL, IDS_ERROR,MB_OK|MB_ICONSTOP); SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); if(!UiTest) { SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1); } } else { // success // // Since the Pid is already built, don't let this dialog // be displayed in the future. // // DisplayPidDialog = FALSE; // // Allow next page to be activated. // dwRet = SetCurrentProductIdInRegistry(); if (dwRet != NOERROR) { SetuplogError( LogSevError, szMSG_LOG_PID_CANT_WRITE_PID, 0, dwRet,NULL,NULL); } SetWindowLongPtr(hdlg,DWLP_MSGRESULT,0); } break; case PSN_KILLACTIVE: WizardKillHelp(hdlg); SetWindowLongPtr(hdlg,DWLP_MSGRESULT, FALSE); END_SECTION(L"Your (Retail) Product Key Page"); break; case PSN_HELP: WizardBringUpHelp(hdlg,WizPageProductIdCd); break; default: break; } break; default: return(FALSE); } return(TRUE); } INT_PTR CALLBACK Pid30OemDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: Dialog procedure for the OEM Pid dialog. Arguments: hWnd - a handle to the dialog proceedure. msg - the message passed from Windows. wParam - extra message dependent data. lParam - extra message dependent data. Return Value: TRUE if the value was edited. FALSE if cancelled or if no changes were made. --*/ { NMHDR *NotifyParams; DWORD i,dwRet; switch(msg) { case WM_INITDIALOG: { if( UiTest ) { // // If testing the wizard, make sure that the PidOEM page is // displayed // CdType = CDOem; DisplayPidDialog = TRUE; } // Disable the IME on the PID edit controls for (i = 0; i < 5;i++) { ImmAssociateContext(GetDlgItem(hdlg, IDT_EDIT_PID1+i), (HIMC)NULL); } // // subclass the edit controls and limit the number of characters // for (i = 0; i < 5;i++) { SendDlgItemMessage(hdlg,IDT_EDIT_PID1+i,EM_LIMITTEXT,MAX_PID30_EDIT,0); OldPidEditProc[i] = (WNDPROC)GetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC); SetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC,(LONG_PTR)PidEditSubProc); } break; } case WM_SIMULATENEXT: // Simulate the next button somehow PropSheet_PressButton( GetParent(hdlg), PSBTN_NEXT); break; case WM_IAMVISIBLE: MessageBoxFromMessage(hdlg,MSG_PID_OEM_IS_INVALID,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP); break; case WM_NOTIFY: NotifyParams = (NMHDR *)lParam; switch(NotifyParams->code) { case PSN_SETACTIVE: TESTHOOK(507); BEGIN_SECTION(L"Your (OEM) Product Key Page"); if(DisplayPidDialog && CdType == CDOem) { // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); SetWizardButtons(hdlg,WizPageProductIdCd); SendDlgItemMessage(hdlg,IDT_EDIT_PID1,EM_SETSEL,0,-1); SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); } else { SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1); END_SECTION(L"Your (OEM) Product Key Page"); break; } if(Unattended) { if (UnattendSetActiveDlg( hdlg, IDD_PID_OEM )) { // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); } } break; case PSN_WIZNEXT: case PSN_WIZFINISH: for (i = 0; i<5; i++) { GetDlgItemText(hdlg,IDT_EDIT_PID1+i,Pid30Text[i],MAX_PID30_EDIT+1); } if (!ValidateAndSetPid30()) { // failure // // Tell user that the Pid is not valid, and // don't allow next page to be activated. // if (Unattended) { UnattendErrorDlg( hdlg, IDD_PID_OEM ); } // if MessageBoxFromMessage(hdlg,MSG_PID_OEM_IS_INVALID,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP); SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); if(!UiTest) { SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1); } } else { // success // // The Pid is valid. // // // // Since the Pid is already built, don't let this dialog // be displayed in the future. // // DisplayPidDialog = FALSE; // Allow next page to be activated. // dwRet = SetCurrentProductIdInRegistry(); if (dwRet != NOERROR) { SetuplogError( LogSevError, szMSG_LOG_PID_CANT_WRITE_PID, 0, dwRet,NULL,NULL); } SetWindowLongPtr(hdlg,DWLP_MSGRESULT,0); } break; case PSN_KILLACTIVE: WizardKillHelp(hdlg); SetWindowLongPtr(hdlg,DWLP_MSGRESULT, FALSE ); END_SECTION(L"Your (OEM) Product Key Page"); break; case PSN_HELP: WizardBringUpHelp(hdlg,WizPageProductIdCd); break; default: break; } break; default: return(FALSE); } return(TRUE); } INT_PTR CALLBACK Pid30SelectDlgProc( IN HWND hdlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: Dialog procedure for the OEM Pid dialog. Arguments: hWnd - a handle to the dialog proceedure. msg - the message passed from Windows. wParam - extra message dependent data. lParam - extra message dependent data. Return Value: TRUE if the value was edited. FALSE if cancelled or if no changes were made. --*/ { NMHDR *NotifyParams; DWORD i,dwRet; switch(msg) { case WM_INITDIALOG: { if( UiTest ) { // // If testing the wizard, make sure that the PidOEM page is // displayed // CdType = CDSelect; DisplayPidDialog = TRUE; } // Disable the IME on the PID edit controls for (i = 0; i < 5;i++) { ImmAssociateContext(GetDlgItem(hdlg, IDT_EDIT_PID1+i), (HIMC)NULL); } // // subclass the edit controls and limit the number of characters // for (i = 0; i < 5;i++) { SendDlgItemMessage(hdlg,IDT_EDIT_PID1+i,EM_LIMITTEXT,MAX_PID30_EDIT,0); OldPidEditProc[i] = (WNDPROC)GetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC); SetWindowLongPtr(GetDlgItem(hdlg, IDT_EDIT_PID1+i),GWLP_WNDPROC,(LONG_PTR)PidEditSubProc); } break; } case WM_SIMULATENEXT: // Simulate the next button somehow PropSheet_PressButton( GetParent(hdlg), PSBTN_NEXT); break; case WM_IAMVISIBLE: MessageBoxFromMessage(hdlg,MSG_PID_OEM_IS_INVALID,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP); break; case WM_NOTIFY: NotifyParams = (NMHDR *)lParam; switch(NotifyParams->code) { case PSN_SETACTIVE: TESTHOOK(508); BEGIN_SECTION(L"Your (Select) Product Key Page"); if(DisplayPidDialog && CdType == CDSelect) { // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); SetWizardButtons(hdlg,WizPageProductIdCd); SendDlgItemMessage(hdlg,IDT_EDIT_PID1,EM_SETSEL,0,-1); SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); } else { SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1); END_SECTION(L"Your (Select) Product Key Page"); break; } if(Unattended) { if (UnattendSetActiveDlg( hdlg, IDD_PID_SELECT )) { // Page becomes active, make page visible. SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0); } } break; case PSN_WIZNEXT: case PSN_WIZFINISH: for (i = 0; i<5; i++) { GetDlgItemText(hdlg,IDT_EDIT_PID1+i,Pid30Text[i],MAX_PID30_EDIT+1); } if (!ValidateAndSetPid30()) { // failure // // Tell user that the Pid is not valid, and // don't allow next page to be activated. // if (Unattended) { UnattendErrorDlg( hdlg, IDD_PID_SELECT ); } // if MessageBoxFromMessage(hdlg,MSG_PID_OEM_IS_INVALID,NULL,IDS_ERROR,MB_OK|MB_ICONSTOP); SetFocus(GetDlgItem(hdlg,IDT_EDIT_PID1)); if(!UiTest) { SetWindowLongPtr(hdlg,DWLP_MSGRESULT,-1); } } else { // success // // The Pid is valid. // // // // Since the Pid is already built, don't let this dialog // be displayed in the future. // // DisplayPidDialog = FALSE; // Allow next page to be activated. // dwRet = SetCurrentProductIdInRegistry(); if (dwRet != NOERROR) { SetuplogError( LogSevError, szMSG_LOG_PID_CANT_WRITE_PID, 0, dwRet,NULL,NULL); } SetWindowLongPtr(hdlg,DWLP_MSGRESULT,0); } break; case PSN_KILLACTIVE: WizardKillHelp(hdlg); SetWindowLongPtr(hdlg,DWLP_MSGRESULT, FALSE ); END_SECTION(L"Your (Select) Product Key Page"); break; case PSN_HELP: WizardBringUpHelp(hdlg,WizPageProductIdCd); break; default: break; } break; default: return(FALSE); } return(TRUE); } BOOL SetPid30Variables( PWSTR Buffer ) { LPWSTR ptr; UINT i; // // all install cases are the same for pid3.0 // Check that the string specified on the unattended script file // represents a valid 25 digit product id: // // 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 - 1 2 3 4 5 // 0 1 2 3 4 5 6 7 8 9 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 // // As a first validation test, we verify that the length is correct, // then we check if the "-" characters are in the correct place // // if( ( wcslen( Buffer ) != (4+ MAX_PID30_EDIT*5)) || ( Buffer[5] != (WCHAR)L'-' ) || ( Buffer[11] != (WCHAR)L'-' ) || ( Buffer[17] != (WCHAR)L'-' ) || ( Buffer[23] != (WCHAR)L'-' ) ) { // // The Pid in the unattended script file is invalid. // return(FALSE); } for (i = 0;i<5;i++) { // // quintet i // ptr = &Buffer[i*(MAX_PID30_EDIT+1)]; wcsncpy(Pid30Text[i], ptr, MAX_PID30_EDIT+1 ); Pid30Text[i][MAX_PID30_EDIT] = (WCHAR)L'\0'; } return TRUE; } BOOL SetPid30FromAnswerFile( ) /*++ Routine Description: set the pid3.0 globals based on unattend file parameter, if it exists. Arguments: None. Return Value: --*/ { WCHAR Buffer[MAX_BUF]; DWORD dwRet; if (!GetPrivateProfileString(pwUserData, pwProductKey, L"", Buffer, sizeof(Buffer)/sizeof(WCHAR), AnswerFile)) { return(FALSE); } if (!Buffer || !*Buffer) { return(FALSE); } // Buffer contains the Product ID // Is the PID encrypted? if (lstrlen(Buffer) > (4 + MAX_PID30_EDIT*5)) { LPWSTR szDecryptedPID = NULL; if (ValidateEncryptedPID(Buffer, &szDecryptedPID) == S_OK) { lstrcpyn(Buffer, szDecryptedPID, sizeof(Buffer)/sizeof(WCHAR)); } if (szDecryptedPID) { GlobalFree(szDecryptedPID); } } if ( !SetPid30Variables( Buffer ) ) { return FALSE; } SetupDebugPrint(L"Found Product key in Answer file.\n"); // // check with pid30 to make sure it's valid // if (!ValidateAndSetPid30()) { return(FALSE); } dwRet = SetCurrentProductIdInRegistry(); if (dwRet != NOERROR) { SetuplogError( LogSevError, szMSG_LOG_PID_CANT_WRITE_PID, 0, dwRet,NULL,NULL); } return(TRUE); } BOOL InitializePid20Array( ) /*++ Routine Description: Build the array that contains all Pid20 found in the machine during textmode setup. Even though we are using pid30 now, we still have a pid20 string id (pid30 is binary and can't be displayed to the user) Arguments: None. Return Value: --*/ { LONG Error; HKEY Key; DWORD cbData; WCHAR Data[ MAX_PATH + 1]; DWORD Type; ULONG i; ULONG PidIndex; ULONG Values; WCHAR ValueName[ MAX_PATH + 1 ]; Pid20Array = NULL; // // Get the Pid from HKEY_LOCAL_MACHINE\SYSTEM\Setup\Pid // Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, szPidListKeyName, 0, KEY_READ, &Key ); if( Error != ERROR_SUCCESS ) { return( FALSE ); } Error = RegQueryInfoKey( Key, NULL, NULL, NULL, NULL, NULL, NULL, &Values, NULL, NULL, NULL, NULL ); if( Error != ERROR_SUCCESS ) { return( FALSE ); } Pid20Array = (PWSTR *)MyMalloc( (Values + 1)* sizeof( PWSTR ) ); for( i = 0, PidIndex = 0; i < Values; i++ ) { Pid20Array[PidIndex] = NULL; Pid20Array[PidIndex + 1] = NULL; swprintf( ValueName, L"Pid_%u", i ); cbData = sizeof(Data); Error = RegQueryValueEx( Key, ValueName, 0, &Type, ( LPBYTE )Data, &cbData ); if( (Error != ERROR_SUCCESS) || ( Type != REG_SZ ) || ( wcslen( Data ) != MAX_PRODUCT_ID ) ) { continue; } Pid20Array[PidIndex] = pSetupDuplicateString( Data ); PidIndex++; } RegCloseKey( Key ); return( TRUE ); } BOOL InitializePidVariables( ) /*++ Routine Description: Read from the registry some values created by textmode setup, and initialize some global Pid flags based on the values found Arguments: None. Return Value: Returns TRUE if the initialization succedded. Returns FALSE if the Pid could not be read from the registry --*/ { LONG Error; HKEY Key; DWORD cbData; WCHAR Data[ MAX_PATH + 1]; DWORD Type; ULONG StringLength; PWSTR p; DWORD Seed; DWORD RandomNumber; ULONG ChkDigit; ULONG i; PCWSTR q; BOOLEAN KeyPresent; WCHAR KeyBuffer[MAX_BUF]; // // find out if product key was entered by the user or not // NB : set the answer file (if needed) // if (!AnswerFile[0]) SpSetupLoadParameter(pwProductKey, KeyBuffer, sizeof(KeyBuffer)/sizeof(WCHAR)); KeyBuffer[0] = 0; KeyPresent = ((GetPrivateProfileString(pwUserData, pwProductKey, pwNull, KeyBuffer, sizeof(KeyBuffer)/sizeof(WCHAR), AnswerFile) != 0) && (KeyBuffer[0] != 0)); // First create an array with the Pids found during textmode setup // if( !(MiniSetup || OobeSetup) ) { InitializePid20Array(); } // // Get the Pid from HKEY_LOCAL_MACHINE\SYSTEM\Setup\Pid // Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, ((MiniSetup || OobeSetup) ? szFinalPidKeyName : szPidKeyName), 0, KEY_READ, &Key ); if( Error != ERROR_SUCCESS ) { SetuplogError( LogSevFatalError, SETUPLOG_USE_MESSAGEID, MSG_LOG_PID_CANT_READ_PID, NULL, SETUPLOG_USE_MESSAGEID, MSG_LOG_X_PARAM_RETURNED_WINERR, szRegOpenKeyEx, Error, szPidKeyName, NULL,NULL); return( FALSE ); } cbData = sizeof(Data); Error = RegQueryValueEx( Key, ((MiniSetup || OobeSetup) ? szFinalPidValueName : szPidValueName), 0, &Type, ( LPBYTE )Data, &cbData ); RegCloseKey( Key ); if( (Error != ERROR_SUCCESS) ) { SetuplogError( LogSevFatalError, SETUPLOG_USE_MESSAGEID, MSG_LOG_PID_CANT_READ_PID, NULL, SETUPLOG_USE_MESSAGEID, MSG_LOG_X_PARAM_RETURNED_WINERR, szRegQueryValueEx, Error, szPidValueName, NULL,NULL); return( FALSE ); } // // Take care of the mini-setup case first because it's quick. // The Pid seeds left behind by textmode are long gone, so // we're going to pull out a few rabbits. We'll go read the // real Pid (the one gui-mode generated the first time he // ran through) and use that to determine which kind of // PID to prompt for later on. // if( MiniSetup || OobeSetup ) { // // tuck away the rpc code for later on // wcsncpy( Pid30Rpc, Data, MAX_PID30_RPC +1 ); Pid30Rpc[MAX_PID30_RPC] = (WCHAR)'\0'; p = Data + (MAX_PID30_RPC + 1); wcsncpy(Pid30Site,p,MAX_PID30_SITE+1); Pid30Site[MAX_PID30_SITE] = (WCHAR)'\0'; // // Look to see what kind of media we're installing from. // CdType = MiniSetupGetCdType(Pid30Site); if (CdType == CDSelect) { goto SelectPid; } else { DisplayPidDialog = TRUE; } return( TRUE ); } // // Do some validation of the value read // if( ( Type != REG_SZ ) || ( ( ( StringLength = wcslen( Data ) ) != 0 ) && ( StringLength != MAX_PID30_RPC ) && ( StringLength != MAX_PID30_RPC + MAX_PID30_SITE ) ) ) { SetuplogError( LogSevFatalError, SETUPLOG_USE_MESSAGEID, MSG_LOG_PID_CANT_READ_PID, NULL, SETUPLOG_USE_MESSAGEID, MSG_LOG_PID_INVALID_PID, szRegQueryValueEx, Type, StringLength, NULL,NULL); return( FALSE ); } // // tuck away the rpc code for later on // wcsncpy( Pid30Rpc, Data, MAX_PID30_RPC +1 ); Pid30Rpc[MAX_PID30_RPC] = (WCHAR)'\0'; // // Find out the kind of product we have (by looking at the site code): // CD Retail, OEM or Select // if( StringLength > MAX_PID30_RPC ) { // // If the Pid contains the Site, then find out what it is // p = Data + MAX_PID30_RPC; wcsncpy(Pid30Site,p,MAX_PID30_SITE+1); if(_wcsicmp( Pid30Site, szPidSelectId ) == 0) { // // This is a Select CD // SelectPid: CdType = CDSelect; if (!EulaComplete && !KeyPresent) { DisplayPidDialog = TRUE; } else { // // The Pid was specified during winnt32. // Set the pid globals and build the product id string // if (!SetPid30FromAnswerFile()) { DisplayPidDialog = TRUE; goto finish; } DisplayPidDialog = FALSE; } /* // Old code. previous version of Windows did not require a PID for Select media. for (i = 0; i< 5; i++) { Pid30Text[i][0] = (WCHAR)L'\0'; } DisplayPidDialog = FALSE; if (!ValidateAndSetPid30()) { SetuplogError( LogSevFatalError, SETUPLOG_USE_MESSAGEID, MSG_LOG_PID_CANT_READ_PID, NULL, SETUPLOG_USE_MESSAGEID, MSG_LOG_PID_INVALID_PID, szRegQueryValueEx, Type, StringLength, NULL,NULL); return( FALSE ); } if (MiniSetup || OobeSetup) { return(TRUE); } */ #if 0 // msdn media no longer exists (and if it does it should be viewed as retail, // so later in this case statement we will fall thru to retail } else if (_wcsicmp( Pid30Site, szPidMsdnId ) == 0) { // // This is an MSDN CD // MsdnPid: for (i = 0; i< 5; i++) { LPWSTR ptr; ptr = (LPTSTR) &szPid30Msdn[i*(MAX_PID30_EDIT+1)]; wcsncpy(Pid30Text[i], ptr, MAX_PID30_EDIT+1 ); Pid30Text[i][MAX_PID30_EDIT] = (WCHAR)L'\0'; } CdType = CDSelect; DisplayPidDialog = FALSE; if (!ValidateAndSetPid30()) { SetuplogError( LogSevFatalError, SETUPLOG_USE_MESSAGEID, MSG_LOG_PID_CANT_READ_PID, NULL, SETUPLOG_USE_MESSAGEID, MSG_LOG_PID_INVALID_PID, szRegQueryValueEx, Type, StringLength, NULL,NULL); return( FALSE ); } if (MiniSetup) { return(TRUE); } #endif } else if( _wcsicmp( Pid30Site, szPidOemId ) == 0 ) { // // This is OEM // CdType = CDOem; if (!EulaComplete && !KeyPresent) { DisplayPidDialog = TRUE; } else { // // The Pid was specified during winnt32. // Set the pid globals and build the product id string // if (!SetPid30FromAnswerFile() ) { DisplayPidDialog = TRUE; goto finish; } DisplayPidDialog = FALSE; } } else { // // This is a bogus site assume CD Retail // CdType = CDRetail; wcsncpy( Pid30Site, L"000", MAX_PID30_SITE+1 ); Pid30Site[ MAX_PID30_SITE ] = (WCHAR)'\0'; if (!EulaComplete && !KeyPresent) { DisplayPidDialog = TRUE; } else { // // The Pid was specified during winnt32. // Set the pid globals and build the product id string // if (!SetPid30FromAnswerFile()) { DisplayPidDialog = TRUE; goto finish; } DisplayPidDialog = FALSE; } } } else { // // If it doesn't contain the Site, then it is a CD retail, // and the appropriate Pid dialog must be displayed. // CdType = CDRetail; wcsncpy( Pid30Site, L"000", MAX_PID30_SITE+1 ); Pid30Site[ MAX_PID30_SITE ] = (WCHAR)'\0'; if (!EulaComplete && !KeyPresent) { DisplayPidDialog = TRUE; } else { // // The Pid was specified during winnt32. // Set the pid globals and build the product id string // if (!SetPid30FromAnswerFile()) { DisplayPidDialog = TRUE; goto finish; } DisplayPidDialog = FALSE; } } finish: // // Don't remove the Setup\Pid here. See MiniSetupGetCdType // Delete Setup\PidList since it is no longer needed // Error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, L"SYSTEM\\Setup", 0, MAXIMUM_ALLOWED, &Key ); if( Error == ERROR_SUCCESS ) { // pSetupRegistryDelnode( Key, L"Pid" ); pSetupRegistryDelnode( Key, L"PidList" ); RegCloseKey( Key ); } return( TRUE ); }