/*++ Copyright (c) 2000 Microsoft Corporation Module Name: msmgen.cpp Abstract: Main Function calls for msm generation Author: Xiaoyu Wu(xiaoyuw) 01-Aug-2001 --*/ #include "msmgen.h" #include "util.h" #include "objbase.h" #include "initguid.h" #include "coguid.h" #include "msidefs.h" #include #define DEFAULT_MODULE_IDENTIFIER_PREFIX L"Module0" #define MSMGEN_FROM_MANFILE 0 #define MSMGEN_FROM_MANLIST 1 #define MSMGEN_FROM_MANGROUP 2 extern void MsmToUpper(CBaseStringBuffer &); // // global variables // CStringBuffer g_sbTemplateFile; ASSEMBLY_INFO curAsmInfo; MSM_INFO g_MsmInfo; // // function declaration // DECLARE_FUNCTION(_file); DECLARE_FUNCTION(_assemblyIdentity); DECLARE_FUNCTION(_comClass); DECLARE_FUNCTION(_typelib); static MSM_DOMNODE_WORKER s_msm_worker[]={ DEFINE_ATTRIBUTE_MSM_INTERESTED(file), DEFINE_ATTRIBUTE_MSM_INTERESTED(assemblyIdentity), DEFINE_ATTRIBUTE_MSM_INTERESTED(comClass), DEFINE_ATTRIBUTE_MSM_INTERESTED(typelib) }; void PrintUsage() { WCHAR filename[MAX_PATH]; PWSTR exe = NULL; if (GetModuleFileNameW(NULL, filename, NUMBER_OF(filename)) > 0) { exe = wcsrchr(filename, L'\\'); if (exe == NULL) { wcscpy(filename, L"msmgen exe "); exe = filename; }else exe ++; } else { wcscpy(filename, L"msmgen exe "); exe = filename; } fprintf(stderr, "Generate .msm for an assembly\n\n"); fprintf(stderr, "%S manfile manifest_file.manifest [-compid {guid}] [-msmid {guid}] [-msm file.msm] [-template templatefile.msm]\n", exe); fprintf(stderr, "%S manlist manifest_list.txt [-msmid {guid}] [-msm file.msm] [-template templatefile.msm]\n", exe); fprintf(stderr, "%S mangroup manifest_group.txt [-template templatefile.msm]\n", exe); exit(0); } VOID ZeroOutCurrentAssemblyInfo() { curAsmInfo.m_sbAssemblyPath.Left(0); curAsmInfo.m_sbManifestFileName.Left(0); curAsmInfo.m_sbCatalogFileName.Left(0); curAsmInfo.m_CchAssemblyPath = 0; curAsmInfo.m_CchManifestFileName = 0; curAsmInfo.m_CchCatalogFileName = 0; curAsmInfo.m_sbComponentID.Left(0); curAsmInfo.m_sbLangID.Left(0); curAsmInfo.m_sbComponentIdentifier.Left(0); curAsmInfo.m_fComponentTableSet = FALSE; } VOID ZeroOutMsmInfo() { g_MsmInfo.m_guidModuleID = GUID_NULL; g_MsmInfo.m_sbModuleGuidStr.Left(0); g_MsmInfo.m_hfci = NULL; g_MsmInfo.m_sbMsmFileName.Left(0); g_MsmInfo.m_hdb = NULL; g_MsmInfo.m_sbModuleIdentifier.Left(0); g_MsmInfo.m_sLanguageID = 0; g_MsmInfo.m_sbCabinet.Left(0); //g_MsmInfo.m_sbMsmTemplateFile.Left(0); } HRESULT ValidateCurrentMsmInfo() { HRESULT hr = S_OK; INTERNAL_ERROR_CHECK_NTC(g_MsmInfo.m_sbMsmFileName.Cch() != 0); INTERNAL_ERROR_CHECK_NTC(g_MsmInfo.m_sbMsmTemplateFile.Cch() != 0); INTERNAL_ERROR_CHECK_NTC(g_MsmInfo.m_guidModuleID != GUID_NULL); INTERNAL_ERROR_CHECK_NTC(g_MsmInfo.m_sbModuleGuidStr.Cch() != 0); hr = S_OK; Exit: return hr; } HRESULT LoadManifestToDOMDocument(IXMLDOMDocument *pDoc) { VARIANT vURL; VARIANT_BOOL vb; HRESULT hr = S_OK; BSTR bstr = NULL; CurrentAssemblyRealign; IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32Append(curAsmInfo.m_sbManifestFileName)); bstr = SysAllocString(curAsmInfo.m_sbAssemblyPath); IFFAILED_EXIT(pDoc->put_async(VARIANT_FALSE)); // Load xml document from the given URL or file path VariantInit(&vURL); vURL.vt = VT_BSTR; V_BSTR(&vURL) = bstr; IFFAILED_EXIT(pDoc->load(vURL, &vb)); Exit: SysFreeString(bstr); return hr; } HRESULT PopulateDOMNode(IXMLDOMNode * node) { HRESULT hr = S_OK; BSTR nodeName = NULL; DOMNodeType nodetype; IFFAILED_EXIT(node->get_nodeType(&nodetype)); if(nodetype == NODE_ELEMENT) { IFFAILED_EXIT(node->get_nodeName(&nodeName)); for ( DWORD i = 0 ; i < NUMBER_OF(s_msm_worker); i++) { if (wcscmp(s_msm_worker[i].pwszNodeName, nodeName) == 0) { IFFAILED_EXIT(s_msm_worker[i].pfn(node)); break; } } } Exit: SysFreeString(nodeName); return hr; } HRESULT WalkDomTree(IXMLDOMNode * node) { HRESULT hr = S_OK; IXMLDOMNode* pChild = NULL, *pNext = NULL; IFFAILED_EXIT(PopulateDOMNode(node)); node->get_firstChild(&pChild); while (pChild) { IFFAILED_EXIT(WalkDomTree(pChild)); pChild->get_nextSibling(&pNext); pChild->Release(); pChild = NULL; pChild = pNext; pNext = NULL; } Exit: if (pChild) pChild->Release(); if(pNext) pNext->Release(); return hr; } HRESULT PopulateAssemblyManifest() { HRESULT hr = S_OK; IXMLDOMDocument *pDoc = NULL; IXMLDOMNode *pNode = NULL; IFFAILED_EXIT(CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void**)&pDoc)); IFFAILED_EXIT(LoadManifestToDOMDocument(pDoc)); IFFAILED_EXIT(pDoc->QueryInterface(IID_IXMLDOMNode,(void**)&pNode)); IFFAILED_EXIT(WalkDomTree(pNode)); Exit: SAFE_RELEASE_COMPOINTER(pDoc); SAFE_RELEASE_COMPOINTER(pNode); return hr; } HRESULT PrepareMergeModuleOutputFileFromScratch() { HRESULT hr = S_OK; LPOLESTR tmpstr = NULL; PMSIHANDLE hSummaryInfo = NULL; CurrentAssemblyRealign; // make sure that msm, template and msm-guid has been set IFFAILED_EXIT(ValidateCurrentMsmInfo()); // // set cabinet output filename // IFFALSE_EXIT(g_MsmInfo.m_sbCabinet.Win32Assign(g_MsmInfo.m_sbMsmFileName)); IFFALSE_EXIT(g_MsmInfo.m_sbCabinet.Win32RemoveLastPathElement()); IFFALSE_EXIT(g_MsmInfo.m_sbCabinet.Win32EnsureTrailingPathSeparator()); IFFALSE_EXIT(g_MsmInfo.m_sbCabinet.Win32Append(MERGEMODULE_CABINET_FILENAME, NUMBER_OF(MERGEMODULE_CABINET_FILENAME) -1)); IFFAILED_EXIT(InitializeCabinetForWrite()); // open msm as a database ASSERT_NTC(g_MsmInfo.m_hdb == NULL); ASSERT_NTC(g_MsmInfo.m_sbMsmTemplateFile.IsEmpty() == FALSE); IFFALSE_EXIT(CopyFileW(g_MsmInfo.m_sbMsmTemplateFile, g_MsmInfo.m_sbMsmFileName, FALSE)); IFFALSE_EXIT(SetFileAttributesW(g_MsmInfo.m_sbMsmFileName, FILE_ATTRIBUTE_NORMAL)); IF_NOTSUCCESS_SET_HRERR_EXIT(MsiOpenDatabaseW(g_MsmInfo.m_sbMsmFileName, (LPCWSTR)(MSIDBOPEN_DIRECT), &g_MsmInfo.m_hdb)); IF_NOTSUCCESS_SET_HRERR_EXIT(MsiGetSummaryInformation(g_MsmInfo.m_hdb, NULL, 3, &hSummaryInfo)); INTERNAL_ERROR_CHECK_NTC(g_MsmInfo.m_guidModuleID != GUID_NULL); IFFAILED_EXIT(StringFromCLSID(g_MsmInfo.m_guidModuleID, &tmpstr)); IF_NOTSUCCESS_SET_HRERR_EXIT(MsiSummaryInfoSetProperty(hSummaryInfo, PID_REVNUMBER, VT_LPSTR, 0,0, tmpstr)); IF_NOTSUCCESS_SET_HRERR_EXIT(MsiSummaryInfoSetProperty(hSummaryInfo, PID_PAGECOUNT, VT_I4, 150, 0, 0)); // // write ModuleSiguature table using version // ASSERT_NTC(g_MsmInfo.m_sbModuleIdentifier.Cch() != 0); IFFAILED_EXIT(ExecuteInsertTableSQL(OPT_MODULESIGNATURE, NUMBER_OF_PARAM_TO_INSERT_TABLE_MODULESIGNATURE, MAKE_PCWSTR(g_MsmInfo.m_sbModuleIdentifier), MAKE_PCWSTR(L"1.0.0.0"))); Exit: MsiSummaryInfoPersist(hSummaryInfo); CoTaskMemFree(tmpstr); return hr; } #define MSMGEN_CREATE_MSM_ALWAYS 0x01 #define MSMGEN_ADD_ASSEMBLY_INTO_EXIST_MSM 0x02 HRESULT PutAssemblyIntoMergeModule(DWORD dwFlags) { HRESULT hr = S_OK; PARAMETER_CHECK_NTC((dwFlags == MSMGEN_CREATE_MSM_ALWAYS) || (dwFlags == MSMGEN_ADD_ASSEMBLY_INTO_EXIST_MSM)); ASSERT_NTC(curAsmInfo.m_sbAssemblyPath.Cch() != 0); ASSERT_NTC(curAsmInfo.m_sbManifestFileName.Cch() != 0); ASSERT_NTC(curAsmInfo.m_sbCatalogFileName.Cch() != 0); ASSERT_NTC(curAsmInfo.m_sbComponentID.Cch() != 0); ASSERT_NTC(g_MsmInfo.m_sbMsmFileName.Cch() != NULL); // validate global structure of msmInfo and asmInfo if (dwFlags == MSMGEN_CREATE_MSM_ALWAYS) { // delete mam if exist SetFileAttributesW(g_MsmInfo.m_sbMsmFileName, FILE_ATTRIBUTE_NORMAL); DeleteFileW(g_MsmInfo.m_sbMsmFileName); if (GetFileAttributesW(g_MsmInfo.m_sbMsmFileName) != (DWORD) (-1)) { ReportError("failed to create a new msm. The msm file, %S, could not be removed!", (PCWSTR)g_MsmInfo.m_sbMsmFileName); SETFAIL_AND_EXIT; } else if (::GetLastError() != ERROR_FILE_NOT_FOUND) { if (::GetLastError() == ERROR_PATH_NOT_FOUND) { CStringBuffer sb; IFFALSE_EXIT(sb.Win32Assign(g_MsmInfo.m_sbMsmFileName)); IFFALSE_EXIT(sb.Win32RemoveLastPathElement()); IFFALSE_EXIT(FusionpCreateDirectories(sb, sb.Cch())); goto MsmContinue; } ReportError("failed to create a new msm. GetFileAttribute for %S return error other than NOT_FOUND, win32 err code = %d\n", (PCWSTR)g_MsmInfo.m_sbMsmFileName, ::GetLastError()); SETFAIL_AND_EXIT; } MsmContinue: IFFAILED_EXIT(PrepareMergeModuleOutputFileFromScratch()); IFFAILED_EXIT(PopulateAssemblyManifest()); } else if (dwFlags == MSMGEN_ADD_ASSEMBLY_INTO_EXIST_MSM) { if (GetFileAttributesW(g_MsmInfo.m_sbMsmFileName) == (DWORD) (-1)) { ReportError("failed to add assembly into a msm. The msm file, %S, does not exist!", (PCWSTR)g_MsmInfo.m_sbMsmFileName); SETFAIL_AND_EXIT; } IFFAILED_EXIT(PopulateAssemblyManifest()); } Exit: return hr; } void Msmgen_TracingFunctionFailure(PWSTR x, PSTR func, DWORD line) { PWSTR p = x; PWSTR q; WCHAR Callee[256]; while ((p != NULL) && ((*p == L' ') || (*p == L'('))) p ++; q = p; if (p != NULL) q = wcschr(p , L'('); if (q != NULL) { wcsncpy(Callee, p, q - p); ReportError("%S called in %s at line %2d failed!", Callee, func, line); }else { ReportError("Function called in %s at line %2d failed!", func, line); } return; } // // make msi-specified guid string ready : uppercase and replace "-" with "_" // HRESULT GetMsiGUIDStrFromGUID(DWORD dwFlags, GUID & guid, CSmallStringBuffer & str) { HRESULT hr = S_OK; WCHAR tmpbuf[MAX_PATH]; LPOLESTR tmpstr = NULL; IFFAILED_EXIT(StringFromCLSID(guid, &tmpstr)); wcscpy(tmpbuf, tmpstr); for (DWORD i=0; i < wcslen(tmpbuf); i++) { if (tmpbuf[i] == L'-') tmpbuf[i] = L'_'; else tmpbuf[i]= towupper(tmpbuf[i]); } if (dwFlags & MSIGUIDSTR_WITH_PREPEND_DOT) { tmpbuf[0] = L'.'; IFFALSE_EXIT(str.Win32Assign(tmpbuf, wcslen(tmpbuf) - 1 )); // has prepend "." }else IFFALSE_EXIT(str.Win32Assign(tmpbuf + 1 , wcslen(tmpbuf) - 2 )); // get rid of "{" and "}" Exit: CoTaskMemFree(tmpstr); return hr; } HRESULT FillMsmForInitialize() { HRESULT hr = S_OK; WCHAR buf[MAX_PATH]; if (GetFileAttributesW(g_MsmInfo.m_sbMsmFileName) != DWORD(-1)) { SetFileAttributesW(g_MsmInfo.m_sbMsmFileName, FILE_ATTRIBUTE_NORMAL); DeleteFileW(g_MsmInfo.m_sbMsmFileName); if (GetFileAttributesW(g_MsmInfo.m_sbMsmFileName) != DWORD(-1)) { ReportError("the output %s already exist, please rename or delete it.\n"); hr = E_INVALIDARG; goto Exit; } } // // set template filename // if (g_MsmInfo.m_sbMsmTemplateFile.Cch() == 0) { if (FALSE == g_sbTemplateFile.IsEmpty()) { IFFALSE_EXIT(g_MsmInfo.m_sbMsmTemplateFile.Win32Assign(g_sbTemplateFile)); } else { // // get template file from current directory // DWORD dwRet; dwRet = GetModuleFileNameW(NULL, buf, NUMBER_OF(buf)); if ((dwRet == 0) || (dwRet >= NUMBER_OF(buf))) SET_HRERR_AND_EXIT(::GetLastError()); IFFALSE_EXIT(g_MsmInfo.m_sbMsmTemplateFile.Win32Assign(buf, wcslen(buf))); IFFALSE_EXIT(g_MsmInfo.m_sbMsmTemplateFile.Win32ChangePathExtension(L"msm", 3, eErrorIfNoExtension)); } } // make sure that the template file must exist! if (::GetFileAttributesW(g_MsmInfo.m_sbMsmTemplateFile) == (DWORD) -1) { fprintf(stderr, "the specified Msm TemplateFile %S does not exist!\n", g_MsmInfo.m_sbMsmTemplateFile); SET_HRERR_AND_EXIT(::GetLastError()); } // set msm ID if (g_MsmInfo.m_guidModuleID == GUID_NULL) { IFFAILED_EXIT(::CoCreateGuid(&g_MsmInfo.m_guidModuleID)); } IFFAILED_EXIT(GetMsiGUIDStrFromGUID(MSIGUIDSTR_WITH_PREPEND_DOT, g_MsmInfo.m_guidModuleID, g_MsmInfo.m_sbModuleGuidStr)); // unset until we get textual assembly identity IFFALSE_EXIT(g_MsmInfo.m_sbModuleIdentifier.Win32Assign(L"_", 1)); //make sure that ModuleID does not start with a "_" IFFALSE_EXIT(g_MsmInfo.m_sbModuleIdentifier.Win32Append(g_MsmInfo.m_sbModuleGuidStr + 1, g_MsmInfo.m_sbModuleGuidStr.Cch() -1)); hr= S_OK; Exit: return hr; } #define FILE_EXTENSION_MSM L"msm" #define FILE_EXTENSION_MSM_CCH 3 using namespace std; #include HRESULT InitializeMsmA(string & strManFile, string & strMsmFile, string & strMsmID, string & strTemplateFile) { HRESULT hr = S_OK; LPOLESTR tmpstr = NULL; PARAMETER_CHECK_NTC(!strManFile.empty()); ZeroOutMsmInfo(); // void function // // set output msm filename // if (strMsmFile.empty()) { IFFALSE_EXIT(g_MsmInfo.m_sbMsmFileName.Win32Assign(strManFile.c_str(), strManFile.length())); IFFALSE_EXIT(g_MsmInfo.m_sbMsmFileName.Win32ChangePathExtension(FILE_EXTENSION_MSM, FILE_EXTENSION_MSM_CCH, eErrorIfNoExtension)); } else { IFFALSE_EXIT(g_MsmInfo.m_sbMsmFileName.Win32Assign(strMsmFile.c_str(), strMsmFile.length())); } // // set template filename // if (!strTemplateFile.empty()) { IFFALSE_EXIT(g_MsmInfo.m_sbMsmTemplateFile.Win32Assign(strTemplateFile.c_str(), strTemplateFile.length())); } // set msm ID g_MsmInfo.m_guidModuleID = GUID_NULL; if (!strMsmID.empty()) { // // get ModuleID from msm and save it into the global structure // CStringBuffer sb; IFFALSE_EXIT(sb.Win32Assign(strMsmID.c_str(), strMsmID.length())); IFFAILED_EXIT(CLSIDFromString(LPOLESTR(PCWSTR(sb)), &g_MsmInfo.m_guidModuleID)); } IFFAILED_EXIT(FillMsmForInitialize()); hr = S_OK; Exit: CoTaskMemFree(tmpstr); return hr; } HRESULT InitializeMsmW(PCWSTR pszManFile, PCWSTR pszMsmFile, PCWSTR pszMsmID, PCWSTR pszTemplateFile) { HRESULT hr = S_OK; PMSIHANDLE hSummaryInfo = NULL;\ PARAMETER_CHECK_NTC(pszManFile != NULL); ZeroOutMsmInfo(); // void function // // set output msm filename // if (pszMsmFile == NULL) { IFFALSE_EXIT(g_MsmInfo.m_sbMsmFileName.Win32Assign(pszManFile, wcslen(pszManFile))); IFFALSE_EXIT(g_MsmInfo.m_sbMsmFileName.Win32ChangePathExtension(FILE_EXTENSION_MSM, FILE_EXTENSION_MSM_CCH, eErrorIfNoExtension)); } else { IFFALSE_EXIT(g_MsmInfo.m_sbMsmFileName.Win32Assign(pszMsmFile, wcslen(pszMsmFile))); } // // set template filename // if (pszTemplateFile != NULL) { IFFALSE_EXIT(g_MsmInfo.m_sbMsmTemplateFile.Win32Assign(pszTemplateFile, wcslen(pszTemplateFile))); } // set msm ID g_MsmInfo.m_guidModuleID = GUID_NULL; if (pszMsmID != NULL) { // // get ModuleID from msm and save it into the global structure // IFFAILED_EXIT(CLSIDFromString(LPOLESTR(pszMsmID), &g_MsmInfo.m_guidModuleID)); } IFFAILED_EXIT(FillMsmForInitialize()); hr = S_OK; Exit: return hr; } HRESULT GetInfoMsmgenCommandLineParameters(const DWORD dwGenFlag, wchar_t ** Options, DWORD NumOptions, PWSTR * pszComponentID, PWSTR * pszMsmFile, PWSTR * pszMsmID, PWSTR * pszTemplateFile) { HRESULT hr = S_OK; DWORD i = 0; PARAMETER_CHECK_NTC((dwGenFlag == MSMGEN_FROM_MANFILE) || (dwGenFlag == MSMGEN_FROM_MANLIST)); PARAMETER_CHECK_NTC(NumOptions % 2 == 0); *pszComponentID = NULL; *pszMsmFile = NULL; *pszMsmID = NULL; *pszTemplateFile = NULL; while ( i < NumOptions) { if (Options[i][0] != L'-') // must begin with "-" goto InvalidOption; if (_wcsicmp(Options[i], L"-msm") == 0 ) { if (*pszMsmFile != NULL) { ReportError("output msm is specified more than once\n"); goto InvalidOption; } else { *pszMsmFile = Options[i + 1]; } } else if (_wcsicmp(Options[i], L"-template") == 0 ) { if (*pszTemplateFile != NULL) { ReportError("TemplateFile is specified more than once\n"); goto InvalidOption; } else { *pszTemplateFile = Options[i + 1]; } } else if (_wcsicmp(Options[i], L"-compid") == 0 ) { if (dwGenFlag == MSMGEN_FROM_MANLIST) { ReportError("ComponentID should not be specified for manlist\n"); goto InvalidOption; } if (*pszComponentID != NULL) { ReportError("ComponentID is specified more than once\n"); goto InvalidOption; } else { *pszComponentID = Options[i + 1]; } } else if (_wcsicmp(Options[i], L"-msmid") == 0 ) { if (*pszMsmID != NULL) { ReportError("msmid is specified more than once\n"); goto InvalidOption; } else { *pszMsmID = Options[i + 1]; } } else goto InvalidOption; i++; // skip the option i++; // skip the value of the option } hr = S_OK; goto Exit; InvalidOption: PrintUsage(); hr = E_INVALIDARG; Exit: return hr; } HRESULT InitializeAsmInfoW(PCWSTR pszManifestFile, PCWSTR pszComponent) { HRESULT hr = S_OK; WCHAR tmp[MAX_PATH]; UINT iRet; LPOLESTR tmpstr = NULL; PWSTR p = NULL; PARAMETER_CHECK_NTC(pszManifestFile != NULL); // initialize the global structure of current assembly info ZeroOutCurrentAssemblyInfo(); // // set manifest, catalog and AssemblyPath, // // the manifest does not exist if (::GetFileAttributesW(pszManifestFile) == DWORD (-1)) { ReportError("the manifest file, %S, does not exist, try with manifest file's fullpath!\n", pszManifestFile); SETFAIL_AND_EXIT; } iRet = ::GetFullPathNameW(pszManifestFile, NUMBER_OF(tmp), tmp, NULL); if ((iRet == 0 ) || (iRet > NUMBER_OF(tmp))) { ReportError("Get the fullpath of manifest file, %S, failed with LastError=%d!\n", pszManifestFile, ::GetLastError()); SET_HRERR_AND_EXIT(::GetLastError()); } IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32Assign(tmp, wcslen(tmp))); IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32GetLastPathElement(curAsmInfo.m_sbManifestFileName)); IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32RemoveLastPathElement()); IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32EnsureTrailingPathSeparator()); // Path with a trailing slash is always ready to use curAsmInfo.m_CchAssemblyPath = curAsmInfo.m_sbAssemblyPath.GetCchAsDWORD(); curAsmInfo.m_CchManifestFileName = curAsmInfo.m_sbManifestFileName.GetCchAsDWORD(); IFFALSE_EXIT(curAsmInfo.m_sbCatalogFileName.Win32Assign(curAsmInfo.m_sbManifestFileName)); IFFALSE_EXIT(curAsmInfo.m_sbCatalogFileName.Win32ChangePathExtension(CATALOG_FILE_EXT, NUMBER_OF(CATALOG_FILE_EXT) -1, eAddIfNoExtension)); IFFALSE_EXIT(curAsmInfo.m_sbAssemblyPath.Win32Append(curAsmInfo.m_sbCatalogFileName)); if (::GetFileAttributesW(curAsmInfo.m_sbAssemblyPath) == DWORD (-1)) { ReportError("The catalog file, %S, does not exist!", (PCWSTR)curAsmInfo.m_sbAssemblyPath); SETFAIL_AND_EXIT; } curAsmInfo.m_CchCatalogFileName = curAsmInfo.m_sbCatalogFileName.GetCchAsDWORD(); // // reset // curAsmInfo.m_sbAssemblyPath.Left(curAsmInfo.m_CchAssemblyPath); curAsmInfo.m_sbManifestFileName.Left(curAsmInfo.m_CchManifestFileName); // // set componentID // if (pszComponent == NULL) { GUID tmpguid; IFFAILED_EXIT(::CoCreateGuid(&tmpguid)); IFFAILED_EXIT(StringFromCLSID(tmpguid, &tmpstr)); IFFALSE_EXIT(curAsmInfo.m_sbComponentID.Win32Assign(tmpstr, wcslen(tmpstr))); } else { IFFALSE_EXIT(curAsmInfo.m_sbComponentID.Win32Assign(pszComponent, wcslen(pszComponent))); } MsmToUpper(curAsmInfo.m_sbComponentID); Exit: CoTaskMemFree(tmpstr); return hr; } HRESULT InitializeAsmInfoA(const string & strManifestFile, const string & strComponent) { WCHAR wstrManifestFile[MAX_PATH]; WCHAR wstrComponentID[64]; HRESULT hr = S_OK; PARAMETER_CHECK_NTC(!strManifestFile.empty()); if (0 == MultiByteToWideChar(CP_ACP, 0, strManifestFile.c_str(), strManifestFile.length(), wstrManifestFile, NUMBER_OF(wstrManifestFile))) { ReportError("convert %s to wstr failed!", strManifestFile.c_str()); SETFAIL_AND_EXIT; } wstrManifestFile[strManifestFile.length()] = L'\0'; if (!strComponent.empty()) { if (0 == MultiByteToWideChar(CP_ACP, 0, strComponent.c_str(), strComponent.length(), wstrComponentID, NUMBER_OF(wstrComponentID))) { ReportError("convert %s to wstr failed!", strComponent.c_str()); SETFAIL_AND_EXIT; } wstrComponentID[strComponent.length()] = L'\0'; } IFFAILED_EXIT(InitializeAsmInfoW(wstrManifestFile, strComponent.empty() ? NULL : wstrComponentID)); Exit: return hr; } void CleanupMsm() { if ( g_MsmInfo.m_hfci != NULL) { FCIDestroy(g_MsmInfo.m_hfci); g_MsmInfo.m_hfci = NULL; } if ( g_MsmInfo.m_hdb!= NULL){ MsiDatabaseCommit(g_MsmInfo.m_hdb); MsiCloseHandle(g_MsmInfo.m_hdb); g_MsmInfo.m_hdb = NULL; } return; } HRESULT FinalizedMsm() { HRESULT hr = S_OK; IFFAILED_EXIT(CloseCabinet()); IFFAILED_EXIT(InsertCabinetIntoMsm()); Exit: DeleteFileW(g_MsmInfo.m_sbCabinet); CleanupMsm(); return hr; } HRESULT CreateMsmForSingleManifest(PCWSTR pszManifestFile, PCWSTR pszComponent) { HRESULT hr = S_OK; PARAMETER_CHECK_NTC(pszManifestFile != NULL); IFFAILED_EXIT(InitializeAsmInfoW(pszManifestFile, pszComponent)); IFFAILED_EXIT(PutAssemblyIntoMergeModule(MSMGEN_CREATE_MSM_ALWAYS)); IFFAILED_EXIT(FinalizedMsm()); Exit: return hr; } #include #if defined(_WIN64) #pragma warning(disable: 4244) #endif #include #if defined(_WIN64) #pragma warning(default: 4244) #endif void MsmTrimString(string & str) { if (!str.empty()) { string::size_type x = str.find_first_not_of(' '); if (x != 0) str.erase(0, x); if (!str.empty()) { x = str.find_last_not_of(' '); if (x != str.length() - 1) str.erase(x + 1, str.length() - 1 - x); } } return; } HRESULT CreateMsmForManifestList(PCWSTR pwszManListFile) { HRESULT hr = S_OK; char pszManListFile[MAX_PATH]; int iBufSize; string strline; string strManifestFilename; string strComponentID; DWORD numLine = 0; ifstream manlist; PARAMETER_CHECK_NTC(pwszManListFile != NULL); iBufSize = WideCharToMultiByte(CP_ACP, 0, pwszManListFile, wcslen(pwszManListFile), NULL, 0, NULL, NULL); if (iBufSize > NUMBER_OF(pszManListFile)) { ReportError("the manlist filename, %S, is longer than MAX_PATH\n", pwszManListFile); SETFAIL_AND_EXIT; } if (0 == WideCharToMultiByte(CP_ACP, 0, pwszManListFile, wcslen(pwszManListFile), pszManListFile, NUMBER_OF(pszManListFile), NULL, NULL)) { ReportError("the manlist filename, %S, failed to convert to ansi string\n", pwszManListFile); SETFAIL_AND_EXIT; } pszManListFile[wcslen(pwszManListFile)] = '\0'; manlist.open(pszManListFile); if (manlist.fail()) { ReportError("the manlist, %S, now as %s, can not be opened\n", pwszManListFile, pszManListFile); SETFAIL_AND_EXIT; } for(; !manlist.eof(); ) { getline(manlist, strline); if (strline.empty()) break; numLine ++; // extract manifest filename[required] and componentID[optional] and its default value could be empty or "default" istrstream iline(strline.c_str()); getline(iline, strManifestFilename, ','); getline(iline, strComponentID); MsmTrimString(strManifestFilename); MsmTrimString(strComponentID); if (strManifestFilename.empty() == TRUE) { ReportError("no manifest is specified on line %d in manlist file %S \n", numLine, pwszManListFile); SETFAIL_AND_EXIT; } IFFAILED_EXIT(InitializeAsmInfoA(strManifestFilename, strComponentID)); if (numLine == 1) { IFFAILED_EXIT(PutAssemblyIntoMergeModule(MSMGEN_CREATE_MSM_ALWAYS)); } else { IFFAILED_EXIT(PutAssemblyIntoMergeModule(MSMGEN_ADD_ASSEMBLY_INTO_EXIST_MSM)); } } IFFAILED_EXIT(FinalizedMsm()); hr = S_OK; Exit: return hr; } class ManGroupKey{ public: string strTag; string strMsmID; string strMsmFile; string strTemplateFile; string strManifest; }; class ManGroupValue{ public: string strComponentID; }; #include bool less::operator()(const ManGroupKey& x, const ManGroupKey& y) const { if (x.strTag != y.strTag) return (x.strTag < y.strTag); if (x.strMsmID != y.strMsmID) return (x.strMsmID < y.strMsmID); if (x.strMsmFile != y.strMsmFile) return (x.strMsmFile < y.strMsmFile); if (x.strTemplateFile != y.strTemplateFile) return (x.strTemplateFile < y.strTemplateFile); return (x.strManifest < y.strManifest); } #include #include HRESULT CreateMsmForManifestGroup(PCWSTR pwszMangroupFile) { HRESULT hr = S_OK; ifstream mangroup; char pszMangroupFile[MAX_PATH]; ManGroupKey mgKey; ManGroupValue mgValue; map mapManGroup; map::iterator m1_pIter; typedef pair Mangroup_Entry; int iBufSize; DWORD numLine; string strline; bool fCurrentInAGroup; bool fStartFromSratch; string strCurrentMsmid; string strCurrentMsmFile; string strCurrentMsmTemplate; PARAMETER_CHECK_NTC(pwszMangroupFile != NULL); iBufSize = WideCharToMultiByte(CP_ACP, 0, pwszMangroupFile, wcslen(pwszMangroupFile), NULL, 0, NULL, NULL); if (iBufSize > NUMBER_OF(pszMangroupFile)) { ReportError("the manlist filename, %S, is longer than MAX_PATH\n", pwszMangroupFile); SETFAIL_AND_EXIT; } if (0 == WideCharToMultiByte(CP_ACP, 0, pwszMangroupFile, wcslen(pwszMangroupFile), pszMangroupFile, NUMBER_OF(pszMangroupFile), NULL, NULL)) { ReportError("the manlist filename, %S, failed to convert to ansi string\n", pwszMangroupFile); SETFAIL_AND_EXIT; } pszMangroupFile[wcslen(pwszMangroupFile)] = '\0'; mangroup.open(pszMangroupFile); if (mangroup.fail()) { ReportError("the mangroupfile, %S, now as %s, can not be opened\n", pwszMangroupFile, pszMangroupFile); SETFAIL_AND_EXIT; } numLine = 0; // // firstly read the file into a map which is sorted during insert // for(; !mangroup.eof(); ) { getline(mangroup, strline); if (strline.empty()) continue; istrstream iline(strline.c_str()); if (iline.str() == NULL) break; numLine ++; // clean the key strings mgKey.strTag.erase(); mgKey.strMsmID.erase(); mgKey.strManifest.erase(); mgKey.strMsmFile.erase(); mgKey.strTemplateFile.erase(); mgValue.strComponentID.erase(); getline(iline, mgKey.strTag, ','); getline(iline, mgKey.strMsmID, ','); getline(iline, mgKey.strMsmFile, ','); getline(iline, mgKey.strManifest, ','); getline(iline, mgValue.strComponentID, ','); getline(iline, mgKey.strTemplateFile, ','); MsmTrimString(mgKey.strTag); MsmTrimString(mgKey.strMsmID); MsmTrimString(mgKey.strManifest); MsmTrimString(mgKey.strMsmFile); MsmTrimString(mgValue.strComponentID); MsmTrimString(mgKey.strTemplateFile); if (mgKey.strTag.empty()) { ReportError("error on line %d in mangroup file %S, also is %s: Tag or MsmID is empty\n", numLine, pwszMangroupFile, pszMangroupFile); SETFAIL_AND_EXIT; } if (mgKey.strManifest.empty()) { ReportError("error on line %d in mangroup file %S, also is %s: manifest file is empty\n", numLine, pwszMangroupFile, pszMangroupFile); SETFAIL_AND_EXIT; } if ((mgKey.strTag.compare("0") != 0) && (mgKey.strTag.compare("1") != 0)) { ReportError("error on line %d in mangroup file %S, also is %s: tag must be 0 or 1\n", numLine, pwszMangroupFile, pszMangroupFile); SETFAIL_AND_EXIT; } if (mgKey.strTag.compare("1") == 0) { // // in this case, msmid and msmfile must be specified // if ((mgKey.strMsmFile.empty()) || (mgKey.strMsmID.empty())) { ReportError("if the first column is set to be \"1\", output msm file and msmid must be specified in the mangroup file!\n"); SETFAIL_AND_EXIT; } } mapManGroup.insert(Mangroup_Entry(mgKey, mgValue)); } fCurrentInAGroup = false; fStartFromSratch = true; for ( m1_pIter = mapManGroup.begin( ); m1_pIter != mapManGroup.end( ); m1_pIter++ ) { mgKey = m1_pIter->first; mgValue = m1_pIter->second; if (mgKey.strTag.compare("1") == 0) { if (fCurrentInAGroup == false) { fStartFromSratch = true; fCurrentInAGroup = true; strCurrentMsmid = mgKey.strMsmID; strCurrentMsmFile = mgKey.strMsmFile; strCurrentMsmTemplate = mgKey.strTemplateFile; }else { if (strCurrentMsmid != mgKey.strMsmID) { IFFAILED_EXIT(FinalizedMsm()); fStartFromSratch = true; fCurrentInAGroup = true; strCurrentMsmid = mgKey.strMsmID; strCurrentMsmFile = mgKey.strMsmFile; strCurrentMsmTemplate = mgKey.strTemplateFile; } else { // they must share the same msmID and msmfile if (!mgKey.strMsmFile.empty()) { if (mgKey.strMsmFile != strCurrentMsmFile) { ReportError("must share the same msm output file\n"); SETFAIL_AND_EXIT; } } if (!mgKey.strTemplateFile.empty()) { if (mgKey.strTemplateFile != strCurrentMsmTemplate) { ReportError("must use the same msm templatefile\n"); SETFAIL_AND_EXIT; } } } } } else if (mgKey.strTag == "0") { if (fCurrentInAGroup == true) { // close current manifest group IFFAILED_EXIT(FinalizedMsm()); } fStartFromSratch = true; fCurrentInAGroup = false; } else { // impossible case ReportError("tag is neither 0 nor 1\n"); SETFAIL_AND_EXIT; } if (fStartFromSratch) { IFFAILED_EXIT(InitializeMsmA(mgKey.strManifest, mgKey.strMsmFile, mgKey.strMsmID, mgKey.strTemplateFile)); IFFAILED_EXIT(InitializeAsmInfoA(mgKey.strManifest, mgValue.strComponentID)); IFFAILED_EXIT(PutAssemblyIntoMergeModule(MSMGEN_CREATE_MSM_ALWAYS)); } else { ASSERT_NTC(fCurrentInAGroup == true); if (fCurrentInAGroup) { IFFAILED_EXIT(InitializeAsmInfoA(mgKey.strManifest, mgValue.strComponentID)); IFFAILED_EXIT(PutAssemblyIntoMergeModule(MSMGEN_ADD_ASSEMBLY_INTO_EXIST_MSM)); } } // for single generation if (fCurrentInAGroup == false) { ASSERT_NTC(fStartFromSratch == true); IFFAILED_EXIT(FinalizedMsm()); } fStartFromSratch = false; } if (fCurrentInAGroup == true) IFFAILED_EXIT(FinalizedMsm()); Exit: return hr; } extern "C" int __cdecl wmain(int argc, wchar_t** argv) { HRESULT hr = S_OK; PWSTR pszComponentID = NULL, pszMsmFile = NULL, pszMsmID = NULL, pszTemplateFile = NULL; DWORD dwGenFlag; if ((argc < 3) || ((argc % 2) != 1)) { PrintUsage(); hr = E_INVALIDARG; goto Exit; } if (!FusionpInitializeHeap(NULL)){ hr = HRESULT_FROM_WIN32(::GetLastError()); goto Exit; } CoInitialize(NULL); // // the first parameter must be "manfile" or "manlist" or "mangroup" // if (_wcsicmp(argv[1], L"manfile") == 0) { dwGenFlag = MSMGEN_FROM_MANFILE; } else if (_wcsicmp(argv[1], L"manlist") == 0) { dwGenFlag = MSMGEN_FROM_MANLIST; } else if (_wcsicmp(argv[1], L"mangroup") == 0) { dwGenFlag = dwGenFlag = MSMGEN_FROM_MANGROUP; if ((argc != 3) && (argc != 5)) { goto InvalidParam; } if (argc == 5) { // it must try to specify a template if (_wcsicmp(argv[3], L"-template") != 0) { goto InvalidParam; }else { IFFALSE_EXIT(g_sbTemplateFile.Win32Assign(argv[4], wcslen(argv[4]))); } } } else { goto InvalidParam; } //check the existence of the input file if ( GetFileAttributesW(argv[2]) == DWORD (-1)) { ReportError("man file does not exist!\n"); goto InvalidParam; } else { if (dwGenFlag == MSMGEN_FROM_MANFILE) { CStringBuffer manfile; CSmallStringBuffer ext; IFFALSE_EXIT(manfile.Win32Assign(argv[2], wcslen(argv[2]))); IFFALSE_EXIT(manfile.Win32GetPathExtension(ext)); if (ext.Cch() == 0) { ReportError("Manifest file must has ext as .man or .manifest\n"); goto InvalidParam; } } } if ((dwGenFlag == MSMGEN_FROM_MANFILE) || (dwGenFlag == MSMGEN_FROM_MANLIST)) { // // parse and validate parameters // IFFAILED_EXIT(GetInfoMsmgenCommandLineParameters(dwGenFlag, argv+3, argc-3, &pszComponentID, &pszMsmFile, &pszMsmID, &pszTemplateFile)); } switch(dwGenFlag) { case MSMGEN_FROM_MANFILE: IFFAILED_EXIT(InitializeMsmW(argv[2], pszMsmFile, pszMsmID, pszTemplateFile)); IFFAILED_EXIT(CreateMsmForSingleManifest(argv[2], pszComponentID)); break; case MSMGEN_FROM_MANLIST: if (pszComponentID != NULL) { ReportError("componentID should not be specified with manlist\n"); goto InvalidParam; } IFFAILED_EXIT(InitializeMsmW(argv[2], pszMsmFile, pszMsmID, pszTemplateFile)); IFFAILED_EXIT(CreateMsmForManifestList(argv[2])); break; case MSMGEN_FROM_MANGROUP: IFFAILED_EXIT(CreateMsmForManifestGroup(argv[2])); break; } hr = S_OK; goto Exit; InvalidParam: PrintUsage(); hr = E_INVALIDARG; Exit: CoUninitialize(); CleanupMsm(); if (hr == S_OK) printf("msm is generated successfully!"); else printf("msm is failed to be generated!"); return (hr == S_OK) ? 0 : 1; }