// Classic Shell (c) 2009-2016, Ivo Beltchev // Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author #define STRICT_TYPED_ITEMIDS #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit #include #include #include #include #include #include "StringUtils.h" #include "FNVHash.h" #include "SettingsParser.h" #include "resource.h" #include "..\UpdateBin\resource.h" #include "ResourceHelper.h" #include #include "SaveLogFile.h" // Manifest to enable the 6.0 common controls #pragma comment(linker, \ "\"/manifestdependency:type='Win32' "\ "name='Microsoft.Windows.Common-Controls' "\ "version='6.0.0.0' "\ "processorArchitecture='*' "\ "publicKeyToken='6595b64144ccf1df' "\ "language='*'\"") HINSTANCE g_hInstance; void EditLoggingOptions( void ); void ManualUninstall( void ); void ShowMetroColorViewer( void ); #ifndef _WIN64 /////////////////////////////////////////////////////////////////////////////// static void LoadFile( const wchar_t *fname, std::vector &buf ) { buf.clear(); FILE *f=NULL; if (_wfopen_s(&f,fname,L"rb") || !f) return; fseek(f,0,SEEK_END); int size=ftell(f); fseek(f,0,SEEK_SET); buf.resize(size); if (size==0 || fread(&buf[0],1,size,f)!=size) buf.clear(); fclose(f); } static unsigned int CalcFileFNV( const wchar_t *fname ) { std::vector buf; LoadFile(fname,buf); if (buf.empty()) return 0; return CalcFNVHash(&buf[0],(int)buf.size(),FNV_HASH0); } static void Printf( const char *format, ... ) { char buf[1024]; va_list args; va_start(args,format); int len=Vsprintf(buf,_countof(buf),format,args); va_end(args); DWORD q; WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),buf,len,&q,NULL); #if _DEBUG OutputDebugStringA(buf); #endif } /////////////////////////////////////////////////////////////////////////////// struct Chunk { int start1, start2, len; }; int CalcMsiChecksum( wchar_t *const *params, int count ) { if (count<2) return 2; AttachConsole(ATTACH_PARENT_PROCESS); // load files wchar_t path1[_MAX_PATH]; std::vector buf1, buf2; Sprintf(path1,_countof(path1),L"%s\\ClassicShellSetup32.msi",params[1]); LoadFile(path1,buf1); if (buf1.empty()) { Printf("Failed to open file %s\n",path1); return 1; } wchar_t path2[_MAX_PATH]; Sprintf(path2,_countof(path2),L"%s\\ClassicShellSetup64.msi",params[1]); LoadFile(path2,buf2); if (buf2.empty()) { Printf("Failed to open file %s\n",path2); return 1; } int len1=(int)buf1.size(); int len2=(int)buf2.size(); for (std::vector::iterator it=buf1.begin();it!=buf1.end();++it) *it^=0xFF; for (std::vector::iterator it=buf2.begin();it!=buf2.end();++it) *it^=0xFF; // detect common blocks (assuming at least 256K in size and in the same order in both files) const int BLOCK_SIZE=256*1024; std::vector chunks; int start2=0; for (int i=0;i0 && chunk.start2>0 && buf1[chunk.start1-1]==buf2[chunk.start2-1]) { chunk.start1--; chunk.start2--; chunk.len++; } while (chunk.start1+chunk.len::const_iterator it=chunks.begin();it!=chunks.end();++it) { if (it->start2-start>0) fwrite(&buf2[start],1,it->start2-start,f); start=it->start2+it->len; } if (len2-start>0) fwrite(&buf2[start],1,len2-start,f); fclose(f); } unsigned int fnvs[2]; fnvs[0]=CalcFNVHash(&buf1[0],len1,FNV_HASH0); fnvs[1]=CalcFNVHash(&buf2[0],len2,FNV_HASH0); // save fnvs and chunks { FILE *f=NULL; if (_wfopen_s(&f,L"msichecksum.bin",L"wb") || !f) { Printf("Failed to write msichecksum.bin\n"); return 1; } fwrite(fnvs,4,_countof(fnvs),f); int count=(int)chunks.size(); fwrite(&count,1,4,f); fwrite(&chunks[0],sizeof(Chunk),count,f); fclose(f); } return 0; } /////////////////////////////////////////////////////////////////////////////// int MakeEnglishDll( wchar_t *const *params, int count ) { if (count<5) return 2; AttachConsole(ATTACH_PARENT_PROCESS); HMODULE hEn0=LoadLibraryEx(L"en-US.dll",NULL,LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (!hEn0) { Printf("Failed to open en-US.dll (err: %d)\n",GetLastError()); return 1; } std::vector version; { HRSRC hResInfo=FindResource(hEn0,MAKEINTRESOURCE(VS_VERSION_INFO),RT_VERSION); if (hResInfo) { HGLOBAL hRes=LoadResource(hEn0,hResInfo); void *pRes=LockResource(hRes); if (pRes) { DWORD len=SizeofResource(hEn0,hResInfo); if (len>=40+sizeof(VS_FIXEDFILEINFO)) { version.resize(len); memcpy(&version[0],pRes,len); } } } } FreeLibrary(hEn0); if (version.empty()) { Printf("Can't find version resource in en-US.dll\n"); return 1; } HANDLE hEn=BeginUpdateResource(L"en-US.dll",FALSE); if (!hEn) { Printf("Failed to open en-US.dll (err: %d)\n",GetLastError()); return 1; } int res=1; HMODULE hExplorer=NULL, hMenu=NULL, hIE=NULL; WORD language=MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US); // get version, strings and dialog from ClassicExplorer32.dll hExplorer=LoadLibraryEx(params[1],NULL,LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (!hExplorer) { Printf("Failed to open %S (err: %d)\n",params[1],GetLastError()); goto qqq; } // copy version { HRSRC hResInfo=FindResource(hExplorer,MAKEINTRESOURCE(VS_VERSION_INFO),RT_VERSION); void *pRes=NULL; if (hResInfo) { HGLOBAL hRes=LoadResource(hExplorer,hResInfo); pRes=LockResource(hRes); } if (!pRes) { Printf("Can't find version resource in %S\n",params[1]); goto qqq; } VS_FIXEDFILEINFO *pVer=(VS_FIXEDFILEINFO*)((char*)pRes+40); VS_FIXEDFILEINFO *pVer0=(VS_FIXEDFILEINFO*)(&version[40]); pVer0->dwProductVersionMS=pVer->dwProductVersionMS; pVer0->dwProductVersionLS=pVer->dwProductVersionLS; pVer0->dwFileVersionMS=pVer->dwFileVersionMS; pVer0->dwFileVersionLS=pVer->dwFileVersionLS; UpdateResource(hEn,RT_VERSION,MAKEINTRESOURCE(VS_VERSION_INFO),language,&version[0],version.size()); } // copy strings for (int i=2000;i<3000;i+=16) { int id=i/16; HRSRC hResInfo=FindResource(hExplorer,MAKEINTRESOURCE(id),RT_STRING); if (!hResInfo) continue; HGLOBAL hRes=LoadResource(hExplorer,hResInfo); void *pRes=LockResource(hRes); if (!pRes) continue; UpdateResource(hEn,RT_STRING,MAKEINTRESOURCE(id),language,pRes,SizeofResource(hExplorer,hResInfo)); } // copy dialogs for (int id=2000;id<2010;id++) { HRSRC hResInfo=FindResource(hExplorer,MAKEINTRESOURCE(id),RT_DIALOG); if (!hResInfo) continue; HGLOBAL hRes=LoadResource(hExplorer,hResInfo); void *pRes=LockResource(hRes); if (!pRes) continue; UpdateResource(hEn,RT_DIALOG,MAKEINTRESOURCE(id),language,pRes,SizeofResource(hExplorer,hResInfo)); } // get strings and dialog from ClassicStartMenuDLL.dll hMenu=LoadLibraryEx(params[2],NULL,LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (!hMenu) { Printf("Failed to open %S (err: %d)\n",params[2],GetLastError()); goto qqq; } // copy strings for (int i=3000;i<5000;i+=16) { int id=i/16; HRSRC hResInfo=FindResource(hMenu,MAKEINTRESOURCE(id),RT_STRING); if (!hResInfo) continue; HGLOBAL hRes=LoadResource(hMenu,hResInfo); void *pRes=LockResource(hRes); if (!pRes) continue; UpdateResource(hEn,RT_STRING,MAKEINTRESOURCE(id),language,pRes,SizeofResource(hMenu,hResInfo)); } for (int i=7000;i<8000;i+=16) { int id=i/16; HRSRC hResInfo=FindResource(hMenu,MAKEINTRESOURCE(id),RT_STRING); if (!hResInfo) continue; HGLOBAL hRes=LoadResource(hMenu,hResInfo); void *pRes=LockResource(hRes); if (!pRes) continue; UpdateResource(hEn,RT_STRING,MAKEINTRESOURCE(id),language,pRes,SizeofResource(hMenu,hResInfo)); } // copy dialogs for (int id=3000;id<4010;id++) { HRSRC hResInfo=FindResource(hMenu,MAKEINTRESOURCE(id),RT_DIALOG); if (!hResInfo) continue; HGLOBAL hRes=LoadResource(hMenu,hResInfo); void *pRes=LockResource(hRes); if (!pRes) continue; UpdateResource(hEn,RT_DIALOG,MAKEINTRESOURCE(id),language,pRes,SizeofResource(hMenu,hResInfo)); } // get strings from ClassicIEDLL.dll hIE=LoadLibraryEx(params[3],NULL,LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (!hIE) { Printf("Failed to open %S (err: %d)\n",params[3],GetLastError()); goto qqq; } // copy strings for (int i=5000;i<6000;i+=16) { int id=i/16; HRSRC hResInfo=FindResource(hIE,MAKEINTRESOURCE(id),RT_STRING); if (!hResInfo) continue; HGLOBAL hRes=LoadResource(hIE,hResInfo); void *pRes=LockResource(hRes); if (!pRes) continue; UpdateResource(hEn,RT_STRING,MAKEINTRESOURCE(id),language,pRes,SizeofResource(hIE,hResInfo)); } // get strings and dialog from ClassicShellUpdate.exe hMenu=LoadLibraryEx(params[4],NULL,LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (!hMenu) { Printf("Failed to open %S (err: %d)\n",params[4],GetLastError()); goto qqq; } // copy strings for (int i=6000;i<7000;i+=16) { int id=i/16; HRSRC hResInfo=FindResource(hMenu,MAKEINTRESOURCE(id),RT_STRING); if (!hResInfo) continue; HGLOBAL hRes=LoadResource(hMenu,hResInfo); void *pRes=LockResource(hRes); if (!pRes) continue; UpdateResource(hEn,RT_STRING,MAKEINTRESOURCE(id),language,pRes,SizeofResource(hMenu,hResInfo)); } // copy dialogs for (int id=6000;id<6010;id++) { HRSRC hResInfo=FindResource(hMenu,MAKEINTRESOURCE(id),RT_DIALOG); if (!hResInfo) continue; HGLOBAL hRes=LoadResource(hMenu,hResInfo); void *pRes=LockResource(hRes); if (!pRes) continue; UpdateResource(hEn,RT_DIALOG,MAKEINTRESOURCE(id),language,pRes,SizeofResource(hMenu,hResInfo)); } res=0; qqq: if (!EndUpdateResource(hEn,res!=0) && res==0) { Printf("Failed to update en-US.dll (err: %d)\n",GetLastError()); res=1; } if (hExplorer) FreeLibrary(hExplorer); if (hMenu) FreeLibrary(hMenu); if (hIE) FreeLibrary(hIE); return res; } /////////////////////////////////////////////////////////////////////////////// struct LanguageData { std::map strings; CString bitmap; }; int GenerateUpdateFile( wchar_t *const *params, int count ) { if (count<3) return 3; std::vector buf; LoadFile(params[1],buf); if (buf.empty()) return 1; buf.push_back(0); buf.push_back(0); wchar_t token[256]; std::map languages; const int DEFAULT_LANGUAGE=0x409; const wchar_t *str0=(wchar_t*)&buf[0]; if (*str0==0xFEFF) str0++; const wchar_t *str; // old (current) version str=wcsstr(str0,L"{OLD_VER}"); if (!str) return 1; GetToken(str+9,token,_countof(token),L"\r\n"); int v1, v2, v3; swscanf_s(token,L"%d.%d.%d",&v1,&v2,&v3); // new version str=wcsstr(str0,L"{NEW_VER}"); if (!str) return 1; GetToken(str+9,token,_countof(token),L"\r\n"); languages[DEFAULT_LANGUAGE].strings[IDS_VERSION]=token; // signer str=wcsstr(str0,L"{SIGNER}"); if (!str) return 1; GetToken(str+8,token,_countof(token),L"\r\n"); languages[DEFAULT_LANGUAGE].strings[IDS_INSTALL_SIGNER]=token; // update str=wcsstr(str0,L"{UPDATE}"); if (!str) return 1; GetToken(str+8,token,_countof(token),L"\r\n"); languages[DEFAULT_LANGUAGE].strings[IDS_UPDATE_LINK]=token; // languages str=wcsstr(str0,L"{LANGUAGES}"); if (!str) return 1; GetToken(str+11,token,_countof(token),L"\r\n"); languages[DEFAULT_LANGUAGE].strings[IDS_LANGUAGE_LINK]=token; // language folder str=wcsstr(str0,L"{LANGFOLDER}"); if (!str) return 1; wchar_t langFolder[_MAX_PATH]; GetToken(str+12,langFolder,_countof(langFolder),L"\r\n"); // alt url str=wcsstr(str0,L"{ALT}"); if (str) { GetToken(str+5,token,_countof(token),L"\r\n"); languages[DEFAULT_LANGUAGE].strings[IDS_ALT_URL]=token; } // news str=wcsstr(str0,L"{NEWS}"); if (!str) return 1; languages[DEFAULT_LANGUAGE].strings[IDS_NEWS]=str+6; // look for {INST: str=str0; bool res=true; while (1) { str=wcsstr(str,L"{INST: "); res=true; if (!str) break; res=false; str+=7; str=GetToken(str,token,_countof(token),L"}\r\n"); int language; if (!GetLocaleInfoEx(token,LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER,(LPWSTR)&language,4)) break; str=GetToken(str,token,_countof(token),L"\r\n"); languages[language].strings[IDS_INSTALL_URL]=token; // languages[language].strings[IDS_VERSION]=...; // TODO: allow per-language version of the installer } if (!res) return 1; str=str0; while (1) { str=wcsstr(str,L"{LANG: "); res=true; if (!str) break; res=false; str+=7; str=GetToken(str,token,_countof(token),L"}\r\n"); int len=Strlen(token); bool bBasic=(len>0 && token[len-1]=='*'); if (bBasic) token[len-1]=0; int language; if (!GetLocaleInfoEx(token,LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER,(LPWSTR)&language,4)) break; wchar_t fname[_MAX_PATH]; Sprintf(fname,_countof(fname),L"%s\\%s.dll",langFolder,token); /* if (GetFileAttributes(fname)==INVALID_FILE_ATTRIBUTES) { fname[0]=0; wchar_t find[_MAX_PATH]; Sprintf(find,_countof(find),L"%s\\*.*",langFolder); WIN32_FIND_DATA data; HANDLE h=FindFirstFile(find,&data); while (h!=INVALID_HANDLE_VALUE) { if ((data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) && data.cFileName[0]!='.') { Sprintf(fname,_countof(fname),L"%s\\%s\\%s.dll",langFolder,data.cFileName,token); if (GetFileAttributes(fname)!=INVALID_FILE_ATTRIBUTES) { FindClose(h); break; } } if (!FindNextFile(h,&data)) { FindClose(h); break; } } }*/ if (!fname[0]) break; unsigned long hash=0; wchar_t version[100]; hash=CalcFileFNV(fname); if (!hash) break; DWORD dllBuild; DWORD dllVer=GetFileVersion(fname,&dllBuild); if (!dllVer) break; if (dllVer>(DWORD)((v1<<24)|(v2<<16)|v3)) break; Sprintf(version,_countof(version),L"%d.%d.%d.%d%s",dllVer>>24,(dllVer>>16)&255,dllVer&65535,dllBuild,bBasic?L"*":L""); Sprintf(token,_countof(token),L"%08X",hash); languages[language].strings[IDS_LNG_CRC]=token; languages[language].strings[IDS_LNG_VERSION]=version; str=GetToken(str,token,_countof(token),L"\r\n"); languages[language].strings[IDS_LNG_URL]=token; } if (!res) return 1; // look for {FLAG: str=str0; while (1) { str=wcsstr(str,L"{FLAG: "); res=true; if (!str) break; res=false; str+=7; str=GetToken(str,token,_countof(token),L"}\r\n"); int language; if (!GetLocaleInfoEx(token,LOCALE_ILANGUAGE|LOCALE_RETURN_NUMBER,(LPWSTR)&language,4)) break; str=GetToken(str,token,_countof(token),L"\r\n"); languages[language].bitmap=token; } if (!res) return 1; FILE *f=NULL; if (_wfopen_s(&f,params[2],L"wb") || !f) { return 1; } fwprintf(f,L"\xFEFF"); for (std::map::const_iterator it=languages.begin();it!=languages.end();++it) { fwprintf(f,L"/////////////////////////////////////////////////////////////////////////////\r\n"); GetLocaleInfo(it->first,LOCALE_SLANGUAGE,token,_countof(token)); fwprintf(f,L"// %s\r\n\r\n",token); fwprintf(f,L"LANGUAGE 0x%02X, 0x%X\r\n",it->first&0x3FF,it->first>>10); fwprintf(f,L"\r\nSTRINGTABLE\r\nBEGIN\r\n"); for (std::map::const_iterator it2=it->second.strings.begin();it2!=it->second.strings.end();++it2) { CString str=it2->second; str.Replace(L"\r\n",L"\\r\\n"); str.Replace(L"\"",L"\"\""); fwprintf(f,L"%4d \"%s\"\r\n",it2->first,(const wchar_t*)str); } fwprintf(f,L"END\r\n\r\n"); if (it->first==DEFAULT_LANGUAGE) { fwprintf(f,L"1 VERSIONINFO\r\n"); fwprintf(f,L" FILEVERSION %d,%d,%d,0\r\n",v1,v2,v3); fwprintf(f,L" PRODUCTVERSION %d,%d,%d,0\r\n",v1,v2,v3); fwprintf(f,L" FILEFLAGSMASK 0x17L\r\n"); fwprintf(f,L" FILEFLAGS 0x0L\r\n"); fwprintf(f,L" FILEOS 0x4L\r\n"); fwprintf(f,L" FILETYPE 0x1L\r\n"); fwprintf(f,L" FILESUBTYPE 0x0L\r\n"); fwprintf(f,L"BEGIN\r\n"); fwprintf(f,L"\tBLOCK \"StringFileInfo\"\r\n"); fwprintf(f,L"\tBEGIN\r\n"); fwprintf(f,L"\t\tBLOCK \"040904b0\"\r\n"); fwprintf(f,L"\t\tBEGIN\r\n"); fwprintf(f,L"\t\t\tVALUE \"CompanyName\", \"IvoSoft\"\r\n"); fwprintf(f,L"\t\t\tVALUE \"FileDescription\", \"Update information\"\r\n"); fwprintf(f,L"\t\t\tVALUE \"FileVersion\", \"%d, %d, %d, 0\"\r\n",v1,v2,v3); fwprintf(f,L"\t\t\tVALUE \"InternalName\", \"Update\"\r\n"); fwprintf(f,L"\t\t\tVALUE \"LegalCopyright\", \"Copyright (C) 2009-2016, Ivo Beltchev\"\r\n"); fwprintf(f,L"\t\t\tVALUE \"OriginalFilename\", \"update.ver\"\r\n"); fwprintf(f,L"\t\t\tVALUE \"ProductName\", \"Classic Shell\"\r\n"); fwprintf(f,L"\t\t\tVALUE \"ProductVersion\", \"%d, %d, %d, 0\"\r\n",v1,v2,v3); fwprintf(f,L"\t\tEND\r\n"); fwprintf(f,L"\tEND\r\n"); fwprintf(f,L"\tBLOCK \"VarFileInfo\"\r\n"); fwprintf(f,L"\tBEGIN\r\n"); fwprintf(f,L"\t\tVALUE \"Translation\", 0x409, 1200\r\n"); fwprintf(f,L"\tEND\r\n"); fwprintf(f,L"END\r\n\r\n"); for (std::map::const_iterator it3=languages.begin();it3!=languages.end();++it3) { if (!it3->second.bitmap.IsEmpty()) fwprintf(f,L"%d BITMAP \"%s\"\r\n",it3->first,(const wchar_t*)it3->second.bitmap); } fwprintf(f,L"\r\n"); } } fclose(f); return 0; } /////////////////////////////////////////////////////////////////////////////// static void UnsescapeString( wchar_t *string ) { wchar_t *dst=string; int len=Strlen(string); bool bQuoted=false; if (string[0]=='"' && string[len-1]=='"') { bQuoted=true; string[len-1]=0; if (*string) string++; } for (const wchar_t *src=string;*src;src++) { if (*src=='\\') { src++; if (!*src) break; if (*src=='t') *dst++='\t'; else if (*src=='r') *dst++='\r'; else if (*src=='n') *dst++='\n'; else *dst++=*src; } else if (*src=='"' && bQuoted) { src++; if (!*src) break; *dst++=*src; } else *dst++=*src; } *dst=0; } static void WriteString( HANDLE csv, int id, const wchar_t *string1, int len1, const wchar_t *string2, int len2, CSettingsParser &parser, int subid=INT_MAX ) { DWORD q; wchar_t buf[256]; int len3; if (subid!=INT_MAX) len3=Sprintf(buf,_countof(buf),L"%d/%d",id,subid&65535); else len3=Sprintf(buf,_countof(buf),L"%d",id); const wchar_t *comment=parser.FindSetting(buf); if (comment && _wcsicmp(comment,L"ignore")==0) return; WriteFile(csv,buf,len3*2,&q,NULL); WriteFile(csv,L"\t",2,&q,NULL); for (int i=0;iflags&=~SWP_SHOWWINDOW; } return FALSE; } static void WriteDialog( HWND parent, HANDLE csv, int id, HINSTANCE hInstance1, const DLGTEMPLATE *pTemplate1, HINSTANCE hInstance2, const DLGTEMPLATE *pTemplate2, CSettingsParser &parser ) { HWND dlg1=CreateDialogIndirect(hInstance1,pTemplate1,parent,DefaultDlgProc); if (dlg1) { HWND dlg2=pTemplate2?CreateDialogIndirect(hInstance2,pTemplate2,parent,DefaultDlgProc):NULL; wchar_t text1[1024]; wchar_t text2[1024]; GetWindowText(dlg1,text1,_countof(text1)); if (*text1) { if (dlg2) GetWindowText(dlg2,text2,_countof(text2)); else text2[0]=0; WriteString(csv,id,text1,Strlen(text1),text2,Strlen(text2),parser,0); } for (HWND child1=GetWindow(dlg1,GW_CHILD);child1;child1=GetWindow(child1,GW_HWNDNEXT)) { GetWindowText(child1,text1,_countof(text1)); if (*text1) { int childId=(int)GetWindowLong(child1,GWL_ID); HWND child2=dlg2?GetDlgItem(dlg2,childId):NULL; if (child2) GetWindowText(child2,text2,_countof(text2)); else text2[0]=0; WriteString(csv,id,text1,Strlen(text1),text2,Strlen(text2),parser,childId); } } DestroyWindow(dlg1); if (dlg2) DestroyWindow(dlg2); } } int ExtractStrings( wchar_t *const *params, int count ) { if (count<3) return 3; const wchar_t *dllName1=params[1]; const wchar_t *dllName2=NULL; const wchar_t *csvName=params[2]; if (count>3) { dllName2=csvName; csvName=params[3]; } HMODULE hDLL1=LoadLibraryEx(dllName1,NULL,LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (!hDLL1) { Printf("Failed to open %S (err: %d)\n",dllName1,GetLastError()); return 1; } HMODULE hDLL2=NULL; if (dllName2) { hDLL2=LoadLibraryEx(dllName2,NULL,LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (!hDLL2) { Printf("Failed to open %S (err: %d)\n",dllName2,GetLastError()); return 1; } } int res=1; CSettingsParser parser; parser.LoadText(L"LocComments.txt"); parser.ParseText(); HANDLE hCSV=CreateFile(csvName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if (hCSV==INVALID_HANDLE_VALUE) { Printf("Failed to write %S\n",csvName); FreeLibrary(hDLL1); if (hDLL2) FreeLibrary(hDLL2); return 1; } wchar_t title[]=L"\xFEFFID\tEnglish\tTranslation\tComment\r\n"; DWORD q; WriteFile(hCSV,title,Strlen(title)*2,&q,NULL); // copy strings for (int i=2000;i<8000;i+=16) { int id=i/16; HRSRC hResInfo=FindResource(hDLL1,MAKEINTRESOURCE(id),RT_STRING); if (!hResInfo) continue; HGLOBAL hRes=LoadResource(hDLL1,hResInfo); void *pRes=LockResource(hRes); if (!pRes) continue; const WORD *data=(WORD*)pRes; for (int j=0;j<16;j++) { int len=*data; if (len>0) { int stringId=(id-1)*16+j; wchar_t buf[1024]; if (!hDLL2 || !LoadString(hDLL2,stringId,buf,_countof(buf))) buf[0]=0; WriteString(hCSV,stringId,(const wchar_t*)data+1,len,buf,Strlen(buf),parser); } data+=len+1; } } HWND parent=CreateWindow(L"button",NULL,WS_POPUP,0,0,10,10,NULL,NULL,NULL,NULL); // copy dialogs for (int id=2000;id<7000;id++) { if (id>=2010 && id<3000) continue; if (id>=3010 && id<4000) continue; if (id>=4010 && id<5000) continue; if (id>=5010 && id<6000) continue; if (id>=6010 && id<7000) continue; HRSRC hResInfo1=FindResource(hDLL1,MAKEINTRESOURCE(id),RT_DIALOG); if (!hResInfo1) continue; void *pRes1=LockResource(LoadResource(hDLL1,hResInfo1)); if (!pRes1) continue; void *pRes2=NULL; if (hDLL2) { HRSRC hResInfo2=FindResource(hDLL2,MAKEINTRESOURCE(id),RT_DIALOG); if (hResInfo2) pRes2=LockResource(LoadResource(hDLL2,hResInfo2)); } WriteDialog(parent,hCSV,id,hDLL1,(DLGTEMPLATE*)pRes1,hDLL2,(DLGTEMPLATE*)pRes2,parser); } // additional strings { HRSRC hResInfo=FindResource(hDLL1,MAKEINTRESOURCE(1),L"L10N"); if (hResInfo) { HGLOBAL hRes=LoadResource(hDLL1,hResInfo); const wchar_t *pRes=(wchar_t*)LockResource(hRes); if (pRes) { int size=SizeofResource(hDLL1,hResInfo)/2; if (*pRes==L'\xFEFF') pRes++, size--; wchar_t *pBuf=new wchar_t[size+1]; memcpy(pBuf,pRes,size*2); pBuf[size]=0; for (int i=0;i> &oldStrings=*(std::vector>*)lParam; oldStrings.push_back(std::pair(PtrToInt(lpszName),wIDLanguage)); } return TRUE; } static BOOL CALLBACK EnumResNameProc( HMODULE hModule, LPCTSTR lpszType, LPTSTR lpszName, LONG_PTR lParam ) { EnumResourceLanguages(hModule,lpszType,lpszName,EnumResLangProc,lParam); return TRUE; } static int ImportStrings( wchar_t *const *params, int count ) { if (count<3) return 3; const wchar_t *dllName=params[1]; const wchar_t *csvName=params[2]; HANDLE hCSV=CreateFile(csvName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if (hCSV==INVALID_HANDLE_VALUE) { Printf("Failed to read %S\n",csvName); return 1; } int size=SetFilePointer(hCSV,0,NULL,FILE_END)/2; SetFilePointer(hCSV,0,NULL,FILE_BEGIN); std::vector buf(size+1); DWORD q; ReadFile(hCSV,&buf[0],size*2,&q,NULL); CloseHandle(hCSV); buf[size]=0; for (int i=0;i lines; wchar_t *str=&buf[0]; int min=100000, max=0; while (str<&buf[size]) { int len=Strlen(str); wchar_t *next=str+len+1; wchar_t *tab=wcschr(str,'\t'); if (tab) { *tab=0; int id=_wtol(str); bool bNumber=id>0; for (;*str;str++) if (*str<'0' || *str>'9') { bNumber=false; break; } if (bNumber) { tab=wcschr(tab+1,'\t'); if (tab) { str=tab+1; tab=wcschr(str,'\t'); if (tab) *tab=0; if (lines.find(id)!=lines.end()) { Printf("Duplicate string ID %d\n",id); return 1; } UnsescapeString(str); lines[id]=str; if (min>id) min=id; if (max> oldStrings; EnumResourceNames(hDll,RT_STRING,EnumResNameProc,(LONG_PTR)&oldStrings); FreeLibrary(hDll); HANDLE hUpdate=BeginUpdateResource(dllName,FALSE); if (!hUpdate) { Printf("Failed to open %S (err: %d)\n",dllName,GetLastError()); return 1; } // delete all string resources for (int i=0;i<(int)oldStrings.size();i++) { UpdateResource(hUpdate,RT_STRING,MAKEINTRESOURCE(oldStrings[i].first),oldStrings[i].second,NULL,0); } // add new string lines max+=32; for (int i=min;i res; for (int j=0;j<16;j++,idx++) { std::map::const_iterator it=lines.find(idx); const wchar_t *str=L""; if (it!=lines.end()) str=it->second; int len=Strlen(str); res.push_back((wchar_t)len); for (int c=0;c16) UpdateResource(hUpdate,RT_STRING,MAKEINTRESOURCE(id),LANG_NEUTRAL,&res[0],res.size()*2); } if (!EndUpdateResource(hUpdate,FALSE)) { Printf("Failed to update %S (err: %d)\n",dllName,GetLastError()); return 1; } return 0; } #endif /////////////////////////////////////////////////////////////////////////////// static HRESULT CALLBACK TaskDialogCallback( HWND hwnd, UINT uNotification, WPARAM wParam, LPARAM lParam, LONG_PTR dwRefData ) { if (uNotification==TDN_HYPERLINK_CLICKED) { const wchar_t *link=(const wchar_t*)lParam; ShellExecute(hwnd,NULL,link,NULL,NULL,SW_SHOWNORMAL); } return S_OK; } /////////////////////////////////////////////////////////////////////////////// // Classic Shell utility - multiple utilities for building and maintaining Classic Shell // Usage: // no parameters - saves a troubleshooting log // crcmsi // creates a file with checksum of both msi files // makeEN // extracts the localization resources and creates a sample en-US.DLL // extract // extracts the string table, the dialog text, and the L10N text from a DLL and stores it in a CSV // extract en-us.dll // extracts the string table, the dialog text, and the L10N text from two DLL and stores it in a CSV // import // replaces the string table in the DLL with the text from the CSV // update // generates a resource file for UpdateBin.dll by calculating the DLL hashes int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpstrCmdLine, int nCmdShow ) { g_hInstance=hInstance; if (!*lpstrCmdLine) { #ifndef _WIN64 DWORD winVer=GetVersionEx(GetModuleHandle(L"user32.dll")); TASKDIALOG_BUTTON taskButtons[4]={ {101,L"Save system log\nSaves detailed information about your system for troubleshooting purposes."}, {102,L"Classic Shell logging options\nEnable or disable various logging options."}, {103,L"Remove Classic Shell\nManually removes Classic Shell from your system. Use only if the conventional methods for uninstallation do not work."}, {104,L"Show Metro colors\nSee the color palette that can be used in start menu skins."}, }; TASKDIALOGCONFIG tasks={sizeof(tasks)}; tasks.dwFlags=TDF_USE_HICON_MAIN|TDF_USE_COMMAND_LINKS|TDF_ENABLE_HYPERLINKS; tasks.dwCommonButtons=TDCBF_CANCEL_BUTTON; tasks.pszWindowTitle=L"Classic Shell Utility"; tasks.hMainIcon=LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON1)); tasks.pszMainInstruction=L"Select task to perform"; tasks.cButtons=HIWORD(winVer)>=0x0602?4:3; tasks.pButtons=taskButtons; tasks.pszFooter=L"www.classicshell.net"; tasks.pfCallback=TaskDialogCallback; int seleciton; if (TaskDialogIndirect(&tasks,&seleciton,NULL,NULL)==IDCANCEL) return 0; if (seleciton==101) { ShowSaveLogFile(); } else if (seleciton==102) { EditLoggingOptions(); } else if (seleciton==103) { wchar_t exe[_MAX_PATH]; GetModuleFileName(NULL,exe,_countof(exe)); CoInitialize(NULL); ShellExecute(NULL,L"runas",exe,L"uninstall",NULL,SW_SHOWNORMAL); CoUninitialize(); } else if (seleciton==104) { ShowMetroColorViewer(); } #endif return 0; } int count; wchar_t *const *params=CommandLineToArgvW(lpstrCmdLine,&count); #ifndef _WIN64 if (_wcsicmp(params[0],L"crcmsi")==0) { return CalcMsiChecksum(params,count); } if (_wcsicmp(params[0],L"makeEN")==0) { return MakeEnglishDll(params,count); } if (_wcsicmp(params[0],L"extract")==0) { return ExtractStrings(params,count); } if (_wcsicmp(params[0],L"import")==0) { return ImportStrings(params,count); } if (_wcsicmp(params[0],L"update")==0) { return GenerateUpdateFile(params,count); } if (_wcsicmp(params[0],L"colors")==0) { ShowMetroColorViewer(); return 0; } if (_wcsicmp(params[0],L"uninstall")==0) { ManualUninstall(); return 0; } #endif if (_wcsicmp(params[0],L"savelog")==0 || _wcsicmp(params[0],L"saveloga")==0) { if (count<=1) return 1; return SaveLogFile(params[1],_wcsicmp(params[0],L"saveloga")==0); } return 1; }