diff --git a/.gitignore b/.gitignore index 32bf7baf..f8e2e4b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/Build/Runtime /TestBench /ipch bin diff --git a/Application/Application.cpp b/Application/Application.cpp index 25d74208..e37396c7 100644 --- a/Application/Application.cpp +++ b/Application/Application.cpp @@ -21,11 +21,11 @@ #include #include #include -#include +#include EXTERN_C IMAGE_DOS_HEADER __ImageBase; -EXTERN_C __declspec(dllimport) int RainmeterMain(LPWSTR cmdLine); +typedef int (*RainmeterMainFunc)(LPWSTR cmdLine); WCHAR* GetCommandLineArguments() { @@ -59,33 +59,37 @@ WCHAR* GetCommandLineArguments() } /* -** Hook to exit the process gracefully if delay-loading dependencies (i.e. Rainmeter.dll) fails. -** +** Attempts to load Rainmeter.dll. If it fails, retries after loading our own copies of the CRT +** DLLs in the Runtime directory. */ -FARPROC WINAPI DelayLoadFailureHook(unsigned int dliNotify, DelayLoadInfo* dli) +HINSTANCE LoadRainmeterLibrary() { - if (dliNotify == dliFailLoadLib) + HINSTANCE rmDll = LoadLibrary(L"Rainmeter.dll"); + if (!rmDll) { - WCHAR buffer[128]; - int arch = 32; -#ifdef _WIN64 - arch = 64; -#endif - const WCHAR* format = L"%S (%i-bit) error %ld.\n\nDo you want to view help online?"; - wsprintf(buffer, format, dli->szDll, arch, dli->dwLastError); - if (MessageBox(nullptr, buffer, L"Rainmeter", MB_YESNO | MB_ICONERROR) == IDYES) + WCHAR path[MAX_PATH]; + if (GetModuleFileName(nullptr, path, MAX_PATH) > 0) { - ShellExecute(nullptr, L"open", L"http://rainmeter.net/dllerror", nullptr, nullptr, SW_SHOWNORMAL); - } + PathRemoveFileSpec(path); + PathAppend(path, L"Runtime"); + SetDllDirectory(path); + PathAppend(path, L"msvcp110.dll"); - ExitProcess(0); + // Loading msvcpNNN.dll will load msvcrNNN.dll as well. + HINSTANCE msvcrDll = LoadLibrary(path); + SetDllDirectory(L""); + + if (msvcrDll) + { + rmDll = LoadLibrary(L"Rainmeter.dll"); + FreeLibrary(msvcrDll); + } + } } - return nullptr; + return rmDll; } -EXTERN_C PfnDliHook __pfnDliFailureHook2 = DelayLoadFailureHook; - /* ** Entry point. In Release builds, the entry point is Main() since the CRT is not used. ** @@ -105,7 +109,22 @@ int APIENTRY wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) HRSRC iconResource = FindResource(instance, MAKEINTRESOURCE(1), RT_ICON); if (iconResource) { - return RainmeterMain(args); + HINSTANCE rmDll = LoadRainmeterLibrary(); + if (rmDll) + { + auto rainmeterMain = (RainmeterMainFunc)GetProcAddress(rmDll, MAKEINTRESOURCEA(1)); + if (rainmeterMain) + { + return rainmeterMain(args); + } + } + + WCHAR message[128]; + wsprintf( + message, + L"Rainmeter.dll load error %ld.", + GetLastError()); + MessageBox(nullptr, message, L"Rainmeter", MB_OK | MB_ICONERROR); } else { diff --git a/Application/Application.vcxproj b/Application/Application.vcxproj index 1919a98e..12a8d140 100644 --- a/Application/Application.vcxproj +++ b/Application/Application.vcxproj @@ -22,7 +22,9 @@ Windows wWinMainCRTStartup - Rainmeter.dll + + + Shlwapi.lib;%(AdditionalDependencies) if exist ..\testbench\x32\debug\Skins goto skip @@ -44,7 +46,9 @@ xcopy /Q /S /Y ..\Build\Themes ..\testbench\x32\debug\Layouts Windows wWinMainCRTStartup - Rainmeter.dll + + + Shlwapi.lib;%(AdditionalDependencies) if exist ..\testbench\x64\debug\Skins goto skip @@ -70,7 +74,9 @@ xcopy /Q /S /Y ..\Build\Themes ..\testbench\x64\debug\Layouts Windows Main true - Rainmeter.dll + + + Shlwapi.lib;%(AdditionalDependencies) if exist ..\testbench\x32\release\Skins goto skip @@ -97,7 +103,9 @@ xcopy /Q /S /Y ..\Build\Themes ..\testbench\x32\release\Layouts Windows Main true - Rainmeter.dll + + + Shlwapi.lib;%(AdditionalDependencies) if exist ..\testbench\x64\release\Skins goto skip diff --git a/Build/Installer/Installer.nsi b/Build/Installer/Installer.nsi index 2f6edac7..2ac295a3 100644 --- a/Build/Installer/Installer.nsi +++ b/Build/Installer/Installer.nsi @@ -513,6 +513,10 @@ FunctionEnd SetOutPath "$INSTDIR\Plugins" File /x *Example*.dll "..\..\TestBench\${DIR}\Release\Plugins\*.dll" + + SetOutPath "$INSTDIR\Runtime" + File "..\Runtime\${DIR}\msvcp110.dll" + File "..\Runtime\${DIR}\msvcr110.dll" !macroend !macro RemoveStartMenuShortcuts STARTMENUPATH @@ -542,73 +546,34 @@ Section ${EndIf} ${If} $InstallPortable <> 1 - ; Download and install VC++ 2012 redist if required - ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\VisualStudio\11.0\VC\Runtimes\$InstArc" "Bld" - ReadRegDWORD $2 HKLM "SOFTWARE\Microsoft\VisualStudio\11.0\VC\Runtimes\$InstArc" "Installed" - ${If} $0 == "" - ; Some VS installs do not appear to have the "VC\Runtimes" node. - ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\VisualStudio\11.0\VC\Libraries\Extended\$InstArc" "Bld" - ReadRegDWORD $2 HKLM "SOFTWARE\Microsoft\VisualStudio\11.0\VC\Libraries\Extended\$InstArc" "Install" - ${EndIf} - - ${VersionCompare} "$0" "51106" $1 - ${If} $1 = 2 - ${OrIf} $2 <> 1 - ${If} ${Silent} - SetErrorLevel ${ERROR_NOVCREDIST} - Quit + ${AndIfNot} ${AtLeastWinVista} + ; Download and install .NET if required + ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727" "Install" + ${If} $0 <> 1 + ${If} $Install64Bit <> 1 + NSISdl::download /TIMEOUT=30000 "http://download.microsoft.com/download/5/6/7/567758a3-759e-473e-bf8f-52154438565a/dotnetfx.exe" "$PLUGINSDIR\dotnetfx.exe" + ${Else} + NSISdl::download /TIMEOUT=30000 "http://download.microsoft.com/download/a/3/f/a3f1bf98-18f3-4036-9b68-8e6de530ce0a/NetFx64.exe" "$PLUGINSDIR\dotnetfx.exe" ${EndIf} - - NSISdl::download /TIMEOUT=30000 "http://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU1/vcredist_$InstArc.exe" "$PLUGINSDIR\vcredist.exe" Pop $0 ${If} $0 == "success" - ExecWait '"$PLUGINSDIR\vcredist.exe" /q /norestart' $0 - Delete "$PLUGINSDIR\vcredist.exe" + ExecWait '"$PLUGINSDIR\dotnetfx.exe" /q:a /c:"install /q"' $0 + Delete "$PLUGINSDIR\dotnetfx.exe" ${If} $0 = 3010 SetRebootFlag true ${ElseIf} $0 <> 0 - MessageBox MB_OK|MB_ICONSTOP "$(VCINSTERROR)" + MessageBox MB_OK|MB_ICONSTOP "$(DOTNETINSTERROR)" Quit ${EndIf} ${ElseIf} $0 == "cancel" Quit ${Else} - MessageBox MB_OK|MB_ICONSTOP "$(VCINSTERROR)" + MessageBox MB_OK|MB_ICONSTOP "$(DOTNETINSTERROR)" Quit ${EndIf} ${EndIf} - - ${IfNot} ${AtLeastWinVista} - ; Download and install .NET if required - ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v2.0.50727" "Install" - ${If} $0 <> 1 - ${If} $Install64Bit <> 1 - NSISdl::download /TIMEOUT=30000 "http://download.microsoft.com/download/5/6/7/567758a3-759e-473e-bf8f-52154438565a/dotnetfx.exe" "$PLUGINSDIR\dotnetfx.exe" - ${Else} - NSISdl::download /TIMEOUT=30000 "http://download.microsoft.com/download/a/3/f/a3f1bf98-18f3-4036-9b68-8e6de530ce0a/NetFx64.exe" "$PLUGINSDIR\dotnetfx.exe" - ${EndIf} - Pop $0 - - ${If} $0 == "success" - ExecWait '"$PLUGINSDIR\dotnetfx.exe" /q:a /c:"install /q"' $0 - Delete "$PLUGINSDIR\dotnetfx.exe" - - ${If} $0 = 3010 - SetRebootFlag true - ${ElseIf} $0 <> 0 - MessageBox MB_OK|MB_ICONSTOP "$(DOTNETINSTERROR)" - Quit - ${EndIf} - ${ElseIf} $0 == "cancel" - Quit - ${Else} - MessageBox MB_OK|MB_ICONSTOP "$(DOTNETINSTERROR)" - Quit - ${EndIf} - ${EndIf} - ${EndIf} ${EndIf} SetOutPath "$INSTDIR" @@ -945,6 +910,7 @@ Section Uninstall RMDir /r "$INSTDIR\Defaults" RMDir /r "$INSTDIR\Languages" RMDir /r "$INSTDIR\Plugins" + RMDir /r "$INSTDIR\Runtime" RMDir /r "$INSTDIR\Skins" Delete "$INSTDIR\Rainmeter.dll" Delete "$INSTDIR\Rainmeter.exe" diff --git a/Docs/Building.md b/Docs/Building.md index 581d3c0c..a9281035 100644 --- a/Docs/Building.md +++ b/Docs/Building.md @@ -11,7 +11,14 @@ After Visual Studio has been installed and updated, open Rainmeter.sln to build. ### Building the installer -To build the full Rainmeter distribution, run Build.bat. If you receive "not found" errors, open Build.bat and change the variables at the top to match your system. +To build the full Rainmeter installer, you need to get obtain 32-bit and 64-bit versions of the Visual C++ 2012 Update 3 redistributable DLLs and place them in: + +* `Build\Runtime\x32\msvcr110.dll` +* `Build\Runtime\x32\msvcp110.dll` +* `Build\Runtime\x64\msvcr110.dll` +* `Build\Runtime\x64\msvcp110.dll` + +Then run Build.bat. If you receive "not found" errors, open Build.bat and change the `set=` lines at the top to match your system. To sign the installer and the Rainmeter executables, create a Certificate.bat file alongside Build.bat with the following contents: