mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-30 01:29:42 -06:00
Move Updater to WinUpdater
This commit is contained in:
82
Source/Core/WinUpdater/Main.cpp
Normal file
82
Source/Core/WinUpdater/Main.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <windows.h>
|
||||
#include <ShlObj.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "UpdaterCommon/UI.h"
|
||||
#include "UpdaterCommon/UpdaterCommon.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
std::vector<std::string> CommandLineToUtf8Argv(PCWSTR command_line)
|
||||
{
|
||||
int nargs;
|
||||
LPWSTR* tokenized = CommandLineToArgvW(command_line, &nargs);
|
||||
if (!tokenized)
|
||||
return {};
|
||||
|
||||
std::vector<std::string> argv(nargs);
|
||||
for (int i = 0; i < nargs; ++i)
|
||||
{
|
||||
argv[i] = UTF16ToUTF8(tokenized[i]);
|
||||
}
|
||||
|
||||
LocalFree(tokenized);
|
||||
return argv;
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
|
||||
{
|
||||
if (lstrlenW(pCmdLine) == 0)
|
||||
{
|
||||
MessageBox(nullptr,
|
||||
L"This updater is not meant to be launched directly. Configure Auto-Update in "
|
||||
"Dolphin's settings instead.",
|
||||
L"Error", MB_ICONERROR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Test for write permissions
|
||||
bool need_admin = false;
|
||||
|
||||
FILE* test_fh = fopen("Updater.log", "w");
|
||||
|
||||
if (test_fh == nullptr)
|
||||
need_admin = true;
|
||||
else
|
||||
fclose(test_fh);
|
||||
|
||||
if (need_admin)
|
||||
{
|
||||
if (IsUserAnAdmin())
|
||||
{
|
||||
MessageBox(nullptr, L"Failed to write to directory despite administrator priviliges.",
|
||||
L"Error", MB_ICONERROR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wchar_t path[MAX_PATH];
|
||||
if (GetModuleFileName(hInstance, path, sizeof(path)) == 0)
|
||||
{
|
||||
MessageBox(nullptr, L"Failed to get updater filename.", L"Error", MB_ICONERROR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Relaunch the updater as administrator
|
||||
ShellExecuteW(nullptr, L"runas", path, pCmdLine, NULL, SW_SHOW);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> args = CommandLineToUtf8Argv(pCmdLine);
|
||||
|
||||
return RunUpdater(args) ? 0 : 1;
|
||||
}
|
36
Source/Core/WinUpdater/Updater.exe.manifest
Normal file
36
Source/Core/WinUpdater/Updater.exe.manifest
Normal file
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="amd64"
|
||||
name="DolphinTeam.DolphinEmuUpdater"
|
||||
type="win32"
|
||||
/>
|
||||
<description>Dolphin updater</description>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<asmv3:windowsSettings
|
||||
xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
<dpiAware>True</dpiAware>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
271
Source/Core/WinUpdater/WinUI.cpp
Normal file
271
Source/Core/WinUpdater/WinUI.cpp
Normal file
@ -0,0 +1,271 @@
|
||||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "UpdaterCommon/UI.h"
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <Windows.h>
|
||||
#include <CommCtrl.h>
|
||||
#include <ShObjIdl.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
HWND window_handle = nullptr;
|
||||
HWND label_handle = nullptr;
|
||||
HWND total_progressbar_handle = nullptr;
|
||||
HWND current_progressbar_handle = nullptr;
|
||||
ITaskbarList3* taskbar_list = nullptr;
|
||||
|
||||
Common::Flag running;
|
||||
Common::Flag request_stop;
|
||||
|
||||
int GetWindowHeight(HWND hwnd)
|
||||
{
|
||||
RECT rect;
|
||||
GetWindowRect(hwnd, &rect);
|
||||
|
||||
return rect.bottom - rect.top;
|
||||
}
|
||||
}; // namespace
|
||||
|
||||
constexpr int PROGRESSBAR_FLAGS = WS_VISIBLE | WS_CHILD | PBS_SMOOTH | PBS_SMOOTHREVERSE;
|
||||
constexpr int WINDOW_FLAGS = WS_VISIBLE | WS_CLIPCHILDREN;
|
||||
constexpr int PADDING_HEIGHT = 5;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
bool InitWindow()
|
||||
{
|
||||
InitCommonControls();
|
||||
|
||||
WNDCLASS wndcl = {};
|
||||
wndcl.lpfnWndProc = DefWindowProcW;
|
||||
wndcl.hbrBackground = GetSysColorBrush(COLOR_MENU);
|
||||
wndcl.lpszClassName = L"UPDATER";
|
||||
|
||||
if (!RegisterClass(&wndcl))
|
||||
return false;
|
||||
|
||||
window_handle =
|
||||
CreateWindow(L"UPDATER", L"Dolphin Updater", WINDOW_FLAGS, CW_USEDEFAULT, CW_USEDEFAULT, 500,
|
||||
100, nullptr, nullptr, GetModuleHandle(nullptr), 0);
|
||||
|
||||
if (!window_handle)
|
||||
return false;
|
||||
|
||||
if (FAILED(CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&taskbar_list))))
|
||||
{
|
||||
taskbar_list = nullptr;
|
||||
}
|
||||
if (taskbar_list && FAILED(taskbar_list->HrInit()))
|
||||
{
|
||||
taskbar_list->Release();
|
||||
taskbar_list = nullptr;
|
||||
}
|
||||
|
||||
int y = PADDING_HEIGHT;
|
||||
|
||||
label_handle = CreateWindow(L"STATIC", NULL, WS_VISIBLE | WS_CHILD, 5, y, 500, 25, window_handle,
|
||||
nullptr, nullptr, 0);
|
||||
|
||||
if (!label_handle)
|
||||
return false;
|
||||
|
||||
// Get the default system font
|
||||
NONCLIENTMETRICS metrics = {};
|
||||
metrics.cbSize = sizeof(NONCLIENTMETRICS);
|
||||
|
||||
if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(metrics), &metrics, 0))
|
||||
return false;
|
||||
|
||||
SendMessage(label_handle, WM_SETFONT,
|
||||
reinterpret_cast<WPARAM>(CreateFontIndirect(&metrics.lfMessageFont)), 0);
|
||||
|
||||
y += GetWindowHeight(label_handle) + PADDING_HEIGHT;
|
||||
|
||||
total_progressbar_handle = CreateWindow(PROGRESS_CLASS, NULL, PROGRESSBAR_FLAGS, 5, y, 470, 25,
|
||||
window_handle, nullptr, nullptr, 0);
|
||||
|
||||
y += GetWindowHeight(total_progressbar_handle) + PADDING_HEIGHT;
|
||||
|
||||
if (!total_progressbar_handle)
|
||||
return false;
|
||||
|
||||
current_progressbar_handle = CreateWindow(PROGRESS_CLASS, NULL, PROGRESSBAR_FLAGS, 5, y, 470, 25,
|
||||
window_handle, nullptr, nullptr, 0);
|
||||
|
||||
y += GetWindowHeight(current_progressbar_handle) + PADDING_HEIGHT;
|
||||
|
||||
if (!current_progressbar_handle)
|
||||
return false;
|
||||
|
||||
RECT rect;
|
||||
GetWindowRect(window_handle, &rect);
|
||||
|
||||
// Account for the title bar
|
||||
y += GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION) +
|
||||
GetSystemMetrics(SM_CXPADDEDBORDER);
|
||||
// ...and window border
|
||||
y += GetSystemMetrics(SM_CYBORDER);
|
||||
|
||||
// Add some padding for good measure
|
||||
y += PADDING_HEIGHT * 3;
|
||||
|
||||
SetWindowPos(window_handle, HWND_TOPMOST, 0, 0, rect.right - rect.left, y, SWP_NOMOVE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Destroy()
|
||||
{
|
||||
DestroyWindow(window_handle);
|
||||
DestroyWindow(label_handle);
|
||||
DestroyWindow(total_progressbar_handle);
|
||||
DestroyWindow(current_progressbar_handle);
|
||||
}
|
||||
|
||||
void SetTotalMarquee(bool marquee)
|
||||
{
|
||||
SetWindowLong(total_progressbar_handle, GWL_STYLE,
|
||||
PROGRESSBAR_FLAGS | (marquee ? PBS_MARQUEE : 0));
|
||||
SendMessage(total_progressbar_handle, PBM_SETMARQUEE, marquee, 0);
|
||||
if (taskbar_list)
|
||||
{
|
||||
taskbar_list->SetProgressState(window_handle, marquee ? TBPF_INDETERMINATE : TBPF_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
void ResetTotalProgress()
|
||||
{
|
||||
SendMessage(total_progressbar_handle, PBM_SETPOS, 0, 0);
|
||||
SetCurrentMarquee(true);
|
||||
}
|
||||
|
||||
void SetTotalProgress(int current, int total)
|
||||
{
|
||||
SendMessage(total_progressbar_handle, PBM_SETRANGE32, 0, total);
|
||||
SendMessage(total_progressbar_handle, PBM_SETPOS, current, 0);
|
||||
if (taskbar_list)
|
||||
{
|
||||
taskbar_list->SetProgressValue(window_handle, current, total);
|
||||
}
|
||||
}
|
||||
|
||||
void SetCurrentMarquee(bool marquee)
|
||||
{
|
||||
SetWindowLong(current_progressbar_handle, GWL_STYLE,
|
||||
PROGRESSBAR_FLAGS | (marquee ? PBS_MARQUEE : 0));
|
||||
SendMessage(current_progressbar_handle, PBM_SETMARQUEE, marquee, 0);
|
||||
}
|
||||
|
||||
void ResetCurrentProgress()
|
||||
{
|
||||
SendMessage(current_progressbar_handle, PBM_SETPOS, 0, 0);
|
||||
SetCurrentMarquee(true);
|
||||
}
|
||||
|
||||
void Error(const std::string& text)
|
||||
{
|
||||
auto wide_text = UTF8ToUTF16(text);
|
||||
|
||||
MessageBox(nullptr,
|
||||
(L"A fatal error occured and the updater cannot continue:\n " + wide_text).c_str(),
|
||||
L"Error", MB_ICONERROR);
|
||||
|
||||
if (taskbar_list)
|
||||
{
|
||||
taskbar_list->SetProgressState(window_handle, TBPF_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void SetCurrentProgress(int current, int total)
|
||||
{
|
||||
SendMessage(current_progressbar_handle, PBM_SETRANGE32, 0, total);
|
||||
SendMessage(current_progressbar_handle, PBM_SETPOS, current, 0);
|
||||
}
|
||||
|
||||
void SetDescription(const std::string& text)
|
||||
{
|
||||
SetWindowText(label_handle, UTF8ToUTF16(text).c_str());
|
||||
}
|
||||
|
||||
void MessageLoop()
|
||||
{
|
||||
request_stop.Clear();
|
||||
running.Set();
|
||||
|
||||
if (!InitWindow())
|
||||
{
|
||||
running.Clear();
|
||||
MessageBox(nullptr, L"Window init failed!", L"", MB_ICONERROR);
|
||||
}
|
||||
|
||||
SetTotalMarquee(true);
|
||||
SetCurrentMarquee(true);
|
||||
|
||||
while (!request_stop.IsSet())
|
||||
{
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, window_handle, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
running.Clear();
|
||||
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
std::thread thread(MessageLoop);
|
||||
thread.detach();
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
if (!running.IsSet())
|
||||
return;
|
||||
|
||||
request_stop.Set();
|
||||
|
||||
while (running.IsSet())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void LaunchApplication(std::string path)
|
||||
{
|
||||
// Hack: Launching the updater over the explorer ensures that admin priviliges are dropped. Why?
|
||||
// Ask Microsoft.
|
||||
ShellExecuteW(nullptr, nullptr, L"explorer.exe", UTF8ToUTF16(path).c_str(), nullptr, SW_SHOW);
|
||||
}
|
||||
|
||||
void Sleep(int sleep)
|
||||
{
|
||||
::Sleep(sleep * 1000);
|
||||
}
|
||||
|
||||
void WaitForPID(u32 pid)
|
||||
{
|
||||
HANDLE parent_handle = OpenProcess(SYNCHRONIZE, FALSE, static_cast<DWORD>(pid));
|
||||
WaitForSingleObject(parent_handle, INFINITE);
|
||||
CloseHandle(parent_handle);
|
||||
}
|
||||
|
||||
void SetVisible(bool visible)
|
||||
{
|
||||
ShowWindow(window_handle, visible ? SW_SHOW : SW_HIDE);
|
||||
}
|
||||
|
||||
}; // namespace UI
|
70
Source/Core/WinUpdater/WinUpdater.vcxproj
Normal file
70
Source/Core/WinUpdater/WinUpdater.vcxproj
Normal file
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{E4BECBAB-9C6E-41AB-BB56-F9D70AB6BE03}</ProjectGuid>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\..\VSProps\Base.props" />
|
||||
<Import Project="..\..\VSProps\PCHUse.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup>
|
||||
<Link>
|
||||
<AdditionalDependencies>iphlpapi.lib;winmm.lib;ws2_32.lib;comctl32.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common\Common.vcxproj">
|
||||
<Project>{2e6c348c-c75c-4d94-8d1e-9c1fcbf3efe4}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\UpdaterCommon\UpdaterCommon.vcxproj">
|
||||
<Project>{B001D13E-7EAB-4689-842D-801E5ACFFAC5}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Main.cpp" />
|
||||
<ClCompile Include="WinUI.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
<!--Copy the .exe to binary output folder-->
|
||||
<ItemGroup>
|
||||
<SourceFiles Include="$(TargetPath)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="Updater.exe.manifest" />
|
||||
</ItemGroup>
|
||||
<Target Name="AfterBuild" Inputs="@(SourceFiles)" Outputs="@(SourceFiles -> '$(BinaryOutputDir)%(Filename)%(Extension)')">
|
||||
<Message Text="Copy: @(SourceFiles) -> $(BinaryOutputDir)" Importance="High" />
|
||||
<Copy SourceFiles="@(SourceFiles)" DestinationFolder="$(BinaryOutputDir)" />
|
||||
</Target>
|
||||
</Project>
|
10
Source/Core/WinUpdater/WinUpdater.vcxproj.filters
Normal file
10
Source/Core/WinUpdater/WinUpdater.vcxproj.filters
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Main.cpp" />
|
||||
<ClCompile Include="WinUI.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="Updater.exe.manifest" />
|
||||
</ItemGroup>
|
||||
</Project>
|
Reference in New Issue
Block a user