/* CVSNT Automatic manifest generator Copyright (C) 2006 Tony Hoyle and March-Hare Software Ltd This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // mkmanifest.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../../version.h" #include // Includes CComVariant and CComBSTR. int parse_manifest(MSXML2::IXMLDOMDocument2Ptr outDoc, const char *xml) { MSXML2::IXMLDOMDocument2Ptr doc; CComVariant varNodeType; HRESULT hr; varNodeType = NODE_ELEMENT; // MSXML2::NODE_ELEMENT hr = doc.CreateInstance("Msxml2.DOMDocument.6.0"); if (!SUCCEEDED(hr)) { // the most common thing here is "invalid class string" hr = doc.CreateInstance("Msxml2.DOMDocument.5.0"); if (!SUCCEEDED(hr)) { hr = doc.CreateInstance("Msxml2.DOMDocument.4.0"); if (!SUCCEEDED(hr)) { return -1; } } } if(!doc->loadXML(xml)) return -1; doc->setProperty("SelectionLanguage","XPath"); doc->setProperty("SelectionNamespaces", "xmlns:ns='urn:schemas-microsoft-com:asm.v1'"); _bstr_t version,name,type,processor,language; MSXML2::IXMLDOMNodePtr node,val; node = doc->selectSingleNode("/ns:assembly/ns:assemblyIdentity"); if(node==NULL) return -1; val = node->attributes->getNamedItem("version"); if(val) version = val->text; else return -1; val = node->attributes->getNamedItem("name"); if(val) name = val->text; else return -1; val = node->attributes->getNamedItem("processorArchitecture"); if(val) processor = val->text; #ifdef _WIN64 else processor="amd64"; #else else processor="x86"; #endif val = node->attributes->getNamedItem("language"); // No examples of this so this may be wrong if(val) language = val->text; else language="*"; val = node->attributes->getNamedItem("type"); if(val) type = val->text; else type="Win32"; node = outDoc->selectSingleNode("/ns:assembly"); val = outDoc->createNode(varNodeType,"dependency","urn:schemas-microsoft-com:asm.v1"); node->appendChild(val); node = val; MSXML2::IXMLDOMNodePtr dep = outDoc->createNode(varNodeType,"dependentAssembly","urn:schemas-microsoft-com:asm.v1"); MSXML2::IXMLDOMNodePtr iden = outDoc->createNode(varNodeType,"assemblyIdentity","urn:schemas-microsoft-com:asm.v1"); node->appendChild(dep); dep->appendChild(iden); val = outDoc->createAttribute("version"); val->text=version; iden->attributes->setNamedItem(val); val = outDoc->createAttribute("type"); val->text=type; iden->attributes->setNamedItem(val); val = outDoc->createAttribute("name"); val->text=name; iden->attributes->setNamedItem(val); val = outDoc->createAttribute("processorArchitecture"); val->text=processor; iden->attributes->setNamedItem(val); val = outDoc->createAttribute("language"); val->text=language; iden->attributes->setNamedItem(val); return 0; } static WORD g_wManifestLanguage=MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); BOOL CALLBACK EnumResLangProc( HANDLE hModule, LPCTSTR lpszType, LPCTSTR lpszName, WORD wIDLanguage, LONG_PTR lParam) { g_wManifestLanguage=wIDLanguage; return FALSE; } void mkmanifest_help(void) { printf("cvsnt manifest patcher "CVSNT_PRODUCTVERSION_STRING"\n"); printf("usage: mkmanifest [-v version] [-d] [-r] [-q] [-a] [-u] [-P searchpath] [-d] \n"); printf(" -d verbose mode (implies -a -u)\n"); printf(" -a create an ascii manifest file\n"); printf(" -u create a unicode manifest file (with BOM)\n"); printf(" -q quiet and do not prompt for a key at end\n"); printf(" -Q really quiet and do not prompt for a key at end\n"); printf(" **** experimental options:\n"); printf(" -r if there is no version block in the resource segment attempt to add one\n"); printf(" -f force mkmanifest to add a manifest with no version block\n"); } int main(int argc, char* argv[]) { LOADED_IMAGE *loaded_image; char szPath[4096]; std::string version,path; bool setversion=false; bool debug=false; bool addversion=false; bool asciimanifest=false; bool forceadd=false; bool unicodemanifest=false; bool donotstop=false; bool reallyquiet=false; HRESULT hr; hr = CoInitialize(NULL); if (hr != S_OK ) return -1; MSXML2::IXMLDOMDocument2Ptr doc; hr = doc.CreateInstance("Msxml2.DOMDocument.6.0"); if (!SUCCEEDED(hr)) { // the most common thing here is "invalid class string" hr = doc.CreateInstance("Msxml2.DOMDocument.5.0"); if (!SUCCEEDED(hr)) { hr = doc.CreateInstance("Msxml2.DOMDocument.4.0"); if (!SUCCEEDED(hr)) { return -1; } } } version=CVSNT_PRODUCTVERSION_SHORT; path="..;d:\\cvsbin\\sysfiles;d:\\cvsbin;..\\cvsdeps\\sysfiles;c:\\Program Files\\CVSNT;c:\\Program Files\\CVS Suite\\CVSNT;c:\\Program Files\\CVSNT\\sysfiles;c:\\Program Files\\CVS Suite\\CVSNT\\sysfiles"; debug=false; while(argc>1 && argv[1][0]=='-') { switch(argv[1][1]) { case 'v': version = argv[2]; argv++; argc--; setversion=true; break; case 'P': path = argv[2]; argv++; argc--; break; case 'a': asciimanifest = true; break; case 'f': forceadd = true; break; case 'u': unicodemanifest = true; break; case 'd': debug = true; break; case 'q': donotstop = true; break; case 'Q': reallyquiet = true; break; case 'r': addversion = true; break; default: mkmanifest_help(); return -1; } argv++; argc--; } if(argc<2) { mkmanifest_help(); return -1; } HMODULE hRes = LoadLibraryEx(argv[1],NULL,DONT_RESOLVE_DLL_REFERENCES); if(!hRes) { printf("Couldn't load image!\n"); return 0; } GetModuleFileName(hRes, szPath, sizeof(szPath)); if(debug) printf("Generating manifest for version %s of %s\n",version.c_str(),szPath); *strrchr(szPath,'\\')='\0'; SetCurrentDirectory(szPath); strcpy(szPath,path.c_str()); strcat(szPath,";"); GetEnvironmentVariable("Path",szPath+strlen(szPath),(DWORD)(sizeof(path)-strlen(szPath))); SetEnvironmentVariable("Path",szPath); HRSRC hRsrc = FindResource(hRes,MAKEINTRESOURCE(1),RT_MANIFEST); if(hRsrc) { if(debug) printf("Found the manifest resource\n"); EnumResourceLanguages(hRes,RT_MANIFEST,MAKEINTRESOURCE(1),(ENUMRESLANGPROC)EnumResLangProc,NULL); LPVOID pRsrc = LoadResource(hRes, hRsrc); DWORD dwLen = SizeofResource(hRes, hRsrc); DWORD dwOldProtect; VirtualProtect(pRsrc,dwLen+1,PAGE_READWRITE,&dwOldProtect); ((LPBYTE)pRsrc)[dwLen]='\0'; // This is cheating but we don't care since this is the only data we need if(doc->loadXML((const char *)pRsrc)) { doc->setProperty("SelectionLanguage","XPath"); doc->setProperty("SelectionNamespaces", "xmlns='urn:schemas-microsoft-com:asm.v1' xmlns:ns='urn:schemas-microsoft-com:asm.v1'"); } else { const char *_xml="\n" "\n" "\n" "\n"; if(doc->loadXML(_xml)) { doc->setProperty("SelectionLanguage","XPath"); doc->setProperty("SelectionNamespaces", "xmlns='urn:schemas-microsoft-com:asm.v1' xmlns:ns='urn:schemas-microsoft-com:asm.v1'"); } } FreeResource(hRsrc); } else { if(debug) printf("No manifest resource in this object - make our own\n"); const char *_xml="\n" "\n" "\n" "\n"; if(doc->loadXML(_xml)) { doc->setProperty("SelectionLanguage","XPath"); doc->setProperty("SelectionNamespaces", "xmlns='urn:schemas-microsoft-com:asm.v1' xmlns:ns='urn:schemas-microsoft-com:asm.v1'"); } } HRSRC hRsrc_v = FindResource(hRes,MAKEINTRESOURCE(VS_VERSION_INFO),RT_VERSION); if(hRsrc_v) { LPVOID pRsrc = LoadResource(hRes, hRsrc_v); // something here.. updating versioninfo is even harder than updating the manifests! if (pRsrc!=NULL) { if(debug) printf("The version resource is loaded \n"); LPVOID pBlock = LockResource(pRsrc); if (pBlock != NULL) { VS_FIXEDFILEINFO fixedinfo; const char csEntry[]="FileVersion\0"; char csRet[4096]; DWORD dwLen,langD; BOOL retVal; LPVOID retbuffer=NULL; static char szSubBlock[256]; sprintf(szSubBlock, "\\"); retVal = VerQueryValue(pBlock, szSubBlock, &retbuffer, (UINT *)&dwLen); if (retVal && dwLen==sizeof(fixedinfo)) { memcpy(&fixedinfo,retbuffer,sizeof(fixedinfo)); if ((!reallyquiet) || (debug)) { printf("FileVersion = %d, %d, %d, %d\n", (fixedinfo.dwFileVersionMS&0xffff0000)>>16, fixedinfo.dwFileVersionMS&0xffff, (fixedinfo.dwFileVersionLS&0xffff0000)>>16, fixedinfo.dwFileVersionLS&0xffff); printf("ProductVersion = %d, %d, %d, %d\n", (fixedinfo.dwProductVersionMS&0xffff0000)>>16, fixedinfo.dwProductVersionMS&0xffff, (fixedinfo.dwProductVersionLS&0xffff0000)>>16, fixedinfo.dwProductVersionLS&0xffff); if (debug) { printf("FileOS = 0x%x\n",fixedinfo.dwFileOS); printf("FileType = 0x%x\n",fixedinfo.dwFileType); printf("FielSubtype = 0x%x\n",fixedinfo.dwFileSubtype); } } } sprintf(szSubBlock, "\\VarFileInfo\\Translation"); retVal = VerQueryValue(pBlock, szSubBlock, &retbuffer, (UINT *)&dwLen); if (retVal && dwLen==4) { memcpy(&langD, retbuffer, 4); sprintf(szSubBlock, "\\StringFileInfo\\%02X%02X%02X%02X\\%s", (langD & 0xff00)>>8, langD & 0xff,(langD & 0xff000000)>>24, (langD & 0xff0000)>>16, csEntry); } else sprintf(szSubBlock, "\\StringFileInfo\\%04X04B0\\%s", GetUserDefaultLangID(), csEntry); dwLen=0; if (VerQueryValue(pBlock, szSubBlock, &retbuffer, (UINT *)&dwLen)) strcpy(csRet, (char *)retbuffer); else { if (debug) printf("VerQueryValue returned error\n"); } UnlockResource(pRsrc); if (debug) printf("VerQueryValue %s = %s\n",szSubBlock,(char *)csRet); } } FreeResource(hRsrc_v); } else { if ((!addversion)&&(!forceadd)) { printf("The file does not contain any version resources, cannot add a manifest!\n"); printf("Use the -r option if you want to attempt adding version resources.\n"); printf("or use the -f option to force adding a manifest with no version resources.\n"); return -1; } else if (forceadd) { printf("The file does not contain any resources, adding a manifest will probably break it!\n"); } else { // attempt to add a version block! VS_FIXEDFILEINFO fixedinfo; size_t lenpath; char szPathFile[4096]; GetModuleFileName(hRes, szPathFile, sizeof(szPathFile)); if (setversion) { printf("Cannot combine -v and -r parameters!\n"); return -1; } if (debug) printf("Attempt to add a version resource.\n"); ZeroMemory(&fixedinfo,sizeof(fixedinfo)); // CVSNT_PRODUCT_MAJOR, // CVSNT_PRODUCT_MINOR, // CVSNT_PRODUCT_PATCHLEVEL: // CVSNT_PRODUCT_BUILD: fixedinfo.dwFileVersionMS = CVSNT_PRODUCT_MAJOR<<16; fixedinfo.dwFileVersionMS += CVSNT_PRODUCT_MINOR; fixedinfo.dwFileVersionLS = CVSNT_PRODUCT_PATCHLEVEL<<16; fixedinfo.dwFileVersionLS += CVSNT_PRODUCT_BUILD; if (!reallyquiet) { printf("FileVersion = %d, %d, %d, %d\n", (fixedinfo.dwFileVersionMS&0xffff0000)>>16, fixedinfo.dwFileVersionMS&0xffff, (fixedinfo.dwFileVersionLS&0xffff0000)>>16, fixedinfo.dwFileVersionLS&0xffff); } fixedinfo.dwProductVersionMS = CVSNT_PRODUCT_MAJOR<<16; fixedinfo.dwProductVersionMS += CVSNT_PRODUCT_MINOR; fixedinfo.dwProductVersionLS = CVSNT_PRODUCT_PATCHLEVEL<<16; if (!reallyquiet) { printf("ProductVersion = %d, %d, %d, %d\n", (fixedinfo.dwProductVersionMS&0xffff0000)>>16, fixedinfo.dwProductVersionMS&0xffff, (fixedinfo.dwProductVersionLS&0xffff0000)>>16, fixedinfo.dwProductVersionLS&0xffff); } fixedinfo.dwFileFlags = 0x1; fixedinfo.dwFileFlagsMask = 0x17; fixedinfo.dwFileOS = 0x4; // VOS_WINDOWS32 if (debug) { printf("FileOS = 0x%x\n",fixedinfo.dwFileOS); printf("FileType = 0x%x\n",fixedinfo.dwFileType); printf("FielSubtype = 0x%x\n",fixedinfo.dwFileSubtype); } lenpath=strlen(argv[1]); if (debug) printf("the file suffiix is %s\n",&argv[1][lenpath-4]); if (stricmp(&argv[1][lenpath-4],".dll")==0) fixedinfo.dwFileType = 0x2; // VFT_DLL else fixedinfo.dwFileType = 0x1; // VFT_APP fixedinfo.dwFileSubtype = 0; // VFT2_UNKNOWN FreeLibrary(hRes); HANDLE hUpdate1 = BeginUpdateResource(argv[1],TRUE); if(!hUpdate1) { printf("BeginUpdateResource (version) failed!!!\n"); return -1; } if(!UpdateResource(hUpdate1,RT_VERSION,"",g_wManifestLanguage,(LPVOID)&fixedinfo,sizeof(fixedinfo))) { printf("UpdateResource (version) failed!!\n"); return -1; } if(!EndUpdateResource(hUpdate1, FALSE)) { printf("EndUpdateResource (version) failed!!\n"); return -1; } hRes = LoadLibraryEx(argv[1],NULL,DONT_RESOLVE_DLL_REFERENCES); if (hRes==NULL) return -1; } } loaded_image=ImageLoad(argv[1],NULL); if(!loaded_image) { printf("Couldn't load image!"); return 0; } DWORD dwSize; PBYTE pDllName; PIMAGE_IMPORT_DESCRIPTOR pimage_import_descriptor= (PIMAGE_IMPORT_DESCRIPTOR) ImageDirectoryEntryToData(loaded_image->MappedAddress,FALSE,IMAGE_DIRECTORY_ENTRY_IMPORT,&dwSize); if(debug) printf("Dependencies:\n"); while(pimage_import_descriptor->Name!=0) { pDllName = (PBYTE)ImageRvaToVa(loaded_image->FileHeader,loaded_image->MappedAddress,pimage_import_descriptor->Name,NULL); HMODULE hRes2 = LoadLibraryEx((LPCSTR)pDllName,NULL,DONT_RESOLVE_DLL_REFERENCES); if(hRes2) { GetModuleFileName(hRes2, szPath, sizeof(szPath)); HRSRC hRsrc = FindResource(hRes2,MAKEINTRESOURCE(1),RT_MANIFEST); if(hRsrc) { LPVOID pRsrc = LoadResource(hRes2, hRsrc); DWORD dwLen = SizeofResource(hRes2, hRsrc); DWORD dwOldProtect; VirtualProtect(pRsrc,dwLen+1,PAGE_READWRITE,&dwOldProtect); ((LPBYTE)pRsrc)[dwLen]='\0'; // This is cheating but we don't care since this is the only data we need if(debug) printf(" %s (manifest)\n",szPath); parse_manifest(doc, (const char *)pRsrc); FreeResource(hRsrc); } else if(debug) printf(" %s\n",szPath); FreeLibrary(hRes2); } pimage_import_descriptor++; } ImgDelayDescr *pimage_delay_descriptor= (ImgDelayDescr*) ImageDirectoryEntryToData(loaded_image->MappedAddress,FALSE,IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT,&dwSize); #if (DELAYLOAD_VERSION >= 0x0200) || (_DELAY_IMP_VER > 1) if(debug) printf("Delayload dependencies (delayload version >= 0x0200 or delay imp ver >1):\n"); while(pimage_delay_descriptor && pimage_delay_descriptor->rvaDLLName!=0) #else if(debug) printf("Delayload dependencies (delayload version < 0x0200 and delay imp ver <1):\n"); while(pimage_delay_descriptor && pimage_delay_descriptor->szName!=NULL) #endif { #if (DELAYLOAD_VERSION >= 0x0200) || (_DELAY_IMP_VER > 1) pDllName = (PBYTE)ImageRvaToVa(loaded_image->FileHeader,loaded_image->MappedAddress,pimage_delay_descriptor->rvaDLLName,NULL); #else pDllName = (PBYTE)pimage_delay_descriptor->szName; #endif HMODULE hRes3 = LoadLibraryEx((LPCSTR)pDllName,NULL,DONT_RESOLVE_DLL_REFERENCES); if(hRes3) { GetModuleFileName(hRes3, szPath, sizeof(szPath)); HRSRC hRsrc = FindResource(hRes3,MAKEINTRESOURCE(1),RT_MANIFEST); if(hRsrc) { LPVOID pRsrc = LoadResource(hRes3, hRsrc); DWORD dwLen = SizeofResource(hRes3, hRsrc); DWORD dwOldProtect; VirtualProtect(pRsrc,dwLen+1,PAGE_READWRITE,&dwOldProtect); ((LPBYTE)pRsrc)[dwLen]='\0'; // This is cheating but we don't care since this is the only data we need if(debug) printf(" %s (manifest)\n",szPath); parse_manifest(doc, (const char *)pRsrc); FreeResource(hRsrc); } else if(debug) printf(" %s\n",szPath); FreeLibrary(hRes3); } pimage_delay_descriptor++; } MSXML2::IXMLDOMNodePtr node,val; std::string name,arch; size_t off; const char *pname; pname = strrchr(loaded_image->ModuleName,'\\'); if(!pname) pname = strrchr(loaded_image->ModuleName,'/'); if(!pname) pname=loaded_image->ModuleName; else pname++; name = pname; off = name.find_last_of('.'); if(off!=std::string::npos) name.resize(off); #ifdef _WIN64 arch="amd64"; #else arch="x86"; #endif node = doc->selectSingleNode("/ns:assembly/ns:assemblyIdentity"); if(node==NULL) { printf("processing error: no assemblyIdentity\n"); return -1; } val = node->attributes->getNamedItem("version"); if(val) val->text=version.c_str(); else { val = doc->createAttribute("version"); val->text=version.c_str(); node->attributes->setNamedItem(val); } val = node->attributes->getNamedItem("name"); if(val) val->text=name.c_str(); else { val = doc->createAttribute("name"); val->text=name.c_str(); node->attributes->setNamedItem(val); } val = node->attributes->getNamedItem("processorArchitecture"); if(val) val->text=arch.c_str(); else { val = doc->createAttribute("processorArchitecture"); val->text=arch.c_str(); node->attributes->setNamedItem(val); } ImageUnload(loaded_image); const char *_xsl = "\n" "\n" "\n" "\n" "\n" " \n" "\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n" "\n" "\n" " \n" "\n" " \n" "\n" "\n" "\n" "\n"; MSXML2::IXMLDOMDocument2Ptr xslDoc; hr = xslDoc.CreateInstance("Msxml2.DOMDocument.6.0"); if (!SUCCEEDED(hr)) { // the most common thing here is "invalid class string" hr = xslDoc.CreateInstance("Msxml2.DOMDocument.5.0"); if (!SUCCEEDED(hr)) { hr = xslDoc.CreateInstance("Msxml2.DOMDocument.4.0"); if (!SUCCEEDED(hr)) { if(debug) printf("Cannot find MSXML 4.0, 5.0 or 6.0 installed\n"); return -1; } else if(debug) printf("Using MSXML 4.0\n"); } else if(debug) printf("Using MSXML 5.0\n"); } else if(debug) printf("Using MSXML 6.0\n"); xslDoc->loadXML(_xsl); _bstr_t txt = doc->transformNode(xslDoc); DWORD txt_length_in_bytes; txt_length_in_bytes=txt.length()*2; if(debug) { printf("\n"); printf("text is %d characters long (%d bytes)\n",(int)txt.length(),(int)txt_length_in_bytes); printf("%s\n",(const char *)txt); } GetModuleFileName(hRes,szPath,MAX_PATH); FreeLibrary(hRes); HANDLE hUpdate = BeginUpdateResource(szPath,FALSE); if(!hUpdate) { printf("BeginUpdateResource failed!!!\n"); return -1; } if (debug||asciimanifest||unicodemanifest) { DWORD written=0; HANDLE hFile; char manifest[4096], *lastdot; if (unicodemanifest || debug) { strcpy(manifest,szPath); if ((lastdot=strrchr(manifest,'.'))!=NULL) strcpy(lastdot,".manifest"); else strcat(manifest,".manifest"); hFile=CreateFile(manifest,GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_DELETE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if (hFile!=INVALID_HANDLE_VALUE) { char szBOM[] = "\377\376\0"; // 0xFF, 0xFE // leave off TEXT() macro. /* file begins with FF FE or FE FF (Reversed Byte Order Mark) */ // this will write a UNICODE file: WriteFile(hFile,(LPVOID)szBOM,2,&written,NULL); WriteFile(hFile,(LPVOID)(const wchar_t *)txt,txt_length_in_bytes,&written,NULL); CloseHandle(hFile); if (debug) printf("Unicode manifest successfully written to %s!\n",manifest); } } if (asciimanifest || debug) { strcpy(manifest,szPath); if ((lastdot=strrchr(manifest,'.'))!=NULL) strcpy(lastdot,"-txt.manifest"); else strcat(manifest,"-txt.manifest"); hFile=CreateFile(manifest,GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_DELETE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if (hFile!=INVALID_HANDLE_VALUE) { WriteFile(hFile,(LPVOID)(const char *)txt,txt.length(),&written,NULL); CloseHandle(hFile); if (debug) printf("ASCII manifest successfully written to %s!\n",manifest); } } } if(!UpdateResource(hUpdate,RT_MANIFEST,MAKEINTRESOURCE(1),g_wManifestLanguage,(LPVOID)(const char *)txt,txt.length())) { printf("UpdateResource failed!!\n"); return -1; } else if(debug) printf("UpdateResource succeeded!\n"); if(!EndUpdateResource(hUpdate, FALSE)) { printf("EndUpdateResource failed!!\n"); return -1; } else if(debug) printf("EndUpdateResource succeeded!\n"); hRes = LoadLibraryEx(argv[1],NULL,DONT_RESOLVE_DLL_REFERENCES); if (hRes==NULL) return -1; if (!donotstop) { if(debug) printf("Press a key to exit\n"); getchar(); } return 0; }