Leaked source code of windows server 2003
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.

934 lines
33 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation
  3. Module Name:
  4. msmgen.cpp
  5. Abstract:
  6. Main Function calls for msm generation
  7. Author:
  8. Xiaoyu Wu(xiaoyuw) 01-Aug-2001
  9. --*/
  10. // NTRAID#NTBUG9 - 589817 - 2002/03/26 - xiaoyuw:
  11. // (1) the operation option, "mangroup" should get rid of the limitation about one manifest one msm,
  12. // (2) the implementation of "manlist", adding more than one assembly into one msm, should be in the pattern like
  13. // a. CreateMsmFromManifest(FirstManifestFileName);
  14. // b. for (i =1; i < n; i++)
  15. // AddingManifestIntoExistingMsm(ithManifestFileName);
  16. // (3) adding tracing info:
  17. // - to make msmgen.exe to be a better tool, TRACE_INFO should be added in a couple of places. using default logfile to record tracing info.
  18. // - adding tracing calls into macros such as IFFAILED_EXIT
  19. // (4) there is no parameter-checking in function definition.
  20. // (5) rename variable to make more sense:
  21. // for example:
  22. // m_sbManifestFileName ==> m_sbManifestFileNameNoPath
  23. // m_sbCatalogFileName ==> m_sbCatalogFileNameNoPath
  24. // (6)get rid of IFFALSE__MARKERROR_EXIT, which is not used, and not a good-defined macros.
  25. // <#define IFFALSE__MARKERROR_EXIT(x) if (!(x)) { hr = E_FAIL; goto Exit; }
  26. // >#define IFFALSE__MARKERROR_EXIT(x) if (!(x)) { hr = HRESULT_FROM_WIN32(::GetLastError()); goto Exit; }
  27. #include "msmgen.h"
  28. #include "util.h"
  29. #include "objbase.h"
  30. #include "initguid.h"
  31. #include "coguid.h"
  32. #include <string.h>
  33. #ifdef MSMGEN_TEST
  34. #define _WIN32_MSM 150
  35. #include "mergemod.h"
  36. #endif
  37. extern "C" { void (__cdecl * _aexit_rtn)(int); }
  38. #define DEFAULT_MODULE_IDENTIFIER_PREFIX L"Module0"
  39. #define MSMGEN_FROM_SINGLE_ASSEMBLY_TO_SINGLE_MERGE_MODULE 0
  40. #define MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_SINGLE_MERGE_MODULE 1
  41. #define MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_MULTIPLE_MERGE_MODULE 2
  42. #define MANLIST_COLUMN_DEFAULT_VALUE_ASSEMBLY_COMPONENT_ID 0
  43. #define MANLIST_COLUMN_DEFAULT_VALUE_MODULE_ID 1
  44. #define MANLIST_COLUMN_DEFAULT_VALUE_MSM_TEMPLATE_FILE 2
  45. #define MANLIST_COLUMN_DEFAULT_VALUE_MSM_OUTPUT_FILE 3
  46. static const PCWSTR ManListColumnDefaultValueInManifestList[] = {
  47. L"new",
  48. L"new",
  49. L"default",
  50. L"default"
  51. };
  52. ASSEMBLY_INFO curAsmInfo;
  53. MSM_INFO g_MsmInfo;
  54. //
  55. // function declaration
  56. //
  57. DECLARE_FUNCTION(_file);
  58. DECLARE_FUNCTION(_assemblyIdentity);
  59. DECLARE_FUNCTION(_comClass);
  60. DECLARE_FUNCTION(_typelib);
  61. //DECLARE_FUNCTION(_interface);
  62. //DECLARE_FUNCTION(_windowClass);
  63. static MSM_DOMNODE_WORKER s_msm_worker[]={
  64. DEFINE_ATTRIBUTE_MSM_INTERESTED(file),
  65. DEFINE_ATTRIBUTE_MSM_INTERESTED(assemblyIdentity),
  66. DEFINE_ATTRIBUTE_MSM_INTERESTED(comClass),
  67. DEFINE_ATTRIBUTE_MSM_INTERESTED(typelib)
  68. //DEFINE_ATTRIBUTE_MSM_INTERESTED(interface);
  69. //DEFINE_ATTRIBUTE_MSM_INTERESTED(windowClass);
  70. };
  71. //
  72. // Function:
  73. // -op : must has one of three ops:
  74. // -op new : CREATE_ALWAYS
  75. // generate a new msm based on a manifest
  76. // -op add : CREATE_OPEN_EXIST
  77. // (1) make more than one assembly in one msm, sharing the same moduleID
  78. // (2) when insert into some table, for example, Directory Table, no dup entries in the table before
  79. // (3) for some table, if the value of TableIdentifier has existed, it is an ERROR
  80. // (4) for add, if there is a cabinet in the msm already, we have to extract files from it and regenerate a
  81. // cabinet with the files of the added assembly. There are two ways to do this:
  82. // i. extract files from old cabinet before generate the new msm, that is, in PrepareMsmgeneration
  83. // ii. delay it until the new cabinet is tried to insert into _Stream table, that is, at the end of
  84. // msmgen.
  85. // the code would use (1) because if there is a MergeModule.cab in _Stream table, the work have to be done
  86. // anyway. And (1) would save the work to merge the cabinet. The files would be extracted into a temporary
  87. // directory and be deleted after be added to the new cabinet;
  88. //
  89. // -op regen : CREATE_OPEN_EXIST
  90. // (1)generate msm for an assembly using the existing msm file
  91. // (2) the only thing reusable are moduleID and ComponentID. If they are not specified on the command line,
  92. // they would be fetched from the msm file.
  93. // (3) we do not want to reuse any tables because almost all tables except Directory Table are generated
  94. // based on manifest. If reuse them, it loose the meaning of "regeneration"
  95. // (4) so what we do is to fetch componentID and moduleID if not present on the command line and then replace
  96. // the msm file with our msmgen template file
  97. //
  98. // -compid: {ComponentGUID}, for -op new, -op add, if it is not present, generate a new one
  99. // for -op regen, if it present, change the value in the component table, otherwise, keep the old one
  100. //
  101. // -msm msmfilename:
  102. // if this option is not present, use manifestFileName.msm and store in the same directory
  103. // else if the msmfilename is a fully pathname, use it
  104. // else if the msmfilename is a relative pathname, reolve it and get a fully pathname
  105. // -msmid MergeModuleGuid:
  106. // for -add and -regen, it is ignored if present,
  107. // for -new, if this is not present, call CoCreateGuid to generate a new one
  108. //
  109. // -manlist file.txt
  110. // a file with multiple manifest filename line by line
  111. //
  112. // -manfile fully-qaulified manifest filename: REQUIRED
  113. // fully qualified pathname of a manifest filename which msm is generated from
  114. //
  115. void PrintUsage(WCHAR * exe)
  116. {
  117. fprintf(stderr, "Usage: %S <options> full-pathname-of-manifest\n",exe);
  118. fprintf(stderr, "Generate .msm for an assembly\n");
  119. fprintf(stderr, "Options :\n");
  120. fprintf(stderr, "[-op (new|regen|add)]\n");
  121. fprintf(stderr, "\t\t:: new generate a new msm from a assembly\n");
  122. fprintf(stderr, "\t\t:: regen generate a msm from a assembly based on an old msm generated for this assembly\n");
  123. fprintf(stderr, "\t\t:: add use the old msm for the content of the assembly\n");
  124. fprintf(stderr, "\t\t:: DefaultValue : new\n\n");
  125. fprintf(stderr, "[-msm msmFileName]\n");
  126. fprintf(stderr, "\t\t:: output msm filename\n");
  127. fprintf(stderr, "\t\t:: DefaultValue : use the basename of manifest file with .msm as the ext\n\n");
  128. fprintf(stderr, "[-compid ComponentGuid]\n");
  129. fprintf(stderr, "\t\t:: Component ID for this Assembly\n");
  130. fprintf(stderr, "\t\t:: DefaultValue : call GUIDGen to get a new GUID\n\n");
  131. fprintf(stderr, "[-msmid MergeModuleGuid]\n");
  132. fprintf(stderr, "\t\t:: module ID for this msm \n");
  133. fprintf(stderr, "\t\t:: DefaultValue : call GUIDGen to get a new GUID\n\n");
  134. fprintf(stderr, "(-manfile | -manlist | -mangroup) filename\n");
  135. fprintf(stderr, "\t\t a manifest filename or a text file which contains manifest-filename line by line\n");
  136. fprintf(stderr, "\t\t About -manList:\n");
  137. fprintf(stderr, "\t\t (1)the format of manlist file would be:\n");
  138. fprintf(stderr, "\t\t\t Columns: ManifestFileName | ComponentID |\n");
  139. fprintf(stderr, "\t\t\t | ManifestFileName | ComponentID \n");
  140. fprintf(stderr, "\t\t (2)if you want to reuse the ModuleID or ComponentID, you have to copy the ID from msm into this text file,");
  141. fprintf(stderr, "\t\t\t otherwise, put \"DefaultValue\" to the column and the program would generate new GUID would be generated if you want to reuse the ModuleID or ComponentID, you have to copy the ID from msm into this text file,");
  142. fprintf(stderr, "\t\t (3)the list in the manifest listfile must be unique!!!");
  143. fprintf(stderr, "\t\t About -mangroup:\n");
  144. fprintf(stderr, "\t\t (1)the format of manGroup file would be:\n");
  145. fprintf(stderr, "\t\t\t Columns: ManifestFileName | ComponentID | MergeModuleID |OutputMsmFileName| TemplateFileName |\n");
  146. fprintf(stderr, "\t\t\t | ManifestFileName | ComponentID \n");
  147. fprintf(stderr, "\t\t (2)if you want to reuse the ModuleID or ComponentID, you have to copy the ID from msm into this text file,");
  148. fprintf(stderr, "\t\t\t otherwise, put \"DefaultValue\" to the column and the program would generate new GUID would be generated if you want to reuse the ModuleID or ComponentID, you have to copy the ID from msm into this text file,");
  149. fprintf(stderr, "\t\t (3)the list in the manifest listfile must be unique!!!");
  150. return;
  151. }
  152. VOID InitCurrentAssemblyInfo()
  153. {
  154. curAsmInfo.m_sbAssemblyPath.Left(0);
  155. curAsmInfo.m_sbManifestFileName.Left(0);
  156. curAsmInfo.m_sbCatalogFileName.Left(0);
  157. curAsmInfo.m_sbLangID.Left(0);
  158. curAsmInfo.m_sbComponentID.Left(0);
  159. curAsmInfo.m_sbComponentIdentifier.Left(0);
  160. curAsmInfo.m_fComponentTableSet = FALSE;
  161. curAsmInfo.m_CchAssemblyPath = 0;
  162. curAsmInfo.m_CchManifestFileName = 0;
  163. curAsmInfo.m_CchCatalogFileName = 0;
  164. }
  165. VOID InitializeMsmInfo()
  166. {
  167. g_MsmInfo.m_guidModuleID = GUID_NULL;
  168. g_MsmInfo.m_sbModuleGuidStr.Left(0);
  169. g_MsmInfo.m_hfci = NULL;
  170. g_MsmInfo.m_sbMsmFileName.Left(0);
  171. g_MsmInfo.m_hdb = NULL;
  172. g_MsmInfo.m_sbModuleIdentifier.Left(0);
  173. g_MsmInfo.m_sLanguageID = 0;
  174. g_MsmInfo.m_enumGenMode = MSMGEN_OPR_NEW;
  175. g_MsmInfo.m_sbCabinet.Left(0);
  176. //g_MsmInfo.m_sbMsmTemplateFile.Left(0);
  177. }
  178. //
  179. // parse the command option
  180. //
  181. HRESULT ValidateMsmgenParameters(wchar_t * exe, wchar_t ** Options, SIZE_T CchOptions,
  182. PWSTR * ppszManifestFileName, PWSTR * ppszManifestListFile,
  183. DWORD & dwManFlag)
  184. {
  185. HRESULT hr = S_OK;
  186. DWORD i = 0;
  187. PWSTR pszMsmFileName = NULL;
  188. PWSTR pszManifestFileName = NULL;
  189. PWSTR pszManifestListFile = NULL;
  190. ASSERT_NTC(ppszManifestFileName != NULL);
  191. ASSERT_NTC(ppszManifestListFile != NULL);
  192. *ppszManifestFileName = NULL;
  193. *ppszManifestListFile = NULL;
  194. dwManFlag = MSMGEN_FROM_SINGLE_ASSEMBLY_TO_SINGLE_MERGE_MODULE;
  195. while ( i < CchOptions)
  196. {
  197. if (Options[i][0] != L'-') // must begin with "-"
  198. goto invalid_param;
  199. if (_wcsicmp(Options[i], L"-op") == 0 )
  200. {
  201. if (_wcsicmp(Options[i+1], L"new") == 0 )
  202. {
  203. g_MsmInfo.m_enumGenMode = MSMGEN_OPR_NEW;
  204. }
  205. else if (_wcsicmp(Options[i+1], L"add") == 0 )
  206. {
  207. g_MsmInfo.m_enumGenMode = MSMGEN_OPR_ADD;
  208. }
  209. else if (_wcsicmp(Options[i+1], L"regen") == 0 )
  210. {
  211. g_MsmInfo.m_enumGenMode = MSMGEN_OPR_REGEN;
  212. }
  213. else
  214. goto invalid_param;
  215. }
  216. else if (_wcsicmp(Options[i], L"-msm") == 0 )
  217. {
  218. if (g_MsmInfo.m_sbMsmFileName.IsEmpty())
  219. {
  220. IFFALSE_EXIT(g_MsmInfo.m_sbMsmFileName.Win32Assign(Options[i+1], wcslen(Options[i+1])));
  221. }
  222. else
  223. goto invalid_param;
  224. }
  225. else if (_wcsicmp(Options[i], L"-template") == 0 )
  226. {
  227. if (g_MsmInfo.m_sbMsmTemplateFile.IsEmpty())
  228. {
  229. IFFALSE_EXIT(g_MsmInfo.m_sbMsmTemplateFile.Win32Assign(Options[i+1], wcslen(Options[i+1])));
  230. }
  231. else
  232. goto invalid_param;
  233. }
  234. else if (_wcsicmp(Options[i], L"-compid") == 0 )
  235. {
  236. // just want to reuse this buffer, bad design
  237. //
  238. // and HRESULT PrepareDatabase() depends on it too
  239. IFFALSE_EXIT(curAsmInfo.m_sbComponentID.Win32Assign(Options[i+1], wcslen(Options[i+1])));
  240. }
  241. else if (_wcsicmp(Options[i], L"-msmid") == 0 )
  242. {
  243. IFFAILED_EXIT(CLSIDFromString((LPOLESTR)(Options[i+1]), &g_MsmInfo.m_guidModuleID));
  244. }
  245. else if (_wcsicmp(Options[i], L"-manlist") == 0 )
  246. {
  247. if (pszManifestListFile == NULL)
  248. {
  249. pszManifestListFile = Options[i+1];
  250. dwManFlag = MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_SINGLE_MERGE_MODULE;
  251. }
  252. else
  253. goto invalid_param;
  254. }
  255. else if (_wcsicmp(Options[i], L"-mangroup") == 0 )
  256. {
  257. if (pszManifestListFile == NULL)
  258. {
  259. pszManifestListFile = Options[i+1];
  260. dwManFlag = MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_MULTIPLE_MERGE_MODULE;
  261. }
  262. else
  263. goto invalid_param;
  264. }
  265. else if (_wcsicmp(Options[i], L"-manfile") == 0 )
  266. {
  267. if (pszManifestFileName == NULL)
  268. {
  269. pszManifestFileName = Options[i+1];
  270. }
  271. else
  272. goto invalid_param;
  273. }
  274. else
  275. goto invalid_param;
  276. i++; // skip the option
  277. i++; // skip the value of the option
  278. }
  279. //
  280. // validate two-mode source manifest-files
  281. //
  282. if (((pszManifestFileName == NULL) && (pszManifestListFile == NULL)) || (pszManifestFileName && pszManifestListFile))
  283. {
  284. fprintf(stderr, "user has to provide a manifest filename or a text file with a list of manifest!\n\n");
  285. goto invalid_param;
  286. }
  287. if (pszManifestListFile != NULL)
  288. {
  289. if ( curAsmInfo.m_sbComponentID.GetCchAsDWORD() != 0)
  290. {
  291. fprintf(stderr, "the listfile may has more than one manifest, ComponentID can not be specified in this case!\n\n");
  292. goto invalid_param;
  293. }
  294. }
  295. goto Exit;
  296. invalid_param:
  297. PrintUsage(exe);
  298. hr = E_INVALIDARG;
  299. Exit:
  300. *ppszManifestFileName = pszManifestFileName;
  301. *ppszManifestListFile = pszManifestListFile;
  302. return hr;
  303. }
  304. HRESULT LoadManifestToDOMDocument(IXMLDOMDocument *pDoc)
  305. {
  306. VARIANT vURL;
  307. VARIANT_BOOL vb;
  308. HRESULT hr = S_OK;
  309. BSTR bstr = NULL;
  310. CurrentAssemblyReset;
  311. IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32Append(curAsmInfo.m_sbManifestFileName));
  312. bstr = ::SysAllocString(curAsmInfo.m_sbAssemblyPath);
  313. IFFAILED_EXIT(pDoc->put_async(VARIANT_FALSE));
  314. // Load xml document from the given URL or file path
  315. VariantInit(&vURL);
  316. vURL.vt = VT_BSTR;
  317. V_BSTR(&vURL) = bstr;
  318. IFFAILED_EXIT(pDoc->load(vURL, &vb));
  319. Exit:
  320. ::SysFreeString(bstr);
  321. return hr;
  322. }
  323. HRESULT PopulateDOMNodeForMsm(IXMLDOMNode * node)
  324. {
  325. HRESULT hr = S_OK;
  326. BSTR nodeName = NULL;
  327. DOMNodeType nodetype;
  328. IFFAILED_EXIT(node->get_nodeType(&nodetype));
  329. if(nodetype == NODE_ELEMENT)
  330. {
  331. IFFAILED_EXIT(node->get_nodeName(&nodeName));
  332. for ( DWORD i = 0 ; i < NUMBER_OF(s_msm_worker); i++)
  333. {
  334. if (wcscmp(s_msm_worker[i].pwszNodeName, nodeName) == 0)
  335. {
  336. IFFAILED_EXIT(s_msm_worker[i].pfn(node));
  337. break;
  338. }
  339. }
  340. }
  341. Exit:
  342. ::SysFreeString(nodeName);
  343. return hr;
  344. }
  345. HRESULT WalkDomTree(IXMLDOMNode * node)
  346. {
  347. HRESULT hr = S_OK;
  348. IXMLDOMNode* pChild = NULL, *pNext = NULL;
  349. IFFAILED_EXIT(PopulateDOMNodeForMsm(node));
  350. node->get_firstChild(&pChild);
  351. while (pChild)
  352. {
  353. IFFAILED_EXIT(WalkDomTree(pChild));
  354. pChild->get_nextSibling(&pNext);
  355. pChild->Release();
  356. pChild = NULL;
  357. pChild = pNext;
  358. pNext = NULL;
  359. }
  360. Exit:
  361. if (pChild)
  362. pChild->Release();
  363. if(pNext)
  364. pNext->Release();
  365. return hr;
  366. }
  367. void CleanupMsm()
  368. {
  369. if ( g_MsmInfo.m_hfci != NULL)
  370. {
  371. FCIDestroy(g_MsmInfo.m_hfci);
  372. g_MsmInfo.m_hfci = NULL;
  373. }
  374. if ( g_MsmInfo.m_hdb!= NULL){
  375. MsiDatabaseCommit(g_MsmInfo.m_hdb);
  376. MsiCloseHandle(g_MsmInfo.m_hdb);
  377. g_MsmInfo.m_hdb = NULL;
  378. }
  379. return;
  380. }
  381. HRESULT EndMsmGeneration()
  382. {
  383. HRESULT hr = S_OK;
  384. IFFAILED_EXIT(CloseCabinet());
  385. IFFAILED_EXIT(InsertCabinetIntoMsm());
  386. CleanupMsm();
  387. Exit:
  388. return hr;
  389. }
  390. HRESULT SetCurrentAssemblyInfo(DWORD dwFlags, PCWSTR pszManifestFileName)
  391. {
  392. WCHAR tmp[MAX_PATH];
  393. UINT iRet;
  394. PWSTR p = NULL;
  395. HRESULT hr = S_OK;
  396. if (dwFlags == MSMGEN_FROM_SINGLE_ASSEMBLY_TO_SINGLE_MERGE_MODULE)
  397. {
  398. //
  399. // componentID is provided from command options
  400. //
  401. }else
  402. {
  403. //
  404. // component ID from a txt file
  405. //
  406. InitCurrentAssemblyInfo();
  407. }
  408. iRet = GetFullPathNameW(pszManifestFileName, NUMBER_OF(tmp), tmp, NULL);
  409. if ((iRet == 0 ) || (iRet > NUMBER_OF(tmp)))
  410. {
  411. SET_HRERR_AND_EXIT(::GetLastError());
  412. }
  413. if (::GetFileAttributesW(tmp) == DWORD (-1))
  414. SETFAIL_AND_EXIT;
  415. IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32Assign(tmp, wcslen(tmp)));
  416. IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32GetLastPathElement(curAsmInfo.m_sbManifestFileName));
  417. IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32RemoveLastPathElement());
  418. IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32EnsureTrailingPathSeparator()); // Path with a trailing slash is always ready to use
  419. curAsmInfo.m_CchAssemblyPath = curAsmInfo.m_sbAssemblyPath.GetCchAsDWORD();
  420. curAsmInfo.m_CchManifestFileName = curAsmInfo.m_sbManifestFileName.GetCchAsDWORD();
  421. IFFALSE_EXIT(curAsmInfo.m_sbCatalogFileName.Win32Assign(curAsmInfo.m_sbManifestFileName));
  422. IFFALSE_EXIT(curAsmInfo.m_sbCatalogFileName.Win32ChangePathExtension(CATALOG_FILE_EXT, NUMBER_OF(CATALOG_FILE_EXT) -1, eAddIfNoExtension));
  423. IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32Append(curAsmInfo.m_sbCatalogFileName));
  424. if (::GetFileAttributesW(curAsmInfo.m_sbAssemblyPath) == DWORD (-1))
  425. SETFAIL_AND_EXIT;
  426. curAsmInfo.m_CchCatalogFileName = curAsmInfo.m_sbCatalogFileName.GetCchAsDWORD();
  427. //
  428. // reset
  429. //
  430. curAsmInfo.m_sbAssemblyPath.Left(curAsmInfo.m_CchAssemblyPath);
  431. curAsmInfo.m_sbManifestFileName.Left(curAsmInfo.m_CchManifestFileName);
  432. Exit:
  433. return hr;
  434. }
  435. HRESULT Msmgen_SingleAssemblyToMsm(DWORD dwFlags, PCWSTR pszManifestFileName)
  436. {
  437. HRESULT hr = S_OK;
  438. IXMLDOMDocument *pDoc = NULL;
  439. IXMLDOMNode *pNode = NULL;
  440. IFFAILED_EXIT(SetCurrentAssemblyInfo(dwFlags, pszManifestFileName));
  441. IFFAILED_EXIT(CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&pDoc));
  442. IFFAILED_EXIT(LoadManifestToDOMDocument(pDoc));
  443. IFFAILED_EXIT(pDoc->QueryInterface(IID_IXMLDOMNode,(void**)&pNode));
  444. IFFAILED_EXIT(WalkDomTree(pNode));
  445. IFFAILED_EXIT(CheckComponentTable());
  446. Exit:
  447. SAFE_RELEASE_COMPOINTER(pDoc);
  448. SAFE_RELEASE_COMPOINTER(pNode);
  449. return hr;
  450. }
  451. const static WCHAR wchLineDividers[] = { L'\r', L'\n', 0xFEFF, 0 };
  452. const static CHAR chLineDividers[] = { '\r', '\n', 0 };
  453. const static WCHAR wchLineItemDividers[] = {L' ', L','};
  454. const static CHAR chLineItemDividers[] = {' ', ','};
  455. #define MSM_ITEM_IN_LINE 1
  456. #define MSM_LINE_IN_FILE 2
  457. static inline bool IsCharacterNulOrInSet(DWORD dwFlags, BOOL fUnicodeFile, PVOID pCursor, ULONGLONG ullCursorPos)
  458. {
  459. ASSERT_NTC((dwFlags == MSM_ITEM_IN_LINE) || (dwFlags == MSM_LINE_IN_FILE));
  460. bool fRet = FALSE;
  461. if ( dwFlags == MSM_ITEM_IN_LINE) {
  462. if (fUnicodeFile)
  463. {
  464. WCHAR ch = (reinterpret_cast<PWSTR>(pCursor))[ullCursorPos];
  465. fRet = (ch == 0 || wcschr(wchLineItemDividers, ch) != NULL);
  466. }
  467. else
  468. {
  469. CHAR ch = (reinterpret_cast<PSTR>(pCursor))[ullCursorPos];
  470. fRet = (ch == 0 || strchr(chLineItemDividers, ch) != NULL);
  471. }
  472. }
  473. else if (dwFlags == MSM_LINE_IN_FILE)
  474. {
  475. if (fUnicodeFile)
  476. {
  477. WCHAR ch = (reinterpret_cast<PWSTR>(pCursor))[ullCursorPos];
  478. fRet = (ch == 0 || wcschr(wchLineDividers, ch) != NULL);
  479. }
  480. else
  481. {
  482. CHAR ch = (reinterpret_cast<PSTR>(pCursor))[ullCursorPos];
  483. fRet = (ch == 0 || strchr(chLineDividers, ch) != NULL);
  484. }
  485. }
  486. return fRet;
  487. }
  488. #define SKIP_BREAKERS(_pStart, _Size, _curPos, _flags) do {while ((_curPos < _Size) && IsCharacterNulOrInSet(_flags, fUnicodeFile, _pStart, _curPos)) _curPos++ ; } while(0)
  489. #define SKIP_LINE_BREAKERS(_pStart, _Size, _curPos) SKIP_BREAKERS(_pStart, _Size, _curPos, MSM_LINE_IN_FILE)
  490. #define SKIP_ITEM_BREAKERS(_pStart, _Size, _curPos) SKIP_BREAKERS(_pStart, _Size, _curPos, MSM_ITEM_IN_LINE)
  491. #define FIND_NEXT_BREAKER(_pStart, _Size, _curPos, _flags) do { while ((_curPos < _Size) && !IsCharacterNulOrInSet(_flags, fUnicodeFile, _pStart, _curPos)) _curPos++; } while(0)
  492. #define FIND_NEXT_LINE_BREAKERS(_pStart, _Size, _curPos) FIND_NEXT_BREAKER(_pStart, _Size, _curPos, MSM_LINE_IN_FILE)
  493. #define FIND_NEXT_ITEM_BREAKERS(_pStart, _Size, _curPos) FIND_NEXT_BREAKER(_pStart, _Size, _curPos, MSM_ITEM_IN_LINE)
  494. #define ENSURE_NOT_END(_curPos, _totalSize) do {if (_curPos > _totalSize) { SET_HRERR_AND_EXIT(ERROR_BAD_FORMAT); goto Exit;} } while(0)
  495. #define SetPointerToCurrentPostion(_pStart, _curPos, _x) do { if (fUnicodeFile) { _x = (PWSTR)_pStart + _curPos;} else { _x = (PSTR)_pStart + _curPos;} } while(0)
  496. HRESULT ParseManifestInfo(BOOL fUnicodeFile, PVOID pszLineStart, DWORD dwLineSize, DWORD dwFlags, CStringBuffer & manifestfile)
  497. {
  498. #define GetLineItemBorder(_pItemStart, _pItemEnd) do { \
  499. SKIP_ITEM_BREAKERS(pszLineStart, dwLineSize, dwCurPos); \
  500. ENSURE_NOT_END(dwCurPos, dwLineSize); \
  501. \
  502. SetPointerToCurrentPostion(pszLineStart, dwCurPos, _pItemStart); \
  503. \
  504. FIND_NEXT_ITEM_BREAKERS(pszLineStart, dwLineSize, dwCurPos);\
  505. ENSURE_NOT_END(dwCurPos, dwLineSize);\
  506. \
  507. SetPointerToCurrentPostion(pszLineStart ,dwCurPos - 1, _pItemEnd); \
  508. } while(0)
  509. #define GetUnicodeString(__pStart, __pEnd, __buf) \
  510. do { \
  511. if (fUnicodeFile) { \
  512. IFFALSE_EXIT(buf.Win32Assign((PWSTR)__pStart, (PWSTR)__pEnd - (PWSTR)__pStart + 1)); \
  513. } \
  514. else { \
  515. WCHAR tmp[MAX_PATH]; \
  516. if (MultiByteToWideChar(CP_ACP, 0, (PSTR)__pStart, (int)((PSTR)__pEnd - (PSTR)__pStart) + 1, tmp, NUMBER_OF(tmp)) == 0) { \
  517. SET_HRERR_AND_EXIT(::GetLastError()); \
  518. } \
  519. tmp[(PSTR)__pEnd - (PSTR)__pStart + 1] = L'\0'; \
  520. IFFALSE_EXIT(__buf.Win32Assign(tmp, wcslen(tmp))); \
  521. } \
  522. }while(0)
  523. HRESULT hr = S_OK;
  524. DWORD dwCurPos = 0;
  525. PVOID pszManifestNameStart, pszManifestNameEnd;
  526. PVOID pszAssemblyComponentIDStart, pszAssemblyComponentIDEnd;
  527. CStringBuffer buf;
  528. //
  529. // fetch Assembly manifest-filename and componentID
  530. //
  531. GetLineItemBorder(pszManifestNameStart, pszManifestNameEnd);
  532. GetLineItemBorder(pszAssemblyComponentIDStart, pszAssemblyComponentIDEnd);
  533. if (dwFlags == MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_MULTIPLE_MERGE_MODULE)
  534. {
  535. //
  536. // the format must be
  537. // ManifestFileName | ComponentID | MergeModuleID | OutputMsmFile | MsmTemplateFile
  538. // so, we need fetch MergeModuleID, MsmTemplateFile, and OutputMsmFile from the manList file
  539. //
  540. // initialize the msmInfo for current generation
  541. //
  542. InitializeMsmInfo();
  543. //
  544. // fetch MoudleID, templateFilename and output filename
  545. //
  546. PVOID pcwszModuleIDStart, pcwszModuleIDEnd;
  547. PVOID pcwszMsmOutputFilenameStart, pcwszMsmOutputFilenameEnd;
  548. GetLineItemBorder(pcwszModuleIDStart, pcwszModuleIDEnd);
  549. GetLineItemBorder(pcwszMsmOutputFilenameStart, pcwszMsmOutputFilenameEnd);
  550. GetUnicodeString(pcwszModuleIDStart, pcwszModuleIDEnd, buf);
  551. if (_wcsicmp(buf, ManListColumnDefaultValueInManifestList[MANLIST_COLUMN_DEFAULT_VALUE_MODULE_ID]) != 0)
  552. {
  553. IFFAILED_EXIT(CLSIDFromString((LPOLESTR)(PCWSTR)buf, &g_MsmInfo.m_guidModuleID));
  554. }
  555. GetUnicodeString(pcwszMsmOutputFilenameStart, pcwszMsmOutputFilenameEnd, buf);
  556. if (_wcsicmp(buf, ManListColumnDefaultValueInManifestList[MANLIST_COLUMN_DEFAULT_VALUE_MSM_OUTPUT_FILE]) != 0)
  557. {
  558. IFFALSE_EXIT(g_MsmInfo.m_sbMsmFileName.Win32Assign(buf));
  559. }
  560. }
  561. //
  562. // get ComponentID and manifest filename
  563. //
  564. GetUnicodeString(pszAssemblyComponentIDStart, pszAssemblyComponentIDEnd, buf);
  565. if (_wcsicmp(buf, ManListColumnDefaultValueInManifestList[MANLIST_COLUMN_DEFAULT_VALUE_ASSEMBLY_COMPONENT_ID]) != 0)
  566. {
  567. IFFALSE_EXIT(curAsmInfo.m_sbComponentID.Win32Assign(buf));
  568. }
  569. GetUnicodeString(pszManifestNameStart, pszManifestNameEnd, manifestfile);
  570. // if the output msm file is more than one, we need do preparation work everytime before msm generation
  571. // prepare for current msm generation
  572. //
  573. if (dwFlags == MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_MULTIPLE_MERGE_MODULE)
  574. {
  575. // set all variables ready
  576. IFFAILED_EXIT(PrepareMsmOutputFiles(manifestfile));
  577. }
  578. Exit:
  579. return hr;
  580. }
  581. HRESULT Msmgen_MultipleAssemblySources(DWORD dwFlags, PCWSTR pszManifestListFile)
  582. {
  583. HRESULT hr = S_OK;
  584. CStringBuffer buf;
  585. CFusionFile File;
  586. CFileMapping FileMapping;
  587. CMappedViewOfFile MappedViewOfFile;
  588. PVOID pCursor = NULL;
  589. BOOL fUnicodeFile = FALSE;
  590. ULONGLONG ullFileCharacters = 0, ullCursorPos = 0;
  591. PVOID pszLineStart, pszLineEnd;
  592. DWORD dwLineSize;
  593. if (!((dwFlags == MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_SINGLE_MERGE_MODULE) || (dwFlags == MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_MULTIPLE_MERGE_MODULE)))
  594. {
  595. SET_HRERR_AND_EXIT(ERROR_INVALID_PARAMETER);
  596. }
  597. IFFALSE_EXIT(File.Win32CreateFile(pszManifestListFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING));
  598. IFFALSE_EXIT(File.Win32GetSize(ullFileCharacters));
  599. IFFALSE_EXIT(FileMapping.Win32CreateFileMapping(File, PAGE_READONLY));
  600. IFFALSE_EXIT(MappedViewOfFile.Win32MapViewOfFile(FileMapping, FILE_MAP_READ));
  601. PBYTE pb = reinterpret_cast<BYTE*>(static_cast<VOID*>(MappedViewOfFile));
  602. if (((pb[0] == 0xFF) && (pb[1] == 0xFE)) || ((pb[1] == 0xFF) && (pb[0] == 0xFE)))
  603. {
  604. fUnicodeFile = TRUE;
  605. ASSERT_NTC(ullFileCharacters %2 == 0);
  606. ullFileCharacters = ullFileCharacters / sizeof(WCHAR);
  607. }
  608. pCursor = static_cast<VOID*>(MappedViewOfFile);
  609. for ( ullCursorPos = 0; ullCursorPos < ullFileCharacters; ++ullCursorPos )
  610. {
  611. CStringBuffer manifestfile;
  612. SKIP_LINE_BREAKERS(pCursor, ullFileCharacters, ullCursorPos);
  613. //
  614. // at end of the file, quit quietly
  615. //
  616. if (ullCursorPos == ullFileCharacters)
  617. {
  618. break;
  619. }
  620. SetPointerToCurrentPostion(pCursor, ullCursorPos, pszLineStart);
  621. FIND_NEXT_LINE_BREAKERS(pCursor, ullFileCharacters, ullCursorPos);
  622. //ENSURE_NOT_END(ullCursorPos, ullFileCharacters);
  623. SetPointerToCurrentPostion(pCursor, ullCursorPos - 1, pszLineEnd);
  624. dwLineSize = (DWORD)(((ULONGLONG)pszLineEnd - (ULONGLONG)pszLineStart) / (fUnicodeFile ? sizeof(WCHAR) : sizeof(CHAR))) + 1;
  625. IFFAILED_EXIT(ParseManifestInfo(fUnicodeFile, pszLineStart, dwLineSize, dwFlags, manifestfile));
  626. // generate the msm file
  627. IFFAILED_EXIT(Msmgen_SingleAssemblyToMsm(dwFlags, manifestfile));
  628. //
  629. // close the msm for each msm right now.
  630. //
  631. if (dwFlags == MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_MULTIPLE_MERGE_MODULE)
  632. {
  633. IFFAILED_EXIT(EndMsmGeneration());
  634. }
  635. }
  636. Exit:
  637. return hr;
  638. }
  639. VOID MsmgenInitialize()
  640. {
  641. InitializeMsmInfo();
  642. InitCurrentAssemblyInfo();
  643. return;
  644. }
  645. HRESULT GenerateMsm(wchar_t * exe, wchar_t ** Options, SIZE_T CchOptions)
  646. {
  647. HRESULT hr = S_OK;
  648. PWSTR pszManifestFileName= NULL;
  649. PWSTR pszManifestListFile =NULL;
  650. DWORD dwGenFlags;
  651. //
  652. // initalize the global structure
  653. //
  654. MsmgenInitialize();
  655. //
  656. // parse and validate parameters
  657. //
  658. IFFAILED_EXIT(ValidateMsmgenParameters(exe, Options, CchOptions, &pszManifestFileName, &pszManifestListFile, dwGenFlags));
  659. //
  660. // if the destination msm is one file, prepare it for msm generation here
  661. //
  662. if ((dwGenFlags == MSMGEN_FROM_SINGLE_ASSEMBLY_TO_SINGLE_MERGE_MODULE) || (dwGenFlags == MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_SINGLE_MERGE_MODULE))
  663. {
  664. IFFAILED_EXIT(PrepareMsmOutputFiles(pszManifestFileName != NULL? pszManifestFileName : pszManifestListFile));
  665. }
  666. if (pszManifestFileName != NULL)
  667. {
  668. IFFAILED_EXIT(Msmgen_SingleAssemblyToMsm(dwGenFlags, pszManifestFileName));
  669. }
  670. else
  671. {
  672. ASSERT_NTC(pszManifestListFile != NULL);
  673. ASSERT_NTC((dwGenFlags == MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_SINGLE_MERGE_MODULE) || (dwGenFlags == MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_MULTIPLE_MERGE_MODULE));
  674. IFFAILED_EXIT(Msmgen_MultipleAssemblySources(dwGenFlags, pszManifestListFile));
  675. }
  676. //
  677. // finish the construction of MSM : close the cabinet and files
  678. //
  679. if ((dwGenFlags == MSMGEN_FROM_SINGLE_ASSEMBLY_TO_SINGLE_MERGE_MODULE) || (dwGenFlags == MSMGEN_FROM_MULTIPLE_ASSEMBLY_TO_SINGLE_MERGE_MODULE))
  680. {
  681. IFFAILED_EXIT(EndMsmGeneration());
  682. }
  683. Exit:
  684. CleanupMsm(); // close it so that msi could use it
  685. return hr;
  686. }
  687. #ifdef MSMGEN_TEST
  688. //
  689. // both input filename are fully-qualified filename
  690. //
  691. HRESULT MergeMsmIntoMsi(PCWSTR msmFilename, PCWSTR msiFilename, PCWSTR FeatureIdentifier)
  692. {
  693. HRESULT hr = S_OK;
  694. IMsmMerge2 * pMsmMerge = NULL;
  695. CStringBuffer destPath;
  696. BSTR bstr = NULL;
  697. //get msi template
  698. IFFALSE_EXIT(CopyFileW(L"%ProgramFiles%\\msmgen\\templates\\msmgen.msi", msiFilename, FALSE));
  699. IFFALSE_EXIT(SetFileAttributesW(msiFilename, FILE_ATTRIBUTE_NORMAL));
  700. IFFAILED_EXIT(CoCreateInstance(CLSID_MsmMerge2, NULL, CLSCTX_INPROC_SERVER,
  701. IID_IMsmMerge2, (void**)&pMsmMerge));
  702. //
  703. // open msi for merge
  704. //
  705. bstr = ::SysAllocString(msiFilename);
  706. IFFAILED_EXIT(pMsmMerge->OpenDatabase(bstr));
  707. ::SysFreeString(bstr);
  708. //
  709. // open msm for merge
  710. //
  711. bstr = ::SysAllocString(msmFilename);
  712. IFFAILED_EXIT(pMsmMerge->OpenModule(bstr, g_MsmInfo.m_sLanguageID));
  713. ::SysFreeString(bstr);
  714. //
  715. // merge the module into the database
  716. //
  717. bstr = ::SysAllocString(FeatureIdentifier);
  718. IFFAILED_EXIT(pMsmMerge->Merge(bstr, NULL));
  719. ::SysFreeString(bstr);
  720. //
  721. // extract files into the destination directory
  722. //
  723. IFFALSE_EXIT(destPath.Win32Assign(msiFilename, wcslen(msiFilename)));
  724. IFFALSE_EXIT(destPath.Win32RemoveLastPathElement());
  725. IFFALSE_EXIT(destPath.Win32Append("\\", 1));
  726. bstr = ::SysAllocString(destPath);
  727. IFFAILED_EXIT(pMsmMerge->ExtractFilesEx(bstr, VARIANT_TRUE, NULL));
  728. ::SysFreeString(bstr);
  729. bstr = NULL;
  730. Exit:
  731. //
  732. // clean up
  733. //
  734. if (pMsmMerge)
  735. {
  736. pMsmMerge->CloseModule();
  737. pMsmMerge->CloseDatabase(SUCCEEDED(hr) ? VARIANT_TRUE : VARIANT_FALSE); // commit
  738. pMsmMerge->Release();
  739. }
  740. ::SysFreeString(bstr);
  741. return hr;
  742. }
  743. #endif
  744. extern "C" int __cdecl wmain(int argc, wchar_t** argv)
  745. {
  746. HRESULT hr = S_OK;
  747. // parse args.
  748. if ((argc <= 2) || ((argc % 2) != 1))
  749. {
  750. PrintUsage(argv[0]);
  751. hr = E_INVALIDARG;
  752. goto Exit;
  753. }
  754. if (!FusionpInitializeHeap(NULL)){
  755. hr = HRESULT_FROM_WIN32(::GetLastError());
  756. goto Exit;
  757. }
  758. ::CoInitialize(NULL);
  759. IFFAILED_EXIT(GenerateMsm(argv[0], argv + 1, argc - 1));
  760. #ifdef MSMGEN_TEST
  761. //
  762. // generate msi by setting input-parameter to be the fullpath filename of manifest
  763. //
  764. WCHAR msifilename[] = L"w:\\tmp\\1.msi";
  765. WCHAR FeatureIdentifier[] = L"SxsMsmgen"; // for test purpose only
  766. WCHAR msmfilename[] = L"w:\\tmp\\1.msm";
  767. curAsmInfo.m_sbAssemblyPath.Left(curAsmInfo.m_CchAssemblyPath);
  768. IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32Append(g_MsmInfo.m_sbMsmFileName));
  769. IFFALSE_EXIT(CopyFileW(curAsmInfo.m_sbAssemblyPath, msmfilename, FALSE));
  770. IFFAILED_EXIT(MergeMsmIntoMsi(msmfilename, msifilename, FeatureIdentifier));
  771. IF_NOTSUCCESS_SET_HRERR_EXIT(MsiInstallProduct(msifilename, NULL));
  772. #endif
  773. Exit:
  774. if (hr == S_OK)
  775. fprintf(stderr, "msm is generated successfully!");
  776. else
  777. fprintf(stderr, "msm is failed to be generated!");
  778. ::CoUninitialize();
  779. return (hr == S_OK) ? 0 : 1;
  780. }