/*++ Copyright (c) 1997 Microsoft Corporation All rights reserved. Module Name: Network.c Abstract: Routines to migrate Win95 network printers to NT via using RunOnce entries Author: Muhunthan Sivapragasam (MuhuntS) 18-Aug-1997 Revision History: --*/ #include "precomp.h" BOOL bDoNetPrnUpgrade = FALSE; LPSTR pszNetPrnEntry = NULL; CHAR szSpool[] = "\\spool\\"; CHAR szMigDll[] = "migrate.dll"; CHAR szRunOnceCount[] = "RunOnceCount"; CHAR szRunOnceCountPath[] = "System\\CurrentControlSet\\control\\Print"; CHAR szRunOnceRegistryPath[] = "Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; // // This is stored in the registry so when network printer upgrade using // RunOnce key is tries enough times without success we can delete files // #define MIN_NETWORK_PRN_RETRIES 5 DWORD dwRunOnceCount = 0; LPSTR GetRunOnceValueToSet( ) /*++ --*/ { CHAR szPath[MAX_PATH]; DWORD dwLen, dwSize; LPSTR pszRet = NULL; dwSize = sizeof(szPath)/sizeof(szPath[0]); if ( !(dwLen = GetFileNameInSpoolDir(szPath, dwSize, szMigDll)) ) goto Done; // // Now build up the RunOnce key which will be set for each user // dwSize = strlen("rundll32.exe") + dwLen + + strlen("ProcessWin9xNetworkPrinters") + 4; if ( pszRet = AllocMem(dwSize * sizeof(CHAR)) ) StringCchPrintfA(pszRet, dwSize, "rundll32.exe %s,ProcessWin9xNetworkPrinters", szPath); Done: return pszRet; } VOID SetupNetworkPrinterUpgrade( IN LPCSTR pszWorkingDir ) /*++ Routine Description: This is called during InitializeSystemNT to setup the upgrade of network printers Arguments: pszWorkingDir : Gives the working directory assigned for printing Return Value: None If everything was setup right bDoNetPrnUpgrade is TRUE, and pszNetPrnEntry is the value to set in per user registry for RunOnce --*/ { CHAR szSource[MAX_PATH], szTarget[MAX_PATH]; DWORD dwSize, dwLen; // // First check if the source paths is ok // dwLen = strlen(szNetprnFile); dwSize = sizeof(szTarget)/sizeof(szTarget[0]); if ( strlen(pszWorkingDir) + dwLen + 2 > dwSize ) return; // // Need to make a copy of migrate.dll and netwkprn.txt to // the %windir%\system32\spool directory // StringCchPrintfA(szSource, SIZECHARS(szSource), "%s\\%s", pszWorkingDir, szNetprnFile); if ( !GetFileNameInSpoolDir(szTarget, dwSize, szNetprnFile) || !CopyFileA(szSource, szTarget, FALSE) ) return; if (!MakeACopyOfMigrateDll( pszWorkingDir )) { return; } bDoNetPrnUpgrade = (pszNetPrnEntry = GetRunOnceValueToSet()) != NULL; } VOID WriteRunOnceCount( ) /*++ Routine Description: This routine is called to write the number of times we need to try the network printer upgrade Arguments: None Return Value: None --*/ { HKEY hKey; DWORD dwSize; if ( dwRunOnceCount == 0 ) return; // // We will try number of user + MIN_NETWORK_PRN_RETRIES till we succeed // dwRunOnceCount += MIN_NETWORK_PRN_RETRIES; if ( ERROR_SUCCESS != RegOpenKeyExA(HKEY_LOCAL_MACHINE, szRunOnceCountPath, 0, KEY_WRITE, &hKey) ) return; dwSize = sizeof(dwRunOnceCount); RegSetValueExA(hKey, szRunOnceCount, 0, REG_DWORD, (LPBYTE)&dwRunOnceCount, dwSize); RegCloseKey(hKey); } BOOL ProcessNetPrnUpgradeForUser( HKEY hKeyUser ) /*++ Routine Description: This is called during MigrateUserNT to handle network printer upgrade for the user Arguments: hKeyUser : Handle to the user registry key Return Value: Return TRUE on success, and FALSE else --*/ { HKEY hKey = NULL; DWORD dwLastError; dwLastError = RegCreateKeyExA(hKeyUser, szRunOnceRegistryPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL); if ( dwLastError == ERROR_SUCCESS ) { dwLastError = RegSetValueExA(hKey, "Printing Migration", 0, REG_SZ, pszNetPrnEntry, ( strlen(pszNetPrnEntry) + 1 ) * sizeof(CHAR)); #ifdef VERBOSE if ( dwLastError == ERROR_SUCCESS ) DebugMsg("Wrote %s to %s", pszNetPrnEntry, szRunOnceRegistryPath); #endif } if ( hKey ) RegCloseKey(hKey); if ( dwLastError ) { SetLastError(dwLastError); return FALSE; } return TRUE; } VOID DecrementRunOnceCount( IN DWORD dwDiff, IN BOOL bResetRunOnceForUser ) /*++ Routine Description: Called after once network printer upgrade is called when user logged in, so we can decrement the retry count When ref count reaches 0 we then we can delete the files Arguments: dwDiff : Value by which ref count should be decremented bResetRunOnceForUser : We need to set RunOnce key again for the user Return Value: None --*/ { HKEY hKey; DWORD dwSize, dwCount, dwType; CHAR szPath[MAX_PATH]; if ( ERROR_SUCCESS != RegOpenKeyExA(HKEY_LOCAL_MACHINE, szRunOnceCountPath, 0, KEY_ALL_ACCESS, &hKey) ) return; dwSize = sizeof(dwCount); if ( ERROR_SUCCESS == RegQueryValueExA(hKey, szRunOnceCount, 0, &dwType, (LPBYTE)&dwCount, &dwSize) ) { dwCount -= dwDiff; if ( dwCount ) { RegSetValueExA(hKey, szRunOnceCount, 0, REG_DWORD, (LPBYTE)&dwCount, dwSize); if ( bResetRunOnceForUser && (pszNetPrnEntry = GetRunOnceValueToSet()) ) { ProcessNetPrnUpgradeForUser(HKEY_CURRENT_USER); FreeMem(pszNetPrnEntry); pszNetPrnEntry = NULL; #ifdef VERBOSE DebugMsg("Processing network/shared printers failed. Will try next time user logs in."); #endif } } else { dwSize = sizeof(szPath)/sizeof(szPath[0]); RegDeleteValueA(hKey, szRunOnceCount); if ( GetFileNameInSpoolDir(szPath, dwSize, szMigDll) ) DeleteFileA(szPath); if ( GetFileNameInSpoolDir(szPath, dwSize, szNetprnFile) ) DeleteFileA(szPath); DebugMsg("Giving up on setting network/shared printers"); } } RegCloseKey(hKey); } BOOL AddNetworkPrinter( IN LPPRINTER_INFO_2A pPrinterInfo2 ) /*++ Routine Description: This is called to add a windows 9x network printer. We will first try to make a conenction and if that fails we will add a masq. printer Arguments: pPrinterInfo2 : Pointer to printer info 2 of the printer Return Value: TRUE on success, FALSE else --*/ { BOOL bRet = FALSE; LPSTR pszName, psz; HANDLE hPrinter = NULL; pszName = pPrinterInfo2->pPortName; if ( !OpenPrinterA(pszName, &hPrinter, NULL) ) { if ( psz = ErrorMsg() ) { DebugMsg("OpenPrinter failed for %s. %s", pszName, psz); FreeMem(psz); psz = NULL; } goto Done; } // // Try to make a printer connection. If that fails with some error // other than unknown driver create a masq printer // if ( AddPrinterConnectionA(pszName) ) { if ( pPrinterInfo2->Attributes & PRINTER_ATTRIBUTE_DEFAULT ) SetDefaultPrinterA(pszName); bRet = TRUE; goto Done; } if ( GetLastError() == ERROR_UNKNOWN_PRINTER_DRIVER ) { if ( psz = ErrorMsg() ) { DebugMsg("AddPrinterConnection failed for %s. %s", pszName, psz); FreeMem(psz); psz = NULL; } goto Done; } ClosePrinter(hPrinter); // // Masc. printers should have port name, printer name both saying // \\server\share. Otherwise printui gets confused and does not refresh // server status correctly (this is since printui has to poll for masc. // printers) // // So we need to fixup PrinterInfo2 temporarily // psz = pPrinterInfo2->pPrinterName; pPrinterInfo2->pPrinterName = pPrinterInfo2->pPortName; if ( hPrinter = AddPrinterA(NULL, 2, (LPBYTE)pPrinterInfo2) ) { if ( pPrinterInfo2->Attributes & PRINTER_ATTRIBUTE_DEFAULT ) SetDefaultPrinterA(pPrinterInfo2->pPrinterName); pPrinterInfo2->pPrinterName = psz; bRet = TRUE; goto Done; } pPrinterInfo2->pPrinterName = psz; if ( psz = ErrorMsg() ) { DebugMsg("AddPrinterA failed for %s. %s", pszName, psz); FreeMem(psz); psz = NULL; } Done: if ( hPrinter ) ClosePrinter(hPrinter); return bRet; } BOOL SharePrinter( IN LPSTR pszPrinterName ) /*++ Routine Description: This is called to share a printer when the user logs in for the first time to NT. Printers can not be shared during GUI setup because we are not on the network yet. Arguments: pszPrinterName : Printer name Return Value: TRUE on success, FALSE else --*/ { BOOL bRet = FALSE; DWORD dwNeeded; HANDLE hPrinter = NULL; LPBYTE pBuf = NULL; LPSTR psz; PRINTER_DEFAULTS PrinterDflts = { NULL, NULL, PRINTER_ALL_ACCESS }; if ( !OpenPrinterA(pszPrinterName, &hPrinter, &PrinterDflts) ) { if ( psz = ErrorMsg() ) { DebugMsg("OpenPrinterA failed for %s. %s", pszPrinterName, psz); FreeMem(psz); psz = NULL; } goto Cleanup; } GetPrinterA(hPrinter, 2, NULL, 0, &dwNeeded); if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(pBuf = AllocMem(dwNeeded)) || !GetPrinterA(hPrinter, 2, pBuf, dwNeeded, &dwNeeded) ) { if ( psz = ErrorMsg() ) { DebugMsg("GetPrinterA failed for %s. %s", pszPrinterName, psz); FreeMem(psz); psz = NULL; } goto Cleanup; } ((LPPRINTER_INFO_2A)pBuf)->Attributes |= PRINTER_ATTRIBUTE_SHARED; bRet = SetPrinterA(hPrinter, 2, pBuf, 0); if ( !bRet && (psz = ErrorMsg()) ) { DebugMsg("OpenPrinterA failed for %s. %s", pszPrinterName, psz); FreeMem(psz); psz = NULL; } Cleanup: if ( hPrinter ) ClosePrinter(hPrinter); FreeMem(pBuf); return bRet; } VOID ProcessWin9xNetworkPrinters( ) /*++ Routine Description: This is called the first time the user logs in to create network printer connections/masq printers. Reads the Win9x printing configuration we stored in the text file so that printing components can be upgraded Arguments: ppPrinterNode : Gives the list of netowrk printers on Win9x Return Value: TRUE on succesfully reading the config information, FALSE else --*/ { BOOL bFail = FALSE, bSuccess = TRUE; HANDLE hFile = INVALID_HANDLE_VALUE; CHAR c, szFile[MAX_PATH], szLine[2*MAX_PATH]; DWORD dwSize, dwLen; PRINTER_INFO_2A PrinterInfo2; #ifdef VERBOSE DebugMsg("ProcessWin9xNetworkPrinters called"); #endif // // If file is not found quit // dwSize = sizeof(szFile)/sizeof(szFile[0]); if ( !GetFileNameInSpoolDir(szFile, dwSize, szNetprnFile) ) { DebugMsg("ProcessWin9xNetworkPrinters: GetFileNameInSpoolDir failed\n"); goto Cleanup; } hFile = CreateFileA(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if ( hFile == INVALID_HANDLE_VALUE ) { DebugMsg("ProcessWin9xNetworkPrinters: CreateFile failed with %d for %s", GetLastError(), szLine); goto Cleanup; } // // Read the printer info // if ( My_fgets(szLine, dwSize, hFile) == NULL || strncmp(szLine, "[Printers]", strlen("[Printers]")) ) goto Cleanup; do { c = (CHAR) My_fgetc(hFile); if ( c == EOF || c == '\n' ) break; // Normal exit if ( c != 'S' || !My_ungetc(hFile) ) goto Cleanup; ZeroMemory(&PrinterInfo2, sizeof(PrinterInfo2)); ReadPrinterInfo2(hFile, &PrinterInfo2, &bFail); if ( bFail ) goto Cleanup; // // If this was a network printer on Win9x it needs to be added as a // connection or as a masc printer // if ( PrinterInfo2.Attributes & PRINTER_ATTRIBUTE_NETWORK ) { if ( !AddNetworkPrinter(&PrinterInfo2) && bSuccess ) bSuccess = FALSE; } else if ( PrinterInfo2.Attributes & PRINTER_ATTRIBUTE_SHARED ) { if ( !SharePrinter(PrinterInfo2.pPrinterName) && bSuccess ) bSuccess = FALSE; } } while ( !bFail ); Cleanup: if ( hFile != INVALID_HANDLE_VALUE ) CloseHandle(hFile); if ( bSuccess && !bFail ) DecrementRunOnceCount(MIN_NETWORK_PRN_RETRIES, FALSE); else DecrementRunOnceCount(1, TRUE); }