#include "nt.h" #include "ntrtl.h" #include "nturtl.h" #include "windows.h" #include "stdio.h" #include "stdlib.h" #include "fcntl.h" #include "sxsexpress.h" #include "fci.h" #define NUMBER_OF(q) (sizeof(q)/sizeof(*q)) #ifndef PAGE_SIZE #define PAGE_SIZE (4*1024) #endif #define FILETYPE_CABINET (0x00000001) #define FILETYPE_DISKFILE (0x00000002) #define DEFAULT_RESERVE_SIZE (1024*1024*80) // By default, reserve 80mb //#define DEFAULT_RESERVE_SIZE (1024*8) // By default, reserve 80mb typedef struct _FILE_HANDLE { DWORD dwFileType; union { HANDLE hDiskFile; struct { PVOID pvFileBase; PVOID pvCursor; SIZE_T cbReserved; SIZE_T cbCommitted; SIZE_T cbWritten; } CabinetFile; }; } FILE_HANDLE; typedef struct _INSTALLED_FILE_NAME { LIST_ENTRY Links; PCWSTR pwszFileName; PCWSTR pwszSubDir; } INSTALLED_FILE; LIST_ENTRY InstalledFiles; typedef struct _FCI_CONTEXT_OBJECT { bool fWasError; DWORD dwLastError; FILE_HANDLE *CabinetFileHandle; PCSTR pszCabFileName; void ObtainErrorContext() { if (!fWasError) { fWasError = true; dwLastError = ::GetLastError(); } } } FCI_CONTEXT_OBJECT; BOOL CreateCabFromPath(PCWSTR pcwszBasePath, FILE_HANDLE *CreatedCabinet, PCWSTR pcwszInfName); void DoHelp(PCWSTR pcwszName) { static const WCHAR wchHelp[] = L"SxsExpress Self-Extracting Creator Tool\r\n" L"Copyright (c) 2001-2002 Microsoft Corporation, all \r\n" L"\r\n" L"Commandline:\r\n" L"\r\n" L" %ls [options] outputname\r\n" L"\r\n" L"Options:\r\n" L"-sourcedir : Sets the base path to package up\r\n" L"-sfxheader : Self-extractor core (exe or dll) file\r\n" L"-infname : Pass in a pre-built downlevel installer .INF\r\n" L" (If not specified, the tool will build an appropriate\r\n" L" .INF that installs everything to system32)\r\n" L"-overwrite : Forces the tool to output the built package even\r\n" L" if the file already exists\r\n"; wprintf(wchHelp, pcwszName); } PCWSTR GenerateInfFromTo( PCWSTR pcwszPackageDir ) { return NULL; } int __cdecl wmain(int argc, PCWSTR* argv) { PCWSTR pcwszHeaderName = NULL; PCWSTR pcwszPackageDir = NULL; PCWSTR pcwszInfName = NULL; PCWSTR pcwszOutputName = NULL; bool fAllocatedInfName = false; bool fOverwrite = false; FILE_HANDLE Created; HANDLE hResource = INVALID_HANDLE_VALUE;; int iResult = -1; BOOL fOk = FALSE; // // Determine parameters // for (int i = 1; i < argc; i++) { if ((*argv[i] == L'-') || (*argv[i] == L'/')) { PCWSTR psz = argv[i] + 1; if (lstrcmpiW(psz, L"sourcedir") == 0) { if (pcwszPackageDir != NULL) { DoHelp(argv[0]); wprintf(L"Already specified a packaging directory.\r\n"); goto Exit; } else { pcwszPackageDir = argv[++i]; } } else if (lstrcmpiW(psz, L"sfxheader") == 0) { if (pcwszHeaderName != NULL) { DoHelp(argv[0]); wprintf(L"Already specified the SFX header.\r\n"); goto Exit; } else { pcwszHeaderName = argv[++i]; } } else if (lstrcmpiW(psz, L"infname") == 0) { if (pcwszInfName != NULL) { DoHelp(argv[0]); wprintf(L"Already specified an INF name, only one at a time.\r\n"); goto Exit; } else { pcwszInfName = argv[++i]; } } else if (lstrcmpiW(psz, L"overwrite") == 0) { fOverwrite = true; } else { DoHelp(argv[0]); wprintf(L"\r\nUnknown option %ls\r\n", psz); goto Exit; } } else { if (pcwszOutputName != NULL) { DoHelp(argv[0]); wprintf(L"\r\nToo many output names, just one please.\r\n"); goto Exit; } else { pcwszOutputName = argv[i]; } } } if ((pcwszHeaderName == NULL) || (pcwszPackageDir == NULL) || (pcwszOutputName == NULL)) { DoHelp(argv[0]); wprintf(L"\r\nMust specify at least the sfx header file, source directory, and output\r\n"); goto Exit; } // // Ok, let's generate the cabinet file from the source directory. When this is done, // it'll be sitting in memory. We then have to copy the source header to the target // filename and do some work to update its resources // if (!CreateCabFromPath(pcwszPackageDir, &Created, pcwszInfName)) { wprintf(L"Error %ld creating cabinet from %ls\r\n", ::GetLastError(), pcwszPackageDir); goto Exit; } // // Copy the file over // if (!CopyFileW(pcwszHeaderName, pcwszOutputName, fOverwrite ? FALSE : TRUE)) { const DWORD dwError = ::GetLastError(); if (dwError == ERROR_FILE_EXISTS) { wprintf(L"Error, file %ls already exists. Move it or delete it, please.\r\n", pcwszOutputName); } else { wprintf(L"Error %ld copying SFX header %ls to output %ls\r\n", dwError, pcwszHeaderName, pcwszOutputName); } goto Exit; } // // The output contains the cabinet data above in its resources at a certain name. // Do this by updating the resource table now. // hResource = BeginUpdateResourceW(pcwszOutputName, FALSE); if ((hResource == NULL) || (hResource == INVALID_HANDLE_VALUE)) { wprintf(L"Unable to open %ls for resource updating, error 0x%08lx\n", pcwszOutputName, GetLastError()); goto Exit; } fOk = UpdateResourceW(hResource, SXSEXPRESS_RESOURCE_TYPE, SXSEXPRESS_RESOURCE_NAME, 0, Created.CabinetFile.pvFileBase, Created.CabinetFile.cbWritten); if (!EndUpdateResourceW(hResource, !fOk)) { wprintf(L"Failed updating resources, error code 0x%08lx\n", GetLastError()); goto Exit; } iResult = 0; Exit: if (fAllocatedInfName && pcwszInfName) HeapFree(GetProcessHeap(), 0, (PVOID)pcwszInfName); return iResult; } /* Our very own mini-cabarc code to create sxsexpress packages */ CHAR WorkingBufferOne[2048]; CHAR WorkingBufferTwo[2048]; FNFCIFILEPLACED(s_FilePlaced) { printf("Placed file %s (%d bytes)\n", pszFile, cbFile); return 1; } FNFCIALLOC(s_Alloc) { return HeapAlloc(GetProcessHeap(), 0, cb); } FNFCIFREE(s_Free) { HeapFree(GetProcessHeap(), 0, memory); } FNFCIOPEN(s_OpenFile) { FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv; FILE_HANDLE *hHandle = NULL; INT_PTR ipRetVal = -1; int iConversion = 0, iActual = 0; PWSTR pszConverted = NULL; hHandle = (FILE_HANDLE*)s_Alloc(sizeof(FILE_HANDLE)); if (!hHandle) { pContext->ObtainErrorContext(); *err = ::GetLastError(); goto Exit; } ZeroMemory(hHandle, sizeof(*hHandle)); // // All strings that get here are in UTF-8. Convert them back. // iConversion = MultiByteToWideChar(CP_UTF8, 0, pszFile, -1, NULL, 0); if (iConversion == 0) { pContext->ObtainErrorContext(); *err = ::GetLastError(); goto Exit; } pszConverted = (PWSTR)s_Alloc(sizeof(WCHAR) * (iConversion + 1)); if (NULL == pszConverted) { pContext->ObtainErrorContext(); *err = ::GetLastError(); goto Exit; } iActual = MultiByteToWideChar(CP_UTF8, 0, pszFile, -1, pszConverted, iConversion); if ((iActual == 0) || (iActual > iConversion)) { pContext->ObtainErrorContext(); *err = ::GetLastError(); goto Exit; } else { pszConverted[iConversion] = UNICODE_NULL; } if (oflag & _O_CREAT) { // // The actual cabinet file becomes a large block of committed bytes // if (lstrcmpiA(pContext->pszCabFileName, pszFile) == 0) { if (pContext->CabinetFileHandle) { pContext->fWasError = true; *err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER); } else { hHandle->dwFileType = FILETYPE_CABINET; hHandle->CabinetFile.pvFileBase = VirtualAlloc(NULL, DEFAULT_RESERVE_SIZE, MEM_RESERVE, PAGE_READWRITE); hHandle->CabinetFile.cbReserved = DEFAULT_RESERVE_SIZE; hHandle->CabinetFile.cbCommitted = 0; hHandle->CabinetFile.pvCursor = hHandle->CabinetFile.pvFileBase; hHandle->CabinetFile.cbWritten = 0; pContext->CabinetFileHandle = hHandle; } } else { // // Open a real file on disk to write - probably a temp file // hHandle->dwFileType = FILETYPE_DISKFILE; hHandle->hDiskFile = CreateFileW(pszConverted, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hHandle->hDiskFile == INVALID_HANDLE_VALUE) { pContext->ObtainErrorContext(); *err = ::GetLastError(); goto Exit; } } } // // Read a file from disk // else { hHandle->dwFileType = FILETYPE_DISKFILE; wprintf(L"Opening %ls\n", pszConverted); hHandle->hDiskFile = CreateFileW(pszConverted, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hHandle->hDiskFile == INVALID_HANDLE_VALUE) { pContext->ObtainErrorContext(); *err = ::GetLastError(); goto Exit; } } ipRetVal = (INT_PTR)hHandle; hHandle = NULL; Exit: if (pszConverted) s_Free(pszConverted); if (hHandle) { s_Free(hHandle); } return ipRetVal; } FNFCIREAD(s_ReadFile) { FILE_HANDLE *pFile = (FILE_HANDLE*)hf; FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv; DWORD cbReadSize = 0; UINT uiRetVal = -1; if (pFile->dwFileType == FILETYPE_DISKFILE) { if (!ReadFile(pFile->hDiskFile, memory, cb, &cbReadSize, NULL)) { pContext->ObtainErrorContext(); *err = ::GetLastError(); goto Exit; } } else if (pFile->dwFileType == FILETYPE_CABINET) { ULONG_PTR ulCursor = (ULONG_PTR)pFile->CabinetFile.pvCursor; const ULONG_PTR ulTotalSize = (ULONG_PTR)pFile->CabinetFile.pvFileBase + pFile->CabinetFile.cbWritten; if (ulCursor >= ulTotalSize) { cbReadSize = 0; } else { if (cb > (ulTotalSize - ulCursor)) { cb = (ulTotalSize - ulCursor); } memcpy(memory, pFile->CabinetFile.pvCursor, cb); pFile->CabinetFile.pvCursor = (PVOID)(ulCursor + cb); } } else { pContext->fWasError = true; *err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER); goto Exit; } uiRetVal = cbReadSize; Exit: return uiRetVal; } FNFCIWRITE(s_WriteFile) { FILE_HANDLE *pFile = (FILE_HANDLE*)hf; FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv; UINT uiRetVal = -1; // // Cabinet in-memory files are in large chunks. If the current cursor // plus the requested write size goes beyond the commit limit, then // reserve some more pages. // if (pFile->dwFileType == FILETYPE_CABINET) { ULONG_PTR ulpRequiredCommitted = cb; ulpRequiredCommitted += ((ULONG_PTR)pFile->CabinetFile.pvCursor - (ULONG_PTR)pFile->CabinetFile.pvFileBase); // // If the cursor is NULL, or the cursor plus the write request goes beyond // the committed range.. // if (ulpRequiredCommitted > pFile->CabinetFile.cbCommitted) { PVOID pvReserveState; // // Just attempt to commit all the pages required // pvReserveState = VirtualAlloc(pFile->CabinetFile.pvFileBase, ulpRequiredCommitted, MEM_COMMIT, PAGE_READWRITE); // // If this fails, reserve another blob and schmooze the bytes over from the // original allocation // if (pvReserveState == NULL) { const SIZE_T cbNewReserve = (ulpRequiredCommitted * 2) + (PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); // Reserve the new block pvReserveState = VirtualAlloc(NULL, cbNewReserve, MEM_RESERVE, PAGE_READWRITE); if (!pvReserveState) { pContext->ObtainErrorContext(); *err = ::GetLastError(); goto Exit; } // Commit what's necessary if (!VirtualAlloc(pvReserveState, ulpRequiredCommitted, MEM_COMMIT, PAGE_READWRITE)) { pContext->ObtainErrorContext(); *err = ::GetLastError(); goto Exit; } // Shoof data over memcpy(pvReserveState, pFile->CabinetFile.pvFileBase, pFile->CabinetFile.cbWritten); // Adjust file cursor, if necessary if (pFile->CabinetFile.pvCursor == NULL) pFile->CabinetFile.pvCursor = pvReserveState; else pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pvReserveState) + ((ULONG_PTR)pFile->CabinetFile.pvCursor - (ULONG_PTR)pFile->CabinetFile.pvFileBase)); // Free old reservation VirtualFree(pFile->CabinetFile.pvFileBase, 0, MEM_RELEASE); // Reset stuff pFile->CabinetFile.pvFileBase = pvReserveState; pFile->CabinetFile.cbReserved = cbNewReserve; pFile->CabinetFile.cbCommitted = ulpRequiredCommitted; } else { pFile->CabinetFile.cbCommitted = ulpRequiredCommitted; } } // // Okay, now just copy to the cursor and be done // memcpy(pFile->CabinetFile.pvCursor, memory, cb); pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pFile->CabinetFile.pvCursor) + cb); // If we wrote more than the high-water marker, reset it if (ulpRequiredCommitted > pFile->CabinetFile.cbWritten) { pFile->CabinetFile.cbWritten = ulpRequiredCommitted; } uiRetVal = cb; } else if (pFile->dwFileType == FILETYPE_DISKFILE) { DWORD cbWritten; if (!WriteFile(pFile->hDiskFile, memory, cb, &cbWritten, NULL)) { pContext->ObtainErrorContext(); *err = ::GetLastError(); goto Exit; } uiRetVal = cbWritten; } else { pContext->fWasError = true; *err = (int)(pContext->dwLastError = ERROR_INVALID_PARAMETER); uiRetVal = -1; goto Exit; } Exit: return uiRetVal; } FNFCICLOSE(s_Close) { FILE_HANDLE *pFile = (FILE_HANDLE*)hf; FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv; if (pFile->dwFileType == FILETYPE_DISKFILE) { CloseHandle(pFile->hDiskFile); } else if (pFile->dwFileType == FILETYPE_CABINET) { if (pFile == pContext->CabinetFileHandle) pFile = NULL; else VirtualFree(pFile->CabinetFile.pvFileBase, 0, MEM_RELEASE); } if (pFile) s_Free(pFile); return 0; } FNFCISEEK(s_Seek) { FILE_HANDLE *pFile = (FILE_HANDLE*)hf; long result; if (pFile->dwFileType == FILETYPE_CABINET) { switch (seektype) { case SEEK_SET: if (dist < 0) dist = 0; pFile->CabinetFile.pvCursor = (PVOID)(((ULONG_PTR)pFile->CabinetFile.pvFileBase) + dist); break; case SEEK_CUR: pFile->CabinetFile.pvCursor = (PVOID)(((INT_PTR)pFile->CabinetFile.pvCursor) + dist); break; case SEEK_END: pFile->CabinetFile.pvCursor = (PVOID)(((INT_PTR)pFile->CabinetFile.pvFileBase) + pFile->CabinetFile.cbWritten + dist); break; } result = (long)((LONG_PTR)pFile->CabinetFile.pvCursor - (LONG_PTR)pFile->CabinetFile.pvFileBase); } else if (pFile->dwFileType == FILETYPE_DISKFILE) { result = (long)SetFilePointer(pFile->hDiskFile, dist, NULL, seektype); } return result; } FNFCIDELETE(s_Delete) { FCI_CONTEXT_OBJECT *pContext = (FCI_CONTEXT_OBJECT*)pv; *err = 0; if (lstrcmpiA(pContext->pszCabFileName, pszFile) == 0) return 0; if (!DeleteFileA(pszFile)) { pContext->ObtainErrorContext(); *err = ::GetLastError(); return -1; } else { return 1; } } FNFCIGETTEMPFILE(s_TempFile) { WCHAR chTemp[MAX_PATH]; WCHAR chTempOutput[MAX_PATH]; int i; GetTempPathW(NUMBER_OF(chTemp), chTemp); GetTempFileNameW(chTemp, L"SXP", 0, chTempOutput); i = WideCharToMultiByte(CP_UTF8, 0, chTempOutput, -1, pszTempName, cbTempName, NULL, NULL); return ((i > 0) && (i < cbTempName)); } FNFCIGETNEXTCABINET(s_GetNextCabinet) { return FALSE; } // // All files are UTF-8 encoded // FNFCIGETOPENINFO(s_GetOpenInfo) { *pdate = *ptime = 0; *pattribs = _A_NAME_IS_UTF; return s_OpenFile(pszName, _O_BINARY|_O_RDONLY, _A_NAME_IS_UTF, err, pv); } FNFCISTATUS(s_StatusCallback) { switch (typeStatus) { case statusFile: case statusFolder: return 1; break; case statusCabinet: return cb2; break; } return -1; } BOOL CabPathWorkerCallback( HFCI hCabinet, PWSTR pwszWorkingTemp, SIZE_T cchTotalTemp, PCWSTR pwszBaseInCabinet, WIN32_FIND_DATAW *pFindData ) { HANDLE hFind = INVALID_HANDLE_VALUE; BOOL fResult = FALSE; SIZE_T ccOriginalLength = wcslen(pwszWorkingTemp); SIZE_T cchBasePathLength; if (pwszBaseInCabinet[0] == L'\\') pwszBaseInCabinet++; cchBasePathLength = wcslen(pwszBaseInCabinet); // // Ensure that the string is slash-terminated // if (pwszWorkingTemp[ccOriginalLength-1] != L'\\') { pwszWorkingTemp[ccOriginalLength++] = L'\\'; pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL; } wcscpy(pwszWorkingTemp + ccOriginalLength, L"*"); hFind = ::FindFirstFileW(pwszWorkingTemp, pFindData); if (hFind != NULL) do { pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL; if (pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { wcscat(pwszWorkingTemp, pFindData->cFileName); if ((wcscmp(pFindData->cFileName, L".") == 0) || (wcscmp(pFindData->cFileName, L"..") == 0)) continue; if (!CabPathWorkerCallback(hCabinet, pwszWorkingTemp, cchTotalTemp, pwszBaseInCabinet, pFindData)) goto Exit; } else { int i; INSTALLED_FILE *pFile = NULL; PWSTR pwszCursor = NULL; SIZE_T cchRequired; cchRequired = (cchBasePathLength + 1) + (wcslen(pFindData->cFileName) + 1); pFile = (INSTALLED_FILE*)s_Alloc(sizeof(INSTALLED_FILE) + (sizeof(WCHAR) * cchRequired)); if (pFile == NULL) goto Exit; pwszCursor = (PWSTR)(pFile + 1); wcscpy(pwszCursor, pwszBaseInCabinet); pFile->pwszSubDir = pwszCursor; pwszCursor[cchBasePathLength] = UNICODE_NULL; pwszCursor += cchBasePathLength + 1; wcscpy(pwszCursor, pFindData->cFileName); pFile->pwszFileName = pwszCursor; InsertHeadList(&InstalledFiles, &pFile->Links); wcscat(pwszWorkingTemp, pFindData->cFileName); i = WideCharToMultiByte( CP_UTF8, 0, pwszBaseInCabinet, -1, WorkingBufferTwo, sizeof(WorkingBufferTwo), NULL, NULL); if ((i == 0) || (i > sizeof(WorkingBufferTwo))) goto Exit; i = WideCharToMultiByte( CP_UTF8, 0, pwszWorkingTemp, -1, WorkingBufferOne, sizeof(WorkingBufferOne), NULL, NULL); if ((i == 0) || (i > sizeof(WorkingBufferOne))) goto Exit; if (!FCIAddFile( hCabinet, WorkingBufferOne, WorkingBufferTwo, FALSE, s_GetNextCabinet, s_StatusCallback, s_GetOpenInfo, tcompTYPE_LZX | tcompLZX_WINDOW_HI)) { goto Exit; } } } while (::FindNextFileW(hFind, pFindData)); if (::GetLastError() != ERROR_NO_MORE_FILES) goto Exit; fResult = TRUE; Exit: if (hFind != INVALID_HANDLE_VALUE) FindClose(hFind); pwszWorkingTemp[ccOriginalLength] = UNICODE_NULL; return fResult; } BOOL WriteFormatted( INT_PTR File, PVOID pvContext, PCWSTR fmt, ... ) { static WCHAR wchDump[1024]; va_list va; int iError; int iResult; va_start(va, fmt); iResult = _vsnwprintf(wchDump, NUMBER_OF(wchDump), fmt, va); va_end(va); if ((iResult >= 0) && (iResult < NUMBER_OF(wchDump))) wchDump[iResult] = UNICODE_NULL; else iResult = 0; return (s_WriteFile(File, wchDump, iResult * sizeof(WCHAR), &iError, pvContext) != -1); } BOOL CreateCabFromPath( PCWSTR pcwszBasePath, FILE_HANDLE *CreatedCabinet, PCWSTR pcwszInfName ) { CHAR chCabName[] = "::::"; ERF erf; FCI_CONTEXT_OBJECT Context = { false, 0, NULL, chCabName }; const ULONG CabMaxSize = 0x04000000; CCAB CabInfo = { CabMaxSize, CabMaxSize }; HFCI hCabinet = NULL; WIN32_FIND_DATAW FindData; BOOL fResult = FALSE; HANDLE hFindFile = INVALID_HANDLE_VALUE; static WCHAR wchBasePath[2048]; int i = 0; InitializeListHead(&InstalledFiles); lstrcpyA(CabInfo.szDisk, "SxsExpressCompressedCabinet"); lstrcpyA(CabInfo.szCab, "::"); lstrcpyA(CabInfo.szCabPath, "::"); wcscpy(wchBasePath, pcwszBasePath); // // Start off by creating a compression object // hCabinet = FCICreate( &erf, s_FilePlaced, s_Alloc, s_Free, s_OpenFile, s_ReadFile, s_WriteFile, s_Close, s_Seek, s_Delete, s_TempFile, &CabInfo, (void*)&Context); fResult = CabPathWorkerCallback(hCabinet, wchBasePath, NUMBER_OF(wchBasePath), wchBasePath + wcslen(wchBasePath), &FindData); if (!fResult) { printf("Error adding cabinet files, %d (code %d)\n", erf.erfOper, erf.erfType); goto Exit; } // // No name given? Cons one up from the files found // if (pcwszInfName == NULL) { CHAR chTempFile[MAX_PATH]; int iError; INT_PTR p; LIST_ENTRY *pLink; s_TempFile(chTempFile, NUMBER_OF(chTempFile), &Context); p = s_OpenFile(chTempFile, _O_CREAT, 0, &iError, &Context); WORD wBom = 0xfeff; // // This is a UCS-2 file. // s_WriteFile(p, &wBom, sizeof(wBom), &iError, &Context); // // Spit out the source and target paths // WriteFormatted(p, &Context, L"[FileEntries]\r\n"); pLink = InstalledFiles.Flink; while (pLink && (pLink != &InstalledFiles)) { INSTALLED_FILE *pFile = CONTAINING_RECORD(pLink, INSTALLED_FILE, Links); WriteFormatted(p, &Context, L"%ls\\%ls;\\%ls\r\n", pFile->pwszSubDir, pFile->pwszFileName, pFile->pwszFileName); pLink = pLink->Flink; } s_Close(p, &iError, &Context); strcpy(WorkingBufferOne, chTempFile); } else { i = WideCharToMultiByte(CP_UTF8, 0, pcwszInfName, -1, WorkingBufferOne, sizeof(WorkingBufferOne), NULL, NULL); if ((i == 0) || (i > sizeof(WorkingBufferOne))) { wprintf(L"Error converting INF file %ls name to UTF-8\r\n", pcwszInfName); goto Exit; } } fResult = FCIAddFile( hCabinet, WorkingBufferOne, INF_SPECIAL_NAME, FALSE, s_GetNextCabinet, s_StatusCallback, s_GetOpenInfo, tcompTYPE_LZX | tcompLZX_WINDOW_HI); FCIFlushFolder(hCabinet, s_GetNextCabinet, s_StatusCallback); FCIFlushCabinet(hCabinet, FALSE, s_GetNextCabinet, s_StatusCallback); fResult = TRUE; Exit: if (hCabinet != NULL) FCIDestroy(hCabinet); *CreatedCabinet = *Context.CabinetFileHandle; return fResult; }