From d80b364e1dbb550b7c12d8365baf34c8887cc412 Mon Sep 17 00:00:00 2001 From: anders_k Date: Fri, 12 May 2017 00:19:23 +0000 Subject: [PATCH] TCHAR cannot be used in RegTool because it must be able to run on Win9x. RegTool now implements A and W functions in the same binary. Bitness mixing is still problematic. git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6858 212acab6-be3b-0410-9dea-997c60f758d6 --- Contrib/Library/RegTool/RegTool.c | 382 ------------------------- Contrib/Library/RegTool/RegTool.cpp | 422 ++++++++++++++++++++++++++++ Contrib/Library/RegTool/SConscript | 2 +- Docs/src/history.but | 2 + Include/Library.nsh | 10 + Include/UpgradeDLL.nsh | 10 + 6 files changed, 445 insertions(+), 383 deletions(-) delete mode 100644 Contrib/Library/RegTool/RegTool.c create mode 100644 Contrib/Library/RegTool/RegTool.cpp diff --git a/Contrib/Library/RegTool/RegTool.c b/Contrib/Library/RegTool/RegTool.c deleted file mode 100644 index 450165be..00000000 --- a/Contrib/Library/RegTool/RegTool.c +++ /dev/null @@ -1,382 +0,0 @@ -// Unicode support by Jim Park & Olivier Marcoux - -#include "../../../Source/Platform.h" -#include - -#ifndef _CRT_STRINGIZE -#define __CRT_STRINGIZE(_Value) #_Value -#define _CRT_STRINGIZE(_Value) __CRT_STRINGIZE(_Value) -#endif /* _CRT_STRINGIZE */ - -#define STR_SIZE 1024 - -void RegFile(TCHAR cmd, TCHAR *file, int x64); -void RegDll(TCHAR *file); -void RegTypeLib(TCHAR *file); -BOOL DeleteFileOnReboot(TCHAR *pszFile); - -NSIS_ENTRYPOINT_GUINOCRT -EXTERN_C void NSISWinMainNOCRT() -{ - TCHAR *cmdline; - TCHAR seekchar = _T(' '); - - cmdline = GetCommandLine(); - if (*cmdline == _T('\"')) - seekchar = *cmdline++; - - while (*cmdline && *cmdline != seekchar) - cmdline = CharNext(cmdline); - cmdline = CharNext(cmdline); - while (*cmdline == _T(' ')) - cmdline++; - - if (*cmdline++ != _T('/')) - { - ExitProcess(1); - } - - if (*cmdline == _T('S')) - { - HKEY rootkey; - TCHAR *keyname, *file; // These are turned into heap memory to avoid _chkstk - keyname = (TCHAR*) GlobalAlloc(GPTR, STR_SIZE*sizeof(TCHAR)); - file = (TCHAR*) GlobalAlloc(GPTR, STR_SIZE*sizeof(TCHAR)); - - if (SUCCEEDED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\NSIS.Library.RegTool.v3"), 0, KEY_READ, &rootkey))) - { - while (RegEnumKey(rootkey, 0, keyname, STR_SIZE) == ERROR_SUCCESS) - { - HKEY key; - - if (SUCCEEDED(RegOpenKeyEx(rootkey, keyname, 0, KEY_READ, &key))) - { - DWORD t, count, l = sizeof(DWORD); - - if (SUCCEEDED(RegQueryValueEx(key, _T("count"), NULL, &t, (LPBYTE) &count, &l)) && t == REG_DWORD) - { - DWORD j; - TCHAR valname[128], mode[3]; - - for (j = 1; j <= count; j++) - { - wsprintf(valname, _T("%u.mode"), j); - l = sizeof(mode); - if (FAILED(RegQueryValueEx(key, valname, NULL, &t, (LPBYTE) mode, &l)) || t != REG_SZ) - continue; - - wsprintf(valname, _T("%u.file"), j); - l = STR_SIZE*sizeof(TCHAR); - if (FAILED(RegQueryValueEx(key, valname, NULL, &t, (LPBYTE) file, &l)) || t != REG_SZ) - continue; - - // JP: Note, if this mode[1] is used as anything but a boolean later on, - // we'll need to consider the next line carefully. - RegFile(mode[0], file, mode[1] == 'X'); - } - } - - RegCloseKey(key); - RegDeleteKey(rootkey, keyname); - } - } - - RegCloseKey(rootkey); - RegDeleteKey(HKEY_LOCAL_MACHINE, _T("Software\\NSIS.Library.RegTool.v3")); - } - - { - if (GetModuleFileName(GetModuleHandle(NULL), file, STR_SIZE)) - { - DeleteFileOnReboot(file); - } - } - GlobalFree(keyname); - GlobalFree(file); - } - else - { - SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); - OleInitialize(NULL); - - if (*cmdline == _T('D')) - { - RegDll(cmdline + 1); - } - else if (*cmdline == _T('T')) - { - RegTypeLib(cmdline + 1); - } - - OleUninitialize(); - SetErrorMode(0); - } - - ExitProcess(0); -} - -void SafeWow64EnableWow64FsRedirection(BOOL Wow64FsEnableRedirection) -{ - HMODULE kernel = GetModuleHandle(_T("kernel32")); - if (kernel) - { - FARPROC proc = GetProcAddress(kernel, "Wow64EnableWow64FsRedirection"); - if (proc) - { - typedef BOOL (WINAPI *Wow64EnableWow64FsRedirectionPtr)(BOOL); - Wow64EnableWow64FsRedirectionPtr Wow64EnableWow64FsRedirectionFunc = - (Wow64EnableWow64FsRedirectionPtr) proc; - - Wow64EnableWow64FsRedirectionFunc(Wow64FsEnableRedirection); - } - } -} - -void RegFile(TCHAR cmd, TCHAR *file, int x64) -{ - TCHAR* self; // These are turned into heap memory to avoid _chkstk - TCHAR* cmdline; - - int ready = 0; - - if (!*file || (cmd != _T('D') && cmd != _T('T') && cmd != _T('E'))) - return; - - self = (TCHAR*) GlobalAlloc(GPTR, sizeof(TCHAR)*STR_SIZE); - cmdline = (TCHAR*) GlobalAlloc(GPTR, sizeof(TCHAR)*STR_SIZE); - - if (cmd == _T('E')) - { - wsprintf(cmdline, _T("\"%s\" /regserver"), file); - ready++; - } - else if (!x64) - { - if (GetModuleFileName(GetModuleHandle(NULL), self, STR_SIZE)) - { - wsprintf(cmdline, _T("\"%s\" /%c%s"), self, cmd, file); - ready++; - } - } - else - { - if (GetSystemDirectory(self, STR_SIZE)) - { - wsprintf(cmdline, _T("\"%s\\regsvr32.exe\" /s \"%s\""), self, file); - ready++; - - SafeWow64EnableWow64FsRedirection(FALSE); - } - } - - if (ready) - { - PROCESS_INFORMATION pi; - STARTUPINFO si = { sizeof(STARTUPINFO) }; - - if (CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) - { - CloseHandle(pi.hThread); - - WaitForSingleObject(pi.hProcess, INFINITE); - - CloseHandle(pi.hProcess); - } - - if (x64) - { - SafeWow64EnableWow64FsRedirection(TRUE); - } - } - - GlobalFree(self); - GlobalFree(cmdline); -} - -void RegDll(TCHAR *file) -{ - HMODULE mod = LoadLibraryEx(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - if (mod) - { - FARPROC regfunc = GetProcAddress(mod, "DllRegisterServer"); - if (regfunc) - regfunc(); - FreeLibrary(mod); - } -} - -void RegTypeLib(TCHAR *file) -{ -#ifdef _UNICODE - WCHAR* wfile = file; -#else - WCHAR wfile[STR_SIZE]; - if (MultiByteToWideChar(CP_ACP, 0, file, -1, wfile, STR_SIZE) == 0) - return; -#endif - { - ITypeLib* tlib; - if (SUCCEEDED(LoadTypeLib(wfile, &tlib))) { - RegisterTypeLib(tlib, wfile, NULL); - tlib->lpVtbl->Release(tlib); - } - } -} - -char *mystrstriA(char *a, const char *b) -{ - int l = lstrlenA(b); - while (lstrlenA(a) >= l) - { - char c = a[l]; - a[l] = 0; - if (!lstrcmpiA(a, b)) - { - a[l] = c; - return a; - } - a[l] = c; - a = CharNextA(a); - } - return NULL; -} - -void mini_memcpy(void *out, const void *in, int len) -{ - char *c_out=(char*)out; - char *c_in=(char *)in; - while (len-- > 0) - { - *c_out++=*c_in++; - } -} - -HANDLE myOpenFile(const TCHAR *fn, DWORD da, DWORD cd) -{ - int attr = GetFileAttributes(fn); - return CreateFile( - fn, - da, - FILE_SHARE_READ, - NULL, - cd, - attr == INVALID_FILE_ATTRIBUTES ? 0 : attr, - NULL - ); -} - -#ifndef _WIN64 -/** Modifies the wininit.ini file to rename / delete a file. - * - * @param prevName The previous / current name of the file. - * @param newName The new name to move the file to. If NULL, the current file - * will be deleted. - */ -void RenameViaWininit(const TCHAR* prevName, const TCHAR* newName) -{ - static char szRenameLine[1024]; - static TCHAR wininit[1024]; - static TCHAR tmpbuf[1024]; - - int cchRenameLine; - LPCSTR szRenameSec = "[Rename]\r\n"; // rename section marker - HANDLE hfile; - DWORD dwFileSize; - DWORD dwBytes; - DWORD dwRenameLinePos; - char *pszWinInit; // Contains the file contents of wininit.ini - - int spn; // length of the short path name in TCHARs. - - lstrcpy(tmpbuf, _T("NUL")); - - if (newName) { - // create the file if it's not already there to prevent GetShortPathName from failing - CloseHandle(myOpenFile(newName,0,CREATE_NEW)); - spn = GetShortPathName(newName,tmpbuf,1024); - if (!spn || spn > 1024) - return; - } - // wininit is used as a temporary here - spn = GetShortPathName(prevName,wininit,1024); - if (!spn || spn > 1024) - return; -#ifdef _UNICODE - cchRenameLine = wsprintfA(szRenameLine, "%ls=l%s\r\n", tmpbuf, wininit); -#else - cchRenameLine = wsprintfA(szRenameLine, "%s=%s\r\n", tmpbuf, wininit); -#endif - // Get the path to the wininit.ini file. - GetWindowsDirectory(wininit, 1024-16); - lstrcat(wininit, _T("\\wininit.ini")); - - hfile = myOpenFile(wininit, GENERIC_READ | GENERIC_WRITE, OPEN_ALWAYS); - - if (hfile != INVALID_HANDLE_VALUE) - { - // We are now working on the Windows wininit file - dwFileSize = GetFileSize(hfile, NULL); - pszWinInit = (char*) GlobalAlloc(GPTR, dwFileSize + cchRenameLine + 10); - - if (pszWinInit != NULL) - { - if (ReadFile(hfile, pszWinInit, dwFileSize, &dwBytes, NULL) && dwFileSize == dwBytes) - { - // Look for the rename section in the current file. - LPSTR pszRenameSecInFile = mystrstriA(pszWinInit, szRenameSec); - if (pszRenameSecInFile == NULL) - { - // No rename section. So we add it to the end of file. - lstrcpyA(pszWinInit+dwFileSize, szRenameSec); - dwFileSize += 10; - dwRenameLinePos = dwFileSize; - } - else - { - // There is a rename section, but is there another section after it? - char *pszFirstRenameLine = pszRenameSecInFile+10; - char *pszNextSec = mystrstriA(pszFirstRenameLine,"\n["); - if (pszNextSec) - { - char *p = pszWinInit + dwFileSize; - char *pEnd = pszWinInit + dwFileSize + cchRenameLine; - - while (p > pszNextSec) - { - *pEnd-- = *p--; - } - - dwRenameLinePos = BUGBUG64TRUNCATE(DWORD, pszNextSec - pszWinInit) + 1; // +1 for the \n - } - // rename section is last, stick item at end of file - else dwRenameLinePos = dwFileSize; - } - - mini_memcpy(&pszWinInit[dwRenameLinePos], szRenameLine, cchRenameLine); - dwFileSize += cchRenameLine; - - SetFilePointer(hfile, 0, NULL, FILE_BEGIN); - WriteFile(hfile, pszWinInit, dwFileSize, &dwBytes, NULL); - - GlobalFree(pszWinInit); - } - } - - CloseHandle(hfile); - } -} -#endif - -BOOL DeleteFileOnReboot(TCHAR *pszFile) -{ - BOOL fOk = - MoveFileEx(pszFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); -#ifndef _WIN64 - if (!fOk) - { - RenameViaWininit(pszFile, NULL); - fOk = TRUE; // BUGBUG: We just pretend everything is OK, nobody checks our return value anyway - } -#endif - return fOk; -} diff --git a/Contrib/Library/RegTool/RegTool.cpp b/Contrib/Library/RegTool/RegTool.cpp new file mode 100644 index 00000000..c1ae72f9 --- /dev/null +++ b/Contrib/Library/RegTool/RegTool.cpp @@ -0,0 +1,422 @@ +// RegTool v3 +// Unicode support by Jim Park & Olivier Marcoux + +#include "../../../Source/Platform.h" +#include + +#define STR_SIZE 1024 + + +/* +All A/W functions need this ugly hack so we can call them in template functions. +Everything is implemented in template functions because the x86 version needs to +call the W functions on NT and the A functions on 9x. +The macros assume that "T" is a [W]CHAR type. +*/ +#define MKSTR(str) ( sizeof(T) > 1 ? (const T*) L##str : (const T*) str ) +#ifdef UNICODE +#define CALL(func) hack::func##W +#else +#define CALL(func) hack::func##A +#endif +#define CALL_wsprintf (sizeof(T) > 1 ? (int(*)(T*,const T*,...)) wsprintfW : (int(*)(T*,const T*,...)) wsprintfA) +namespace hack { +// Allow cast from [const] T* to [W]CHAR* but nothing else (losing constness is acceptable). +inline WCHAR* WP(const WCHAR*p) { return (WCHAR*) p; } +inline WCHAR* WP(const CHAR*p) { return (WCHAR*) p; } +inline CHAR* NP(const WCHAR*p) { return (CHAR*) p; } +inline CHAR* NP(const CHAR*p) { return (CHAR*) p; } +// These function names rely on the C preprocessor & PSDK macros to append A/W to their name so they can be found by the CALL macro. +template T* GetCommandLine() { return sizeof(T) > 1 ? (T*) ::GetCommandLineW() : (T*) ::GetCommandLineA(); } +template T* CharNext(const T*p1) { return sizeof(T) > 1 ? (T*) ::CharNextW(WP(p1)) : (T*) ::CharNextA(NP(p1)); } +template T* lstrcpy(T*p1,const T*p2) { return sizeof(T) > 1 ? (T*) ::lstrcpyW(WP(p1),WP(p2)) : (T*) ::lstrcpyA(NP(p1),NP(p2)); } +template T* lstrcat(T*p1,const T*p2) { return sizeof(T) > 1 ? (T*) ::lstrcatW(WP(p1),WP(p2)) : (T*) ::lstrcatA(NP(p1),NP(p2)); } +template LONG RegEnumKey(HKEY p1, DWORD p2, T*p3,DWORD p4) { return sizeof(T) > 1 ? ::RegEnumKeyW(p1,p2,WP(p3),p4) : ::RegEnumKeyA(p1,p2,NP(p3),p4); } +template LONG RegOpenKeyEx(HKEY p1,const T*p2,DWORD p3,REGSAM p4,PHKEY p5) { return sizeof(T) > 1 ? ::RegOpenKeyExW(p1,WP(p2),p3,p4,p5) : ::RegOpenKeyExA(p1,NP(p2),p3,p4,p5); } +template LONG RegQueryValueEx(HKEY p1,const T*p2,LPDWORD p3,LPDWORD p4,LPBYTE p5,LPDWORD p6) { return sizeof(T) > 1 ? ::RegQueryValueExW(p1,WP(p2),p3,p4,p5,p6) : ::RegQueryValueExA(p1,NP(p2),p3,p4,p5,p6); } +template LONG RegDeleteKey(HKEY p1,const T*p2) { return sizeof(T) > 1 ? ::RegDeleteKeyW(p1,WP(p2)) : ::RegDeleteKeyA(p1,NP(p2)); } +template DWORD GetModuleFileName(HMODULE p1,T*p2,DWORD p3) { return sizeof(T) > 1 ? ::GetModuleFileNameW(p1,WP(p2),p3) : ::GetModuleFileNameA(p1,NP(p2),p3); } +template HMODULE LoadLibraryEx(const T*p1,void*p2,DWORD p3) { return sizeof(T) > 1 ? ::LoadLibraryExW(WP(p1),p2,p3) : ::LoadLibraryExA(NP(p1),p2,p3); } +template UINT GetWindowsDirectory(const T*p1,UINT p2) { return sizeof(T) > 1 ? ::GetWindowsDirectoryW(WP(p1),p2) : ::GetWindowsDirectoryA(NP(p1),p2); } +template UINT GetSystemDirectory(const T*p1,UINT p2) { return sizeof(T) > 1 ? ::GetSystemDirectoryW(WP(p1),p2) : ::GetSystemDirectoryA(NP(p1),p2); } +template DWORD GetShortPathName(const T*p1,const T*p2,DWORD p3) { return sizeof(T) > 1 ? ::GetShortPathNameW(WP(p1),WP(p2),p3) : ::GetShortPathNameA(NP(p1),NP(p2),p3); } +template DWORD GetFileAttributes(const T*p1) { return sizeof(T) > 1 ? ::GetFileAttributesW(WP(p1)) : ::GetFileAttributesA(NP(p1)); } +template BOOL MoveFileEx(const T*p1,const T*p2,DWORD p3) { return sizeof(T) > 1 ? ::MoveFileExW(WP(p1),WP(p2),p3) : ::MoveFileExA(NP(p1),NP(p2),p3); } +template HANDLE CreateFile(const T*p1,DWORD p2,DWORD p3,LPSECURITY_ATTRIBUTES p4,DWORD p5,DWORD p6,HANDLE p7) { return sizeof(T) > 1 ? ::CreateFileW(WP(p1),p2,p3,p4,p5,p6,p7) : ::CreateFileA(NP(p1),p2,p3,p4,p5,p6,p7); } +template BOOL CreateProcess(const T*p1,const T*p2,LPSECURITY_ATTRIBUTES p3,LPSECURITY_ATTRIBUTES p4,BOOL p5,DWORD p6,LPVOID p7,const T*p8,STARTUPINFO*p9,LPPROCESS_INFORMATION p10) { return sizeof(T) > 1 ? ::CreateProcessW(WP(p1),WP(p2),p3,p4,p5,p6,p7,WP(p8),(STARTUPINFOW*)p9,p10) : ::CreateProcessA(NP(p1),NP(p2),p3,p4,p5,p6,p7,NP(p8),(STARTUPINFOA*)p9,p10); } +} + + +static bool IsWinNT() +{ +#ifdef _WIN64 + return true; +#else + LPCWSTR str = L"count"; // Using this string because it's already used in other parts of the code + return CharNextW(str) != NULL; +#endif +} + +void SafeWow64EnableWow64FsRedirection(BYTE EnableFsRedirection) +{ +#ifndef _WIN64 + HMODULE hK32 = LoadLibraryA("KERNEL32"); + FARPROC proc = GetProcAddress(hK32, "Wow64EnableWow64FsRedirection"); + if (proc) + { + typedef BYTE WINNTBOOLEAN; + typedef WINNTBOOLEAN (WINAPI*W64EW64FSR)(WINNTBOOLEAN); + W64EW64FSR Wow64EnableWow64FsRedirectionFunc = (W64EW64FSR) proc; + Wow64EnableWow64FsRedirectionFunc(EnableFsRedirection); + } +#endif +} + +char *mystrstriA(char *a, const char *b) +{ + int l = lstrlenA(b); + while (lstrlenA(a) >= l) + { + char c = a[l]; + a[l] = 0; + if (!lstrcmpiA(a, b)) + { + a[l] = c; + return a; + } + a[l] = c; + a = CharNextA(a); + } + return NULL; +} + +void mini_memcpy(void *out, const void *in, int len) +{ + char *c_out=(char*)out; + char *c_in=(char *)in; + while (len-- > 0) + { + *c_out++=*c_in++; + } +} + +template static HANDLE myOpenFile(const T *fn, DWORD da, DWORD cd) +{ + DWORD attr = CALL(GetFileAttributes)(fn), share = FILE_SHARE_READ; + return CALL(CreateFile)(fn, da, share, NULL, cd, attr == INVALID_FILE_ATTRIBUTES ? 0 : attr, NULL); +} + +#ifndef _WIN64 +/** Modifies the wininit.ini file to rename / delete a file. + * + * @param prevName The previous / current name of the file. + * @param newName The new name to move the file to. If NULL, the current file + * will be deleted. + */ +template void RenameViaWininit(const T* prevName, const T* newName) // Note: Not thread safe! +{ + static char szRenameLine[1024]; + static T wininit[1024]; + static T tmpbuf[1024]; + + int cchRenameLine; + LPCSTR szRenameSec = "[Rename]\r\n"; // rename section marker + HANDLE hfile; + DWORD dwFileSize; + DWORD dwBytes, dwRenameLinePos; + char *pszWinInit; // Contains the file contents of wininit.ini + + int spn; // length of the short path name in TCHARs. + + CALL(lstrcpy)(tmpbuf, MKSTR("NUL")); + + if (newName) { + // create the file if it's not already there to prevent GetShortPathName from failing + CloseHandle(myOpenFile(newName,0,CREATE_NEW)); + spn = CALL(GetShortPathName)(newName,tmpbuf,COUNTOF(tmpbuf)); + if (!spn || spn > 1024) + return; + } + // wininit is used as a temporary here + spn = CALL(GetShortPathName)(prevName,wininit,COUNTOF(wininit)); + if (!spn || spn > 1024) + return; +#ifdef _UNICODE + cchRenameLine = wsprintfA(szRenameLine, "%S=%S\r\n", tmpbuf, wininit); +#else + cchRenameLine = wsprintfA(szRenameLine, "%s=%s\r\n", tmpbuf, wininit); +#endif + // Get the path to the wininit.ini file. + CALL(GetWindowsDirectory)(wininit, COUNTOF(wininit)-16); + CALL(lstrcat)(wininit, MKSTR("\\wininit.ini")); + + hfile = myOpenFile(wininit, GENERIC_READ | GENERIC_WRITE, OPEN_ALWAYS); + + if (hfile != INVALID_HANDLE_VALUE) + { + // We are now working on the Windows wininit file + dwFileSize = GetFileSize(hfile, NULL); + pszWinInit = (char*) GlobalAlloc(GPTR, dwFileSize + cchRenameLine + 10); + + if (pszWinInit != NULL) + { + if (ReadFile(hfile, pszWinInit, dwFileSize, &dwBytes, NULL) && dwFileSize == dwBytes) + { + // Look for the rename section in the current file. + LPSTR pszRenameSecInFile = mystrstriA(pszWinInit, szRenameSec); + if (pszRenameSecInFile == NULL) + { + // No rename section. So we add it to the end of file. + lstrcpyA(pszWinInit+dwFileSize, szRenameSec); + dwFileSize += 10; + dwRenameLinePos = dwFileSize; + } + else + { + // There is a rename section, but is there another section after it? + char *pszFirstRenameLine = pszRenameSecInFile+10; + char *pszNextSec = mystrstriA(pszFirstRenameLine,"\n["); + if (pszNextSec) + { + char *p = pszWinInit + dwFileSize; + char *pEnd = pszWinInit + dwFileSize + cchRenameLine; + + while (p > pszNextSec) + { + *pEnd-- = *p--; + } + + dwRenameLinePos = BUGBUG64TRUNCATE(DWORD, pszNextSec - pszWinInit) + 1; // +1 for the \n + } + // rename section is last, stick item at end of file + else dwRenameLinePos = dwFileSize; + } + + mini_memcpy(&pszWinInit[dwRenameLinePos], szRenameLine, cchRenameLine); + dwFileSize += cchRenameLine; + + SetFilePointer(hfile, 0, NULL, FILE_BEGIN); + WriteFile(hfile, pszWinInit, dwFileSize, &dwBytes, NULL); + + GlobalFree(pszWinInit); + } + } + + CloseHandle(hfile); + } +} +#endif + +template BOOL DeleteFileOnReboot(const T *pszFile) +{ + BOOL fOk = CALL(MoveFileEx)(pszFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); +#ifndef _WIN64 + if (!fOk && sizeof(T) == 1) + { + RenameViaWininit(pszFile, (const T*)NULL); + fOk = TRUE; // BUGBUG: We just pretend everything is OK, nobody checks our return value anyway + } +#endif + return fOk; +} + +template void RegFile(T cmd, const T *file, BOOL x64) +{ + T *self; // These are allocated on the heap to avoid _chkstk + T *cmdline; + int ready = 0; + + if (!*file || (cmd != _T('D') && cmd != _T('T') && cmd != _T('E'))) + return; + + self = (T*) GlobalAlloc(GPTR, sizeof(T)*STR_SIZE); + cmdline = (T*) GlobalAlloc(GPTR, sizeof(T)*STR_SIZE); + + if (cmd == ('E')) + { + CALL_wsprintf(cmdline, MKSTR("\"%s\" /regserver"), file); + ready++; + } + else if (!x64) + { + if (CALL(GetModuleFileName)(GetModuleHandle(NULL), self, STR_SIZE)) + { + CALL_wsprintf(cmdline, MKSTR("\"%s\" /%c%s"), self, cmd, file); + ready++; + } + } + else + { + if (CALL(GetSystemDirectory)(self, STR_SIZE)) + { + CALL_wsprintf(cmdline, MKSTR("\"%s\\regsvr32.exe\" /s \"%s\""), self, file); + ready++; + + SafeWow64EnableWow64FsRedirection(FALSE); + } + } + + if (ready) + { + PROCESS_INFORMATION pi; + BYTE sibuf[sizeof(T) > 1 ? sizeof(STARTUPINFOW) : sizeof(STARTUPINFOA)] = {0,}; + STARTUPINFO &si = (STARTUPINFO&) sibuf; + si.cb = sizeof(sibuf); + + if (CALL(CreateProcess)(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) + { + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } + + if (x64) + { + SafeWow64EnableWow64FsRedirection(TRUE); + } + } + + GlobalFree(self); + GlobalFree(cmdline); +} + +template void RegDll(const T *file) +{ + HMODULE mod = CALL(LoadLibraryEx)(file, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + if (mod) + { + FARPROC regfunc = GetProcAddress(mod, "DllRegisterServer"); + if (regfunc) + regfunc(); + FreeLibrary(mod); + } +} + +template void RegTypeLib(T *file) +{ + WCHAR wbuf[sizeof(T) > 1 ? 1 : STR_SIZE]; // Buffer only used by ANSI implementation! + WCHAR *wfile = wbuf; // Not const because of RegisterTypeLib in old PSDK + if (sizeof(T) > 1) + wfile = (WCHAR*) file; + else + { + if (MultiByteToWideChar(CP_ACP, 0, (const CHAR*) file, -1, wbuf, STR_SIZE) == 0) + return; + } + + ITypeLib* tlib; + if (SUCCEEDED(LoadTypeLib(wfile, &tlib))) + { + RegisterTypeLib(tlib, wfile, NULL); + tlib->Release(); + } +} + +template int RegTool() +{ + T *cmdline; + T seekchar = (' '); + + cmdline = CALL(GetCommandLine)(); + if (*cmdline == ('\"')) + seekchar = *cmdline++; + + while (*cmdline && *cmdline != seekchar) + cmdline = CALL(CharNext)(cmdline); + cmdline = CALL(CharNext)(cmdline); + while (*cmdline == (' ')) + cmdline++; + + if (*cmdline++ != ('/')) + { + return 1; + } + + if (*cmdline == ('S')) + { + HKEY rootkey; + T *keyname, *file; // These are allocated on the heap to avoid _chkstk + keyname = (T*) GlobalAlloc(GPTR, STR_SIZE*sizeof(T)); + file = (T*) GlobalAlloc(GPTR, STR_SIZE*sizeof(T)); + + if (SUCCEEDED(RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\NSIS.Library.RegTool.v3", 0, KEY_READ, &rootkey))) + { + DWORD keyidx = 0; + while (CALL(RegEnumKey)(rootkey, keyidx, keyname, STR_SIZE) == ERROR_SUCCESS) + { + HKEY key; + + if (SUCCEEDED(CALL(RegOpenKeyEx)(rootkey, keyname, 0, KEY_READ, &key))) + { + DWORD t, count, l = sizeof(DWORD); + + if (SUCCEEDED(CALL(RegQueryValueEx)(key, MKSTR("count"), NULL, &t, (LPBYTE) &count, &l)) && t == REG_DWORD) + { + DWORD j; + T valname[128], mode[3]; + + for (j = 0; ++j <= count;) + { + CALL_wsprintf(valname, MKSTR("%u.mode"), j); + l = sizeof(mode); + if (FAILED(CALL(RegQueryValueEx)(key, valname, NULL, &t, (LPBYTE) mode, &l)) || t != REG_SZ) + continue; + + CALL_wsprintf(valname, MKSTR("%u.file"), j); + l = STR_SIZE*sizeof(T); + if (FAILED(CALL(RegQueryValueEx)(key, valname, NULL, &t, (LPBYTE) file, &l)) || t != REG_SZ) + continue; + + // JP: Note, if this mode[1] is used as anything but a boolean later on, + // we'll need to consider the next line carefully. + RegFile(mode[0], file, mode[1] == 'X'); + } + } + + RegCloseKey(key); + + CALL(RegDeleteKey)(rootkey, keyname); + } + keyidx++; // Must increment this so we don't loop forever if a non-admin accidentally executes RegTool /S + } + + RegCloseKey(rootkey); + RegDeleteKeyA(HKEY_LOCAL_MACHINE, "Software\\NSIS.Library.RegTool.v3"); + } + + { + if (CALL(GetModuleFileName)(GetModuleHandle(NULL), file, STR_SIZE)) + { + DeleteFileOnReboot(file); + } + } + GlobalFree(keyname); + GlobalFree(file); + } + else + { + DWORD orgerrmode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + OleInitialize(NULL); + + if (*cmdline == ('D')) + { + RegDll(cmdline + 1); + } + else if (*cmdline == ('T')) + { + RegTypeLib(cmdline + 1); + } + + OleUninitialize(); + SetErrorMode(orgerrmode); + } + + return 0; +} + +NSIS_ENTRYPOINT_GUINOCRT +EXTERN_C void NSISWinMainNOCRT() +{ + int ec = IsWinNT() ? RegTool() : RegTool(); + ExitProcess(ec); +} diff --git a/Contrib/Library/RegTool/SConscript b/Contrib/Library/RegTool/SConscript index abb3e55f..df4e4bde 100644 --- a/Contrib/Library/RegTool/SConscript +++ b/Contrib/Library/RegTool/SConscript @@ -1,7 +1,7 @@ target = 'RegTool' files = Split(""" - RegTool.c + RegTool.cpp """) libs = Split(""" diff --git a/Docs/src/history.but b/Docs/src/history.but index 64f04dab..42397a7b 100644 --- a/Docs/src/history.but +++ b/Docs/src/history.but @@ -8,6 +8,8 @@ Released on ? ?th, 2017 \S2{} Major Changes +\b Fixed RegTool Win9x bug + \b Added \R{execshellwait}{ExecShellWait} \b Added \R{writeregmultistr}{WriteRegMultiStr} (\W{http://sf.net/p/nsis/feature-requests/382}{RFE #382}, \W{http://sf.net/p/nsis/patches/219}{patch #219}) diff --git a/Include/Library.nsh b/Include/Library.nsh index e316c954..ad24afea 100644 --- a/Include/Library.nsh +++ b/Include/Library.nsh @@ -121,6 +121,16 @@ ;------------------------ ;Setup RegTool + !ifdef NSIS_MAKENSIS64 + !if "${NSIS_PTR_SIZE}" < 8 + !error "Incompatible RegTool bitness, compile with 32-bit NSIS!" ; 64-bit RegTool on 32-bit Windows + !endif + !else + !if "${NSIS_PTR_SIZE}" > 4 + !warning "Incompatible RegTool bitness!" ; 32-bit RegTool will probably fail to register 64-bit library + !endif + !endif + ReadRegStr $R3 HKLM "Software\Microsoft\Windows\CurrentVersion\RunOnce" "${REGTOOL_KEY}" StrCpy $R3 $R3 -4 1 IfFileExists $R3 +3 diff --git a/Include/UpgradeDLL.nsh b/Include/UpgradeDLL.nsh index c2bbb3a8..6548d488 100644 --- a/Include/UpgradeDLL.nsh +++ b/Include/UpgradeDLL.nsh @@ -67,6 +67,16 @@ Example: ;------------------------ ;Setup RegTool + !ifdef NSIS_MAKENSIS64 + !if "${NSIS_PTR_SIZE}" < 8 + !error "Incompatible RegTool bitness, compile with 32-bit NSIS!" ; 64-bit RegTool on 32-bit Windows + !endif + !else + !if "${NSIS_PTR_SIZE}" > 4 + !warning "Incompatible RegTool bitness!" ; 32-bit RegTool will probably fail to register 64-bit library + !endif + !endif + ReadRegStr $R3 HKLM "Software\Microsoft\Windows\CurrentVersion\RunOnce" "NSIS.Library.RegTool.v3" StrCpy $R3 $R3 -4 1 IfFileExists $R3 +3