diff --git a/Contrib/Library/RegTool/RegTool.bin b/Contrib/Library/RegTool/RegTool.bin deleted file mode 100644 index a80f1da3..00000000 Binary files a/Contrib/Library/RegTool/RegTool.bin and /dev/null differ diff --git a/Contrib/Library/RegTool/RegTool.c b/Contrib/Library/RegTool/RegTool.c new file mode 100644 index 00000000..24db286a --- /dev/null +++ b/Contrib/Library/RegTool/RegTool.c @@ -0,0 +1,273 @@ +#include + +#define STR_SIZE 1024 + +void RunSelf(char cmd, char *file); +void RegDll(char *file); +void RegTypeLib(char *file); +void DeleteFileOnReboot(char *pszFile); + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + char *cmdline; + char seekchar = ' '; + + cmdline = GetCommandLine(); + if (*cmdline == '\"') + seekchar = *cmdline++; + + while (*cmdline && *cmdline != seekchar) + cmdline = CharNext(cmdline); + cmdline = CharNext(cmdline); + while (*cmdline == ' ') + cmdline++; + + if (*cmdline++ != '/') + { + ExitProcess(1); + return 0; + } + + if (*cmdline == 'S') + { + HKEY rootkey; + + if (SUCCEEDED(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\NSIS.Library.RegTool.v2", 0, KEY_READ, &rootkey))) + { + char keyname[STR_SIZE]; + + 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, "count", NULL, &t, (LPBYTE) &count, &l)) && t == REG_DWORD) + { + DWORD j; + char valname[128], mode[2], file[STR_SIZE]; + + for (j = 1; j <= count; j++) + { + wsprintf(valname, "%u.mode", j); + l = sizeof(mode); + if (FAILED(RegQueryValueEx(key, valname, NULL, &t, mode, &l)) || t != REG_SZ) + continue; + + wsprintf(valname, "%u.file", j); + l = STR_SIZE; + if (FAILED(RegQueryValueEx(key, valname, NULL, &t, file, &l)) || t != REG_SZ) + continue; + + RunSelf(mode[0], file); + } + } + + RegCloseKey(key); + RegDeleteKey(rootkey, keyname); + } + } + + RegCloseKey(rootkey); + RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\NSIS.Library.RegTool.v2"); + } + + { + char file[STR_SIZE]; + if (GetModuleFileName(GetModuleHandle(NULL), file, STR_SIZE)) + { + DeleteFileOnReboot(file); + } + } + } + else + { + SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + OleInitialize(NULL); + + if (*cmdline == 'D') + { + RegDll(cmdline + 1); + } + else if (*cmdline == 'T') + { + RegTypeLib(cmdline + 1); + } + + OleUninitialize(); + SetErrorMode(0); + } + + ExitProcess(0); + return 0; +} + +void RunSelf(char cmd, char *file) +{ + char self[STR_SIZE]; + char cmdline[STR_SIZE]; + + if (!*file || (cmd != 'D' && cmd != 'T')) + return; + + if (GetModuleFileName(GetModuleHandle(NULL), self, STR_SIZE)) + { + PROCESS_INFORMATION pi; + STARTUPINFO si = { sizeof(STARTUPINFO) }; + + wsprintf(cmdline, "\"%s\" /%c%s", self, cmd, file); + + if (CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) + { + CloseHandle(pi.hThread); + + WaitForSingleObject(pi.hProcess, INFINITE); + + CloseHandle(pi.hProcess); + } + } +} + +void RegDll(char *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(char *file) +{ + WCHAR wfile[STR_SIZE]; + + if (MultiByteToWideChar(CP_ACP, 0, file, -1, wfile, STR_SIZE) != 0) + { + ITypeLib* tlib; + if (SUCCEEDED(LoadTypeLib(wfile, &tlib))) { + RegisterTypeLib(tlib, wfile, NULL); + tlib->lpVtbl->Release(tlib); + } + } +} + +char *mystrstri(char *a, char *b) +{ + int l = lstrlen(b); + while (lstrlen(a) >= l) + { + char c = a[l]; + a[l] = 0; + if (!lstrcmpi(a, b)) + { + a[l] = c; + return a; + } + a[l] = c; + a = CharNext(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++; + } +} + +void DeleteFileOnReboot(char *pszFile) +{ + BOOL fOk = 0; + HMODULE hLib=GetModuleHandle("KERNEL32.dll"); + if (hLib) + { + typedef BOOL (WINAPI *mfea_t)(LPCSTR lpExistingFileName,LPCSTR lpNewFileName,DWORD dwFlags); + mfea_t mfea; + mfea=(mfea_t) GetProcAddress(hLib,"MoveFileExA"); + if (mfea) + { + fOk=mfea(pszFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); + } + } + + if (!fOk) + { + static char szRenameLine[1024]; + static char wininit[1024]; + int cchRenameLine; + char *szRenameSec = "[Rename]\r\n"; + HANDLE hfile, hfilemap; + DWORD dwFileSize, dwRenameLinePos; + + int spn; + + // wininit is used as a temporary here + spn = GetShortPathName(pszFile,wininit,1024); + if (!spn || spn > 1024) + return; + cchRenameLine = wsprintf(szRenameLine,"NUL=%s\r\n",wininit); + + GetWindowsDirectory(wininit, 1024-16); + lstrcat(wininit, "\\wininit.ini"); + hfile = CreateFile(wininit, + GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); + + if (hfile != INVALID_HANDLE_VALUE) + { + dwFileSize = GetFileSize(hfile, NULL); + hfilemap = CreateFileMapping(hfile, NULL, PAGE_READWRITE, 0, dwFileSize + cchRenameLine + 10, NULL); + + if (hfilemap != NULL) + { + LPSTR pszWinInit = (LPSTR) MapViewOfFile(hfilemap, FILE_MAP_WRITE, 0, 0, 0); + + if (pszWinInit != NULL) + { + LPSTR pszRenameSecInFile = mystrstri(pszWinInit, szRenameSec); + if (pszRenameSecInFile == NULL) + { + lstrcpy(pszWinInit+dwFileSize, szRenameSec); + dwFileSize += 10; + dwRenameLinePos = dwFileSize; + } + else + { + char *pszFirstRenameLine = pszRenameSecInFile+10; + char *pszNextSec = mystrstri(pszFirstRenameLine,"\n["); + if (pszNextSec) + { + char *p = ++pszNextSec; + while (p < pszWinInit + dwFileSize) { + p[cchRenameLine] = *p; + p++; + } + + dwRenameLinePos = pszNextSec - pszWinInit; + } + // rename section is last, stick item at end of file + else dwRenameLinePos = dwFileSize; + } + + mini_memcpy(&pszWinInit[dwRenameLinePos], szRenameLine, cchRenameLine); + dwFileSize += cchRenameLine; + + UnmapViewOfFile(pszWinInit); + } + CloseHandle(hfilemap); + } + SetFilePointer(hfile, dwFileSize, NULL, FILE_BEGIN); + SetEndOfFile(hfile); + CloseHandle(hfile); + } + } +} diff --git a/Contrib/Library/RegTool/RegTool.nsi b/Contrib/Library/RegTool/RegTool.nsi deleted file mode 100644 index e3431e4f..00000000 --- a/Contrib/Library/RegTool/RegTool.nsi +++ /dev/null @@ -1,126 +0,0 @@ -; RegTool -; Written by Joost Verburg -; -; This tool is used by the Library.nsh macros to register -; dynamic link libraries and type libraries after a reboot. - -;-------------------------------- - -Name "RegTool" -OutFile "RegToolGenerator.exe" -SilentInstall silent -SilentUninstall silent - -SetCompressor lzma - -Icon "${NSISDIR}\Contrib\Graphics\Icons\classic-install.ico" -UninstallIcon "${NSISDIR}\Contrib\Graphics\Icons\classic-install.ico" - -AllowRootDirInstall true - -;-------------------------------- - -Var MODE -Var FILENAME -Var FOLDER - -;-------------------------------- - -Section - - WriteUninstaller $EXEDIR\RegTool.bin - -SectionEnd - -Section uninstall - - StrCpy $0 -1 - - loop: - - IntOp $0 $0 + 1 - - EnumRegValue $FILENAME HKLM "Software\NSIS.Library.RegTool" $0 - StrCmp $FILENAME "" done - - ReadRegStr $MODE HKLM "Software\NSIS.Library.RegTool" $FILENAME - - StrCmp $MODE "DT" 0 +4 - - Call un.RegDLL - Call un.RegTLB - Goto loop - - StrCmp $MODE "D" 0 +3 - - Call un.RegDLL - Goto loop - - StrCmp $MODE "T" 0 +3 - - Call un.RegTLB - Goto loop - - Goto loop - - done: - - DeleteRegKey HKLM "Software\NSIS.Library.RegTool" - Delete $INSTDIR\NSIS.Library.RegTool.exe - -SectionEnd - -Function un.RegDLL - - Push $FILENAME - Call un.GetParent - Pop $FOLDER - - SetOutPath $FOLDER - RegDLL $FILENAME - -FunctionEnd - -Function un.RegTLB - - TypeLib::Register $FILENAME - -FunctionEnd - -; GetParent -; input, top of stack (e.g. C:\Program Files\Poop) -; output, top of stack (replaces, with e.g. C:\Program Files) -; modifies no other variables. -; -; Usage: -; Push "C:\Program Files\Directory\Whatever" -; Call GetParent -; Pop $R0 -; ; at this point $R0 will equal "C:\Program Files\Directory" - -Function un.GetParent - - Exch $R0 - Push $R1 - Push $R2 - Push $R3 - - StrCpy $R1 0 - StrLen $R2 $R0 - - loop: - IntOp $R1 $R1 + 1 - IntCmp $R1 $R2 get 0 get - StrCpy $R3 $R0 1 -$R1 - StrCmp $R3 "\" get - Goto loop - - get: - StrCpy $R0 $R0 -$R1 - - Pop $R3 - Pop $R2 - Pop $R1 - Exch $R0 - -FunctionEnd diff --git a/Contrib/Library/RegTool/SConscript b/Contrib/Library/RegTool/SConscript index 2228a898..625609da 100644 --- a/Contrib/Library/RegTool/SConscript +++ b/Contrib/Library/RegTool/SConscript @@ -7,13 +7,11 @@ files = Split(""" libs = Split(""" kernel32 oleaut32 - version + advapi32 + user32 + ole32 """) -#Import('BuildUtil') +Import('BuildUtil') -#BuildUtil(target, files, libs, install = 'Bin') - -Import('env') - -env.Distribute('Bin', 'RegTool.bin') +BuildUtil(target, files, libs, entry = 'WinMain', nodeflib = 1, install_as = 'Bin/RegTool.bin') diff --git a/Include/Library.nsh b/Include/Library.nsh index 0f6e7c01..606be165 100644 --- a/Include/Library.nsh +++ b/Include/Library.nsh @@ -1,153 +1,21 @@ -/* - -*********************** -Macro - Install Library -*********************** - -This macro can be used to install DLL and TLB libraries. It checks for version numbers and Windows file protection, -registers the files and can update files on reboot. - -To ask the user for a reboot if required, use the Modern UI with a Finish page or use IfRebootFlag and make your -own page or message box. - -Usage: - -!insertmacro InstallLib libtype shared install localfile destfile tempbasedir - -Parameters: - -libtype The type of the library - - DLL Dynamic link library (DLL) - REGDLL DLL that has to be registered - TLB Type library or DLL that contains a type LIBRARY - REGDLLTLB DLL that has to be registered and contains a type library - -shared Specify whether the library is shared with other applications - - NOTSHARED The library is not shared - $VARNAME Variable that is empty when the application is installed for the first time, - which is when the shared library count will be increased. - -install Specify the installation method - - REBOOT_PROTECTED * Upgrade the library on reboot when in use (required for system files). - * Upgrade the library if the file is not protected by Windows File Protection. - - NOREBOOT_PROTECTED * Warns the user when the library is in use. The user will have to close - applications using the library. - * Upgrade the library if the file is not protected by Windows File Protection. - - REBOOT_NOTPROTECTED * Upgrade the library on reboot when in use (required for system files). - * Upgrade the library without checking for Windows File Protection. - - NOREBOOT_NOTPROTECTED * Warns the user when the library is in use. The user will have to close - applications using the library. - * Upgrade the library without checking for Windows File Protection. - -localfile Location of the library on the compiler system - -destfile Location to store the library on the user's system - -tempbasedir Directory on the user's system to store a temporary file when the system has - to be rebooted. - - For Windows 9x/ME support, this directory should be on the same volume as the - destination file (destfile). - The Windows temp directory could be located on any volume, so you cannot use - this directory. - -Options: - -LIBRARY_SHELL_EXTENSION - - Define this before inserting InstallLib macro to call SHChangeNotify with SHCNE_ASSOCCHANGED after registration. Use this to refresh the shell when installing a shell extension or when changing file associations. - -LIBRARY_COM - - Define this before inserting InstallLib macro to call CoFreeUnusedLibraries after registration. Use this for unloading all unnecessary libraries from memory when installing COM libraries. - -Notes: - -* If you want to support Windows 9x/ME, you can only use short filenames (8.3). - -* You can only compile scripts using this macro on Windows systems. - ------------------------- - -Example: - -Var ALREADY_INSTALLED -;Add code here that sets $ALREADY_INSTALLED to a non-zero value if the application is -;already installed. - -!insertmacro InstallLib REGDLL $ALREADY_INSTALLED REBOOT_NOTPROTECTED dllname.dll $SYSDIR\dllname.dll $SYSDIR - - -************************* -Macro - Uninstall Library -************************* - -This macro can be used to uninstall DLL and TLB libraries. It unregisters files and can remove them on reboot. - -Usage: - -!insertmacro UnInstallLib libtype shared uninstall file - -Parameters: - -libtype The type of the library - - DLL Dynamic link library (DLL) - REGDLL DLL that has to be registered - TLB Type library or DLL that contains a type LIBRARY - REGTLB DLL that has to be registered and contains a type library - -shared Specify whether the library is shared with other applications - - NOTSHARED The library is not shared - SHARE The library is shared and should be removed if the shared library count - indicates that the file is not in use anymore. - -uninstall Specify the uninstallation method - - NOREMOVE The library should not be removed. - You should use this option for common or important system files such as the - Visual Basic/C++/MFC runtimes. - - REBOOT_PROTECTED * Remove the library on reboot when in use (required for system files). - * Remove the library if the file is not protected by Windows File Protection. - - NOREBOOT_PROTECTED * Warns the user when the library is in use. The user will have to close - applications using the library. - * Remove the library if the file is not protected by Windows File Protection. - - REBOOT_NOTPROTECTED * Remove the library on reboot when in use (required for system files). - * Remove the library without checking for Windows File Protection. - - NOREBOOT_NOTPROTECTED * Warns the user when the library is in use. The user will have to close - applications using the library. - * Remove the library without checking for Windows File Protection. - -file Location of the library - -Options: - -LIBRARY_SHELL_EXTENSION - - Define this before inserting UninstallLib macro to call SHChangeNotify with SHCNE_ASSOCCHANGED after unregistration. Use this to refresh the shell when uninstalling a shell extension or when changing file associations. - -LIBRARY_COM - - Define this before inserting UninstallLib macro to call CoFreeUnusedLibraries after unregistration. Use this for unloading all unnecessary libraries from memory when uninstalling COM libraries. - ------------------------- - -Example: - -!insertmacro UnInstallLib REGDLL SHARED REBOOT_NOTPROTECTED $SYSDIR\dllname.dll - -*/ +# +# Library.nsh +# +# A system for the installation and uninstallation of dynamic +# link libraries (DLL) and type libraries (TLB). Using this +# system you can handle the complete setup with one single +# line of code: +# +# * File copying +# * File copying on reboot +# * Version checks +# * Registration and unregistration +# * Registration and unregistration on reboot +# * Shared DLL counting +# * Windows File Protection checks +# +# For more information, read appendix B in the documentation. +# !ifndef LIB_INCLUDED @@ -160,10 +28,81 @@ Example: !define SHCNF_IDLIST 0x0000 !endif +Var __INSTALLLLIB_SESSIONGUID + +### Initialize session id (GUID) +!macro __InstallLib_Helper_InitSession + + StrCmp $__INSTALLLLIB_SESSIONGUID '' 0 +6 + + System::Alloc 16 + System::Call 'ole32::CoCreateGuid(i sR0)' + System::Call 'ole32::StringFromGUID2(i R0, w .s, i ${NSIS_MAX_STRLEN})' + System::Free $R0 + Pop $__INSTALLLLIB_SESSIONGUID + + StrCmp $__INSTALLLLIB_SESSIONGUID '' 0 +2 + StrCpy $__INSTALLLLIB_SESSIONGUID 'session' + +!macroend + +### Add a RegTool entry to register after reboot +!macro __InstallLib_Helper_AddRegToolEntry mode filename tempdir + + Push $R0 + Push $R1 + Push $R2 + Push $R3 + + ;------------------------ + ;Copy the parameters + + Push "${filename}" + Push "${tempdir}" + + Pop $R2 ; temporary directory + Pop $R1 ; file name to register + + ;------------------------ + ;Initialize session id + + !insertmacro __InstallLib_Helper_InitSession + + ;------------------------ + ;Advance counter + + StrCpy $R0 0 + ReadRegDWORD $R0 HKLM "Software\NSIS.Library.RegTool.v2\$__INSTALLLLIB_SESSIONGUID" "count" + IntOp $R0 $R0 + 1 + WriteRegDWORD HKLM "Software\NSIS.Library.RegTool.v2\$__INSTALLLLIB_SESSIONGUID" "count" "$R0" + + ;------------------------ + ;Setup RegTool + + ReadRegStr $R3 HKLM "Software\Microsoft\Windows\CurrentVersion\RunOnce" "NSIS.Library.RegTool.v2" + IfFileExists $R3 +3 + + File /oname=$R2\NSIS.Library.RegTool.v2.exe "${NSISDIR}\Bin\RegTool.bin" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\RunOnce" \ + "NSIS.Library.RegTool.v2" '"$R2\NSIS.Library.RegTool.v2.exe" /S' + + ;------------------------ + ;Add RegTool entry + + WriteRegStr HKLM "Software\NSIS.Library.RegTool.v2\$__INSTALLLLIB_SESSIONGUID" "$R0.file" "$R1" + WriteRegStr HKLM "Software\NSIS.Library.RegTool.v2\$__INSTALLLLIB_SESSIONGUID" "$R0.mode" "${mode}" + + Pop $R2 + Pop $R1 + Pop $R0 + +!macroend + +### Install library !macro InstallLib libtype shared install localfile destfile tempbasedir !verbose push - !verbose 3 + #!verbose 3 Push $R0 Push $R1 @@ -402,36 +341,7 @@ Example: ;------------------------ ;Register on reboot - !ifdef INSTALLLIB_LIBTYPE_REGDLL | INSTALLLIB_LIBTYPE_TLB | INSTALLLIB_LIBTYPE_REGDLLTLB - - ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\RunOnce" "NSIS.Library.RegTool" - IfFileExists $R0 installlib.rebootreg_${INSTALLLIB_UNIQUE} - - File /oname=$R5\NSIS.Library.RegTool.exe "${NSISDIR}\Bin\RegTool.bin" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\RunOnce" \ - "NSIS.Library.RegTool" '"$R5\NSIS.Library.RegTool.exe"' - - installlib.rebootreg_${INSTALLLIB_UNIQUE}: - - !endif - - !ifdef INSTALLLIB_LIBTYPE_REGDLL - - WriteRegStr HKLM "Software\NSIS.Library.RegTool" "$R4" 'D' - - !endif - - !ifdef INSTALLLIB_LIBTYPE_TLB - - WriteRegStr HKLM "Software\NSIS.Library.RegTool" "$R4" 'T' - - !endif - - !ifdef INSTALLLIB_LIBTYPE_REGDLLTLB - - WriteRegStr HKLM "Software\NSIS.Library.RegTool" "$R4" 'DT' - - !endif + Call :installlib.regonreboot_${INSTALLLIB_UNIQUE} Goto installlib.done_${INSTALLLIB_UNIQUE} @@ -452,15 +362,37 @@ Example: !endif - !ifdef INSTALLLIB_LIBTYPE_REGDLL | INSTALLLIB_LIBTYPE_REGDLLTLB + !ifdef INSTALLLIB_LIBTYPE_REGDLL | INSTALLLIB_LIBTYPE_TLB | INSTALLLIB_LIBTYPE_REGDLLTLB - RegDLL $R4 + !ifdef INSTALLLIB_INSTALL_REBOOT_PROTECTED | INSTALLLIB_INSTALL_REBOOT_NOTPROTECTED - !endif + IfRebootFlag 0 installlib.regnoreboot_${INSTALLLIB_UNIQUE} - !ifdef INSTALLLIB_LIBTYPE_TLB | INSTALLLIB_LIBTYPE_REGDLLTLB + Call :installlib.regonreboot_${INSTALLLIB_UNIQUE} - TypeLib::Register $R4 + Goto installlib.registerfinish_${INSTALLLIB_UNIQUE} + + installlib.regnoreboot_${INSTALLLIB_UNIQUE}: + + !endif + + !ifdef INSTALLLIB_LIBTYPE_TLB | INSTALLLIB_LIBTYPE_REGDLLTLB + + TypeLib::Register $R4 + + !endif + + !ifdef INSTALLLIB_LIBTYPE_REGDLL | INSTALLLIB_LIBTYPE_REGDLLTLB + + RegDll $R4 + + !endif + + !ifdef INSTALLLIB_INSTALL_REBOOT_PROTECTED | INSTALLLIB_INSTALL_REBOOT_NOTPROTECTED + + installlib.registerfinish_${INSTALLLIB_UNIQUE}: + + !endif !endif @@ -483,7 +415,7 @@ Example: !undef LIBRARY_DEFINE_DONE_LABEL - installlib.done_${INSTALLLIB_UNIQUE}: + installlib.done_${INSTALLLIB_UNIQUE}: !endif @@ -528,6 +460,30 @@ Example: !endif + ;------------------------ + ;Register on reboot + + !ifdef INSTALLLIB_INSTALL_REBOOT_PROTECTED | INSTALLLIB_INSTALL_REBOOT_NOTPROTECTED + + installlib.regonreboot_${INSTALLLIB_UNIQUE}: + + !ifdef INSTALLLIB_LIBTYPE_REGDLL + !insertmacro __InstallLib_Helper_AddRegToolEntry 'D' "$R4" "$R5" + !endif + + !ifdef INSTALLLIB_LIBTYPE_TLB + !insertmacro __InstallLib_Helper_AddRegToolEntry 'T' "$R4" "$R5" + !endif + + !ifdef INSTALLLIB_LIBTYPE_REGDLLTLB + !insertmacro __InstallLib_Helper_AddRegToolEntry 'D' "$R4" "$R5" + !insertmacro __InstallLib_Helper_AddRegToolEntry 'T' "$R4" "$R5" + !endif + + Return + + !endif + ;------------------------ ;Undefine @@ -544,6 +500,7 @@ Example: !macroend +### Uninstall library !macro UnInstallLib libtype shared uninstall file !verbose push