Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

342 lines
13 KiB

  1. //=======================================================================
  2. //
  3. // Copyright (c) 1998-2001 Microsoft Corporation. All Rights Reserved.
  4. //
  5. // File: install.cpp
  6. //
  7. // Description:
  8. //
  9. // Functions called to install Active Setup/Windows Installer/and Custom Installer
  10. // type components.
  11. //
  12. //=======================================================================
  13. #include <windows.h>
  14. #include <iucommon.h>
  15. #include <tchar.h>
  16. #include <shlwapi.h>
  17. #include <install.h>
  18. #include <advpub.h>
  19. #include <memutil.h>
  20. #include <fileutil.h>
  21. #include <WaitUtil.h>
  22. #include <strsafe.h>
  23. #include <wusafefn.h>
  24. typedef struct
  25. {
  26. char szInfname[MAX_PATH];
  27. char szSection[MAX_PATH];
  28. char szDir[MAX_PATH];
  29. char szCab[MAX_PATH];
  30. DWORD dwFlags;
  31. DWORD dwType;
  32. } INF_ARGUMENTS;
  33. DWORD WINAPI LaunchInfCommand(void *p);
  34. HRESULT InstallSoftwareItem(LPTSTR pszInstallSourcePath, BOOL fRebootRequired, LONG lNumberOfCommands,
  35. PINSTALLCOMMANDINFO pCommandInfoArray, DWORD *pdwStatus)
  36. {
  37. LOG_Block("InstallASItem");
  38. HRESULT hr = S_OK;
  39. TCHAR szCommand[MAX_PATH+1]; // sourcepath + commandname from INSTALLCOMMANDINFO array
  40. TCHAR szCommandTemp[MAX_PATH+1]; // temporary buffer used to wrap the command line in quotes for CreateProcess
  41. TCHAR szDecompressFile[MAX_PATH];
  42. WIN32_FIND_DATA fd;
  43. HANDLE hProc;
  44. HANDLE hFind;
  45. BOOL fMore;
  46. LONG lCnt;
  47. DWORD dwRet;
  48. DWORD dwThreadId;
  49. USES_IU_CONVERSION;
  50. if ((NULL == pszInstallSourcePath) || (NULL == pCommandInfoArray) || (0 == lNumberOfCommands) || (NULL == pdwStatus))
  51. {
  52. hr = E_INVALIDARG;
  53. hr = LOG_ErrorMsg(hr);
  54. return hr;
  55. }
  56. *pdwStatus = ITEM_STATUS_FAILED; // default to failed in case no commands match known installers
  57. // Need to enumerate all .CAB files in the Install Source Path and Decompress them
  58. // before executing commands.
  59. hr = PathCchCombine(szCommand, ARRAYSIZE(szCommand), pszInstallSourcePath, _T("*.cab"));
  60. if (FAILED(hr)) {
  61. LOG_ErrorMsg(hr);
  62. return hr;
  63. }
  64. hFind = FindFirstFile(szCommand, &fd);
  65. fMore = (INVALID_HANDLE_VALUE != hFind);
  66. while (fMore)
  67. {
  68. if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
  69. {
  70. hr = PathCchCombine(szDecompressFile, ARRAYSIZE(szDecompressFile), pszInstallSourcePath, fd.cFileName);
  71. if (FAILED(hr))
  72. {
  73. LOG_ErrorMsg(hr);
  74. } else {
  75. if (!IUExtractFiles(szDecompressFile, pszInstallSourcePath))
  76. {
  77. LOG_Software(_T("Failed to Decompress file %s"), szDecompressFile);
  78. // ISSUE: do we abort this item?, or try the install anyway?
  79. }
  80. }
  81. }
  82. fMore = FindNextFile(hFind, &fd);
  83. }
  84. if (INVALID_HANDLE_VALUE != hFind)
  85. {
  86. FindClose(hFind);
  87. }
  88. for (lCnt = 0; lCnt < lNumberOfCommands; lCnt++)
  89. {
  90. // the szCommand variable is used to launch a process (msi or exe installer), but because of
  91. // oddities in the CreateProcess API's handling of the commandline parameter we need to wrap
  92. // the command line in quotes.
  93. hr = SafePathCombine(szCommandTemp, ARRAYSIZE(szCommandTemp), pszInstallSourcePath, pCommandInfoArray[lCnt].szCommandLine, SPC_FILE_MUST_EXIST);
  94. if (SUCCEEDED(hr))
  95. hr = StringCchPrintf(szCommand, ARRAYSIZE(szCommand), _T("\"%s\""), szCommandTemp);
  96. if (FAILED(hr))
  97. {
  98. LOG_ErrorMsg(hr);
  99. return hr;
  100. }
  101. switch (pCommandInfoArray[lCnt].iCommandType)
  102. {
  103. case COMMANDTYPE_INF:
  104. case COMMANDTYPE_ADVANCEDINF:
  105. {
  106. // Call INF Installer Passing Commandline and Parameters (if any)
  107. INF_ARGUMENTS infArgs;
  108. infArgs.dwType = pCommandInfoArray[lCnt].iCommandType;
  109. hr = StringCchCopyA(infArgs.szInfname, ARRAYSIZE(infArgs.szInfname),
  110. T2A(pCommandInfoArray[lCnt].szCommandLine));
  111. if (SUCCEEDED(hr))
  112. {
  113. hr = StringCchCopyA(infArgs.szSection, ARRAYSIZE(infArgs.szSection), ""); // use default
  114. }
  115. if (SUCCEEDED(hr))
  116. {
  117. hr = StringCchCopyA(infArgs.szDir, ARRAYSIZE(infArgs.szDir), T2A(pszInstallSourcePath));
  118. }
  119. if (SUCCEEDED(hr))
  120. {
  121. hr = StringCchCopyA(infArgs.szCab, ARRAYSIZE(infArgs.szCab), "");
  122. }
  123. if (FAILED(hr)) {
  124. LOG_ErrorMsg(hr);
  125. break;
  126. }
  127. infArgs.dwFlags = StrToInt(pCommandInfoArray[lCnt].szCommandParameters);
  128. LOG_Software(_T("Launching Inf - inf: %hs, section: %hs"), infArgs.szInfname, lstrlenA(infArgs.szSection) ? infArgs.szSection : "Default");
  129. hr = E_FAIL; // default INF result to E_FAIL.. if GetExitCodeThread fails so did the install
  130. hProc = CreateThread(NULL, 0, LaunchInfCommand, &infArgs, 0, &dwThreadId);
  131. if (NULL != hProc)
  132. {
  133. WaitAndPumpMessages(1, &hProc, QS_ALLINPUT);
  134. if (GetExitCodeThread(hProc, &dwRet))
  135. {
  136. hr = HRESULT_FROM_WIN32(dwRet);
  137. if (SUCCEEDED(hr) || hr == HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED))
  138. {
  139. *pdwStatus = ITEM_STATUS_SUCCESS;
  140. if (hr == HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED))
  141. {
  142. hr = S_OK;
  143. *pdwStatus = ITEM_STATUS_SUCCESS_REBOOT_REQUIRED;
  144. }
  145. }
  146. else
  147. {
  148. LOG_Error(_T("Inf Failed - return code %x"), hr);
  149. }
  150. }
  151. else
  152. {
  153. LOG_Software(_T("Failed to get Install Thread Exit Code"));
  154. }
  155. }
  156. else
  157. {
  158. hr = GetLastError();
  159. LOG_ErrorMsg(hr);
  160. }
  161. CloseHandle(hProc);
  162. break;
  163. }
  164. case COMMANDTYPE_EXE:
  165. {
  166. // Call EXE Installer Passing Commandline and Parameters (if any)
  167. STARTUPINFO startInfo;
  168. PROCESS_INFORMATION processInfo;
  169. ZeroMemory(&startInfo, sizeof(startInfo));
  170. startInfo.cb = sizeof(startInfo);
  171. startInfo.dwFlags |= STARTF_USESHOWWINDOW;
  172. startInfo.wShowWindow = SW_SHOWNORMAL;
  173. if (NULL != pCommandInfoArray[lCnt].szCommandParameters)
  174. {
  175. hr = StringCchCat(szCommand, ARRAYSIZE(szCommand), _T(" "));
  176. if (FAILED(hr))
  177. {
  178. LOG_ErrorMsg(hr);
  179. break;
  180. }
  181. hr = StringCchCat(szCommand, ARRAYSIZE(szCommand), pCommandInfoArray[lCnt].szCommandParameters);
  182. if (FAILED(hr))
  183. {
  184. LOG_ErrorMsg(hr);
  185. break;
  186. }
  187. }
  188. if (CreateProcess(NULL, szCommand, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, pszInstallSourcePath, &startInfo, &processInfo))
  189. {
  190. CloseHandle(processInfo.hThread);
  191. hr = S_OK; // Default EXE result to S_OK, if GetExitCodeProcess fails result was unknown assume success
  192. WaitAndPumpMessages(1, &processInfo.hProcess, QS_ALLINPUT);
  193. if (GetExitCodeProcess(processInfo.hProcess, &dwRet))
  194. {
  195. hr = HRESULT_FROM_WIN32(dwRet);
  196. if (SUCCEEDED(hr) || hr == HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED))
  197. {
  198. *pdwStatus = ITEM_STATUS_SUCCESS;
  199. if (hr == HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED))
  200. {
  201. hr = S_OK;
  202. *pdwStatus = ITEM_STATUS_SUCCESS_REBOOT_REQUIRED;
  203. }
  204. }
  205. else
  206. {
  207. LOG_Software(_T("EXE Install Failed - return code %x"), hr);
  208. }
  209. }
  210. else
  211. {
  212. LOG_Software(_T("Failed to get Install Process Exit Code"));
  213. }
  214. }
  215. else
  216. {
  217. hr = GetLastError();
  218. LOG_ErrorMsg(hr);
  219. }
  220. CloseHandle(processInfo.hProcess);
  221. break;
  222. }
  223. case COMMANDTYPE_MSI:
  224. {
  225. // Call MSI Installer Passing MSI Package and Parameters (if any)
  226. STARTUPINFO startInfo;
  227. PROCESS_INFORMATION processInfo;
  228. ZeroMemory(&startInfo, sizeof(startInfo));
  229. startInfo.cb = sizeof(startInfo);
  230. startInfo.dwFlags |= STARTF_USESHOWWINDOW;
  231. startInfo.wShowWindow = SW_SHOWNORMAL;
  232. // The MSI Installer is run a little differently than a normal EXE package. The command line in
  233. // CommandInfo Array will actually be the MSI package name. We are going to form a new set of
  234. // parameters to include the MSI package name and command line will be MSIEXEC.
  235. TCHAR szCommandLine[MAX_PATH];
  236. hr = StringCchPrintf( szCommandLine, ARRAYSIZE(szCommandLine),
  237. _T("msiexec.exe /i %s %s"),
  238. pCommandInfoArray[lCnt].szCommandLine,
  239. pCommandInfoArray[lCnt].szCommandParameters );
  240. if (FAILED(hr))
  241. {
  242. LOG_ErrorMsg(hr);
  243. break;
  244. }
  245. if (CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, pszInstallSourcePath, &startInfo, &processInfo))
  246. {
  247. CloseHandle(processInfo.hThread);
  248. hr = E_FAIL; // Default MSI install result to Error
  249. WaitAndPumpMessages(1, &processInfo.hProcess, QS_ALLINPUT);
  250. if (GetExitCodeProcess(processInfo.hProcess, &dwRet))
  251. {
  252. hr = HRESULT_FROM_WIN32(dwRet);
  253. if (SUCCEEDED(hr) || hr == HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED))
  254. {
  255. *pdwStatus = ITEM_STATUS_SUCCESS;
  256. if (hr == HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED))
  257. {
  258. hr = S_OK;
  259. *pdwStatus = ITEM_STATUS_SUCCESS_REBOOT_REQUIRED;
  260. }
  261. }
  262. else
  263. {
  264. LOG_Software(_T("MSI Install Failed - return code %x"), hr);
  265. }
  266. }
  267. else
  268. {
  269. LOG_Software(_T("Failed to get Install Process Exit Code"));
  270. }
  271. }
  272. else
  273. {
  274. hr = GetLastError();
  275. LOG_ErrorMsg(hr);
  276. }
  277. CloseHandle(processInfo.hProcess);
  278. break;
  279. }
  280. case COMMANDTYPE_CUSTOM:
  281. LOG_Software(_T("Custom Install Command Type Not Implemented Yet"));
  282. break;
  283. default:
  284. LOG_Software(_T("Unknown Command Type, No Install Action"));
  285. break;
  286. }
  287. }
  288. return hr;
  289. }
  290. DWORD WINAPI LaunchInfCommand(void *p)
  291. {
  292. HRESULT hr = S_OK;
  293. INF_ARGUMENTS *pinfArgs = (INF_ARGUMENTS *)p;
  294. if(pinfArgs->dwType == COMMANDTYPE_ADVANCEDINF)
  295. {
  296. CABINFO cabinfo;
  297. cabinfo.pszCab = pinfArgs->szCab;
  298. cabinfo.pszInf = pinfArgs->szInfname;
  299. cabinfo.pszSection = pinfArgs->szSection;
  300. // cabinfo.szSrcPath is a char[MAXPATH] in the CABINFO struct
  301. StringCchCopyA(cabinfo.szSrcPath, ARRAYSIZE(cabinfo.szSrcPath), pinfArgs->szDir);
  302. cabinfo.dwFlags = pinfArgs->dwFlags;
  303. hr = ExecuteCab(NULL, &cabinfo, 0);
  304. }
  305. else
  306. {
  307. hr = RunSetupCommand(NULL, pinfArgs->szInfname,
  308. lstrlenA(pinfArgs->szSection) ? pinfArgs->szSection : NULL,
  309. pinfArgs->szDir, NULL, NULL, pinfArgs->dwFlags, NULL );
  310. }
  311. return hr;
  312. }