Improved updater UI

This commit is contained in:
WolverinDEV 2020-04-22 20:01:29 +02:00
parent 5433bc55f4
commit 185f70182a
9 changed files with 861 additions and 11 deletions

View File

@ -9,10 +9,14 @@ set(SOURCE_FILES
file.cpp file.cpp
) )
if (WIN32)
list(APPEND SOURCE_FILES win32/retry_ui.cpp win32/Resource.rc)
endif ()
add_executable(update_installer ${SOURCE_FILES}) add_executable(update_installer ${SOURCE_FILES})
if(WIN32) if(WIN32)
target_link_libraries(update_installer kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;Shlwapi.lib) target_link_libraries(update_installer kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;Shlwapi.lib;Rstrtmgr.lib)
add_custom_command(TARGET update_installer POST_BUILD add_custom_command(TARGET update_installer POST_BUILD
COMMAND ${CMAKE_COMMAND} -E COMMAND ${CMAKE_COMMAND} -E

View File

@ -3,10 +3,12 @@
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include "logger.h" #include "./logger.h"
#include "config.h" #include "./config.h"
#include "util.h" #include "./util.h"
#include "file.h" #include "./file.h"
#include "./ui.h"
using namespace std; using namespace std;
using namespace log_helper; using namespace log_helper;
@ -116,7 +118,7 @@ int main(int argc, char** argv) {
{ {
auto admin = is_administrator(); auto admin = is_administrator();
logger::info("App executed as admin: %s", admin ? "yes" : "no"); logger::info("App executed as admin: %s", admin ? "yes" : "no");
if(!admin) { if(!admin && false) {
logger::info("Requesting administrator rights"); logger::info("Requesting administrator rights");
if(!request_administrator(argc, argv)) { if(!request_administrator(argc, argv)) {
execute_callback_fail_exit("permissions", "failed to get administrator permissions"); execute_callback_fail_exit("permissions", "failed to get administrator permissions");
@ -137,20 +139,24 @@ int main(int argc, char** argv) {
{ {
logger::info("Awaiting the unlocking of all files"); logger::info("Awaiting the unlocking of all files");
await_unlock:
auto begin = chrono::system_clock::now(); auto begin = chrono::system_clock::now();
while(true) { while(true) {
bool locked = false; bool locked = false;
for(shared_ptr<config::LockFile>& file : config::locking_files) { for(shared_ptr<config::LockFile>& file : config::locking_files) {
if(file::file_locked(file->filename)) { if(file::file_locked(file->filename)) {
locked = true; locked = true;
if(chrono::system_clock::now() - chrono::milliseconds(file->timeout) > begin) { if(chrono::system_clock::now() - std::chrono::milliseconds{1000} > begin) { /* we don't use the lock timeout here because we've a new system */
auto result = ui::open_file_blocked(file->filename);
if(result == ui::FileBlockedResult::PROCESSES_CLOSED)
goto await_unlock;
logger::fatal( logger::fatal(
"Failed to lock file %s. Timeout: %d, Time tried: %d", "Failed to lock file %s. Timeout: %d, Time tried: %d",
file->filename.c_str(), file->filename.c_str(),
file->timeout, file->timeout,
chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now() - begin).count() chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now() - begin).count()
); );
execute_callback_fail_exit(file->error_id, "lock timeout (" + to_string(file->timeout) + ")"); execute_callback_fail_exit(file->error_id, "failed to lock file");
} }
} }
} }

View File

@ -5,9 +5,11 @@
"callback_file": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", "callback_file": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"callback_argument_fail": "-EncodedCommand \"QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA==\"", "callback_argument_fail": "-EncodedCommand \"QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA==\"",
"callback_argument_success": "-EncodedCommand \"QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAcwB1AGMAYwBlAGUAZABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA==\"", "callback_argument_success": "-EncodedCommand \"QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAcwB1AGMAYwBlAGUAZABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA==\"",
"locks": [ "locks": [{
"filename": "D:\\Program Files\\IDA 7.0\\ida.exe",
], "timeout": 5000,
"error-id": "Hello World error"
}],
"moves": [ "moves": [
{ {
"source": "source/file_new.txt", "source": "source/file_new.txt",

View File

View File

@ -355,3 +355,268 @@
[2] Update unpacking successfully! [2] Update unpacking successfully!
[2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with success with command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAcwB1AGMAYwBlAGUAZABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA==" bG9nX2ZpbGU6ZFhCa1lYUmxMbXh2Wnc9PQ== [2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with success with command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAcwB1AGMAYwBlAGUAZABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA==" bG9nX2ZpbGU6ZFhCa1lYUmxMbXh2Wnc9PQ==
[2] ----------- log ended at 2019-04-16.16:46:25 ----------- [2] ----------- log ended at 2019-04-16.16:46:25 -----------
[523F][2] ----------- log started at 2020-04-22.19:51:40 -----------
[523F][2] App executed as admin: no
[523F][2] Requesting administrator rights
[523F][2] Admin right granted. New updater instance executes the update now.
[523F][2] ----------- log ended at 2020-04-22.19:51:43 -----------
[3DC4][2] ----------- log started at 2020-04-22.19:51:43 -----------
[3DC4][2] App executed as admin: yes
[3DC4][2] loading config from file updater\test\dummy_config.json
[3DC4][5] failed to load config: failed to get key error-id. error: [json.exception.type_error.305] cannot use operator[] with a string argument with string
[3DC4][2] ----------- log ended at 2020-04-22.19:51:43 -----------
[39EE][2] ----------- log started at 2020-04-22.19:52:35 -----------
[39EE][2] App executed as admin: no
[39EE][2] Requesting administrator rights
[39EE][2] Admin right granted. New updater instance executes the update now.
[39EE][2] ----------- log ended at 2020-04-22.19:52:38 -----------
[52B2][2] ----------- log started at 2020-04-22.19:52:38 -----------
[52B2][2] App executed as admin: yes
[52B2][2] loading config from file updater\test\dummy_config.json
[52B2][5] failed to load config: failed to get key error-id. error: [json.exception.type_error.305] cannot use operator[] with a string argument with string
[52B2][2] ----------- log ended at 2020-04-22.19:52:38 -----------
[35F3][2] ----------- log started at 2020-04-22.19:52:47 -----------
[35F3][2] App executed as admin: no
[35F3][2] Requesting administrator rights
[35F3][2] Admin right granted. New updater instance executes the update now.
[35F3][2] ----------- log ended at 2020-04-22.19:52:49 -----------
[64D5][2] ----------- log started at 2020-04-22.19:52:49 -----------
[64D5][2] App executed as admin: yes
[64D5][2] loading config from file updater\test\dummy_config.json
[64D5][5] failed to load config: failed to get key error-id. error: [json.exception.type_error.305] cannot use operator[] with a string argument with string
[64D5][2] ----------- log ended at 2020-04-22.19:52:49 -----------
[65B6][2] ----------- log started at 2020-04-22.19:52:51 -----------
[65B6][2] App executed as admin: no
[65B6][2] Requesting administrator rights
[65B6][5] callback file () is not executable! Ignoring fail callback
[65B6][2] ----------- log ended at 2020-04-22.19:52:53 -----------
[59A3][2] ----------- log started at 2020-04-22.19:53:00 -----------
[59A3][2] App executed as admin: no
[59A3][2] loading config from file updater\test\dummy_config.json
[59A3][5] failed to load config: failed to get key error-id. error: [json.exception.type_error.305] cannot use operator[] with a string argument with string
[59A3][2] ----------- log ended at 2020-04-22.19:53:00 -----------
[370F][2] ----------- log started at 2020-04-22.19:53:43 -----------
[370F][2] App executed as admin: no
[370F][2] loading config from file updater\test\dummy_config.json
[370F][5] failed to load config: failed to get key error-id. error: [json.exception.type_error.302] type must be string, but is null
[370F][2] ----------- log ended at 2020-04-22.19:53:43 -----------
[65DD][2] ----------- log started at 2020-04-22.19:53:45 -----------
[65DD][2] App executed as admin: no
[65DD][2] loading config from file updater\test\dummy_config.json
[65DD][5] failed to load config: failed to get key error-id. error: [json.exception.type_error.302] type must be string, but is null
[65DD][2] ----------- log ended at 2020-04-22.19:53:45 -----------
[4225][2] ----------- log started at 2020-04-22.19:55:34 -----------
[4225][2] App executed as admin: no
[4225][2] loading config from file updater\test\dummy_config.json
[4225][1] Loaded 1 locking actions and 1 moving actions
[4225][2] Awaiting the unlocking of all files
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][2] Failed to open file! (The system cannot find the file specified.) (2)
[4225][5] Failed to lock file lock_file.txt. Timeout: 5000, Time tried: 7941
[4225][2] Rollbacking 0 moved files
[4225][2] Rollbacking 0 deleted files
[4225][2] Rollback done
[4225][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6U0dWc2JHOGdWMjl5YkdRZ1pYSnliM0k9O2Vycm9yX21lc3NhZ2U6Ykc5amF5QjBhVzFsYjNWMElDZzFNREF3S1E9PQ==
[4225][2] ----------- log ended at 2020-04-22.19:55:50 -----------
[3BB5][2] ----------- log started at 2020-04-22.19:56:36 -----------
[3BB5][2] App executed as admin: no
[3BB5][2] loading config from file updater\test\dummy_config.json
[3BB5][1] Loaded 1 locking actions and 1 moving actions
[3BB5][2] Awaiting the unlocking of all files
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][2] Failed to open file! (The system cannot find the file specified.) (2)
[3BB5][5] Failed to lock file lock_file.txt. Timeout: 5000, Time tried: 8660
[3BB5][2] Rollbacking 0 moved files
[3BB5][2] Rollbacking 0 deleted files
[3BB5][2] Rollback done
[3BB5][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6U0dWc2JHOGdWMjl5YkdRZ1pYSnliM0k9O2Vycm9yX21lc3NhZ2U6Ykc5amF5QjBhVzFsYjNWMElDZzFNREF3S1E9PQ==
[3BB5][2] ----------- log ended at 2020-04-22.19:56:44 -----------
[6DF9][2] ----------- log started at 2020-04-22.19:57:03 -----------
[6DF9][2] App executed as admin: no
[6DF9][2] loading config from file updater\test\dummy_config.json
[6DF9][1] Loaded 1 locking actions and 1 moving actions
[6DF9][2] Awaiting the unlocking of all files
[6DF9][2] All files have been unlocked (0ms required)
[6DF9][2] Moving file from source/file_new.txt to target/file.txt
[6DF9][5] failed to move file source/file_new.txt to target/file.txt (source file does not exists)
[6DF9][2] Rollbacking 0 moved files
[6DF9][2] Rollbacking 0 deleted files
[6DF9][2] Rollback done
[6DF9][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
[6DF9][2] ----------- log ended at 2020-04-22.19:57:03 -----------
[2B0D][2] ----------- log started at 2020-04-22.19:57:18 -----------
[2B0D][2] App executed as admin: no
[2B0D][2] loading config from file updater\test\dummy_config.json
[2B0D][1] Loaded 1 locking actions and 1 moving actions
[2B0D][2] Awaiting the unlocking of all files
[2B0D][2] All files have been unlocked (0ms required)
[2B0D][2] Moving file from source/file_new.txt to target/file.txt
[2B0D][5] failed to move file source/file_new.txt to target/file.txt (source file does not exists)
[2B0D][2] Rollbacking 0 moved files
[2B0D][2] Rollbacking 0 deleted files
[2B0D][2] Rollback done
[2B0D][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
[2B0D][2] ----------- log ended at 2020-04-22.19:57:18 -----------
[79AD][2] ----------- log started at 2020-04-22.19:57:34 -----------
[79AD][2] App executed as admin: no
[79AD][2] loading config from file updater\test\dummy_config.json
[79AD][1] Loaded 1 locking actions and 1 moving actions
[79AD][2] Awaiting the unlocking of all files
[79AD][2] All files have been unlocked (0ms required)
[79AD][2] Moving file from source/file_new.txt to target/file.txt
[79AD][5] failed to move file source/file_new.txt to target/file.txt (source file does not exists)
[79AD][2] Rollbacking 0 moved files
[79AD][2] Rollbacking 0 deleted files
[79AD][2] Rollback done
[79AD][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
[79AD][2] ----------- log ended at 2020-04-22.19:57:34 -----------
[5665][2] ----------- log started at 2020-04-22.19:57:35 -----------
[5665][2] App executed as admin: no
[5665][2] loading config from file updater\test\dummy_config.json
[5665][1] Loaded 1 locking actions and 1 moving actions
[5665][2] Awaiting the unlocking of all files
[5665][2] All files have been unlocked (0ms required)
[5665][2] Moving file from source/file_new.txt to target/file.txt
[5665][5] failed to move file source/file_new.txt to target/file.txt (source file does not exists)
[5665][2] Rollbacking 0 moved files
[5665][2] Rollbacking 0 deleted files
[5665][2] Rollback done
[5665][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
[5665][2] ----------- log ended at 2020-04-22.19:57:35 -----------
[0A74][2] ----------- log started at 2020-04-22.19:57:43 -----------
[0A74][2] App executed as admin: no
[0A74][2] loading config from file updater\test\dummy_config.json
[0A74][1] Loaded 1 locking actions and 1 moving actions
[0A74][2] Awaiting the unlocking of all files
[0A74][2] All files have been unlocked (0ms required)
[0A74][2] Moving file from source/file_new.txt to target/file.txt
[0A74][5] failed to move file source/file_new.txt to target/file.txt (source file does not exists)
[0A74][2] Rollbacking 0 moved files
[0A74][2] Rollbacking 0 deleted files
[0A74][2] Rollback done
[0A74][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
[0A74][2] ----------- log ended at 2020-04-22.19:57:43 -----------
[4337][2] ----------- log started at 2020-04-22.19:57:44 -----------
[4337][2] App executed as admin: no
[4337][2] loading config from file updater\test\dummy_config.json
[4337][1] Loaded 1 locking actions and 1 moving actions
[4337][2] Awaiting the unlocking of all files
[4337][2] All files have been unlocked (0ms required)
[4337][2] Moving file from source/file_new.txt to target/file.txt
[4337][5] failed to move file source/file_new.txt to target/file.txt (source file does not exists)
[4337][2] Rollbacking 0 moved files
[4337][2] Rollbacking 0 deleted files
[4337][2] Rollback done
[4337][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
[4337][2] ----------- log ended at 2020-04-22.19:57:44 -----------
[5F3A][2] ----------- log started at 2020-04-22.19:59:41 -----------
[5F3A][2] App executed as admin: no
[5F3A][2] loading config from file updater\test\dummy_config.json
[5F3A][1] Loaded 1 locking actions and 1 moving actions
[5F3A][2] Awaiting the unlocking of all files
[5F3A][2] All files have been unlocked (0ms required)
[5F3A][2] Moving file from source/file_new.txt to target/file.txt
[5F3A][5] failed to move file source/file_new.txt to target/file.txt (source file does not exists)
[5F3A][2] Rollbacking 0 moved files
[5F3A][2] Rollbacking 0 deleted files
[5F3A][2] Rollback done
[5F3A][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
[5F3A][2] ----------- log ended at 2020-04-22.19:59:41 -----------
[3179][2] ----------- log started at 2020-04-22.19:59:52 -----------
[3179][2] App executed as admin: no
[3179][2] loading config from file updater\test\dummy_config.json
[3179][1] Loaded 1 locking actions and 1 moving actions
[3179][2] Awaiting the unlocking of all files
[3179][2] All files have been unlocked (0ms required)
[3179][2] Moving file from source/file_new.txt to target/file.txt
[3179][5] failed to move file source/file_new.txt to target/file.txt (source file does not exists)
[3179][2] Rollbacking 0 moved files
[3179][2] Rollbacking 0 deleted files
[3179][2] Rollback done
[3179][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
[3179][2] ----------- log ended at 2020-04-22.20:00:03 -----------
[5E98][2] ----------- log started at 2020-04-22.20:00:53 -----------
[5E98][2] App executed as admin: no
[5E98][2] loading config from file updater\test\dummy_config.json
[5E98][1] Loaded 1 locking actions and 1 moving actions
[5E98][2] Awaiting the unlocking of all files
[5E98][2] All files have been unlocked (0ms required)
[5E98][2] Moving file from source/file_new.txt to target/file.txt
[5E98][5] failed to move file source/file_new.txt to target/file.txt (source file does not exists)
[5E98][2] Rollbacking 0 moved files
[5E98][2] Rollbacking 0 deleted files
[5E98][2] Rollback done
[5E98][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
[5E98][2] ----------- log ended at 2020-04-22.20:00:54 -----------
[59B2][2] ----------- log started at 2020-04-22.20:00:59 -----------
[59B2][2] App executed as admin: no
[59B2][2] loading config from file updater\test\dummy_config.json
[59B2][1] Loaded 1 locking actions and 1 moving actions
[59B2][2] Awaiting the unlocking of all files
[59B2][2] All files have been unlocked (0ms required)
[59B2][2] Moving file from source/file_new.txt to target/file.txt
[59B2][5] failed to move file source/file_new.txt to target/file.txt (source file does not exists)
[59B2][2] Rollbacking 0 moved files
[59B2][2] Rollbacking 0 deleted files
[59B2][2] Rollback done
[59B2][2] executing callback file C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe with fail command line C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -EncodedCommand "QQBkAGQALQBUAHkAcABlACAALQBBAHMAcwBlAG0AYgBsAHkATgBhAG0AZQAgAFMAeQBzAHQAZQBtAC4AVwBpAG4AZABvAHcAcwAuAEYAbwByAG0AcwA7ACAAWwBTAHkAcwB0AGUAbQAuAFcAaQBuAGQAbwB3AHMALgBGAG8AcgBtAHMALgBNAGUAcwBzAGEAZwBlAEIAbwB4AF0AOgA6AFMAaABvAHcAKAAiAFUAcABkAGEAdABlACAAZgBhAGkAbABlAGQAIgAsACIAVQBwAGQAYQB0AGUAcgAiACwAMAApAA=="bG9nX2ZpbGU6ZFhCa1lYUmxjbHgwWlhOMFhIVndaR0YwWlM1c2IyYz07ZXJyb3JfaWQ6TURBd01RPT07ZXJyb3JfbWVzc2FnZTpjMjkxY21ObElHWnBiR1VnWkc5bGN5QnViM1FnWlhocGMzUno=
[59B2][2] ----------- log ended at 2020-04-22.20:01:11 -----------

17
native/updater/ui.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <string>
namespace ui {
enum struct FileBlockedResult {
UNSET,
PROCESSES_CLOSED,
CANCELED,
INTERNAL_ERROR
};
#ifdef WIN32
extern void init_win32();
#endif
extern FileBlockedResult open_file_blocked(const std::string&);
}

View File

@ -0,0 +1 @@
101 ICON "logo.ico"

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@ -0,0 +1,555 @@
//
// Created by WolverinDEV on 21/04/2020.
//
#include <Windows.h>
#include <RestartManager.h>
#include <CommCtrl.h>
#include <windowsx.h>
#include <cstdio>
#include <cassert>
#include <array>
#include <vector>
#include <string>
#include <iostream>
#include <thread>
#include <condition_variable>
#include "../ui.h"
struct BlockingProcess {
RM_APP_TYPE type{};
std::wstring name{};
std::wstring exe_path{};
int pid{};
};
bool blocking_processes(std::vector<BlockingProcess>& result, std::wstring& error, const std::wstring& file) {
DWORD dwSession{0};
WCHAR szSessionKey[CCH_RM_SESSION_KEY + 1] = { 0 };
DWORD dwError = RmStartSession(&dwSession, 0, szSessionKey);
if(dwError != ERROR_SUCCESS) {
error = L"Failed to start rm session (" + std::to_wstring(dwError) + L")";
goto error_exit;
}
PCWSTR pszFile = file.data();
dwError = RmRegisterResources(dwSession, 1, &pszFile, 0, nullptr, 0, nullptr);
if(dwError != ERROR_SUCCESS) {
error = L"Failed to register resource (" + std::to_wstring(dwError) + L")";
goto error_exit;
}
DWORD dwReason;
UINT i;
UINT nProcInfoNeeded;
UINT nProcInfo = 10;
RM_PROCESS_INFO rgpi[10];
dwError = RmGetList(dwSession, &nProcInfoNeeded, &nProcInfo, rgpi, &dwReason);
if(dwError != ERROR_SUCCESS) {
error = L"Failed to get list from rm (" + std::to_wstring(dwError) + L")";
goto error_exit;
}
result.reserve(nProcInfo);
for (i = 0; i < nProcInfo; i++) {
auto& info = result.emplace_back();
info.type = rgpi[i].ApplicationType;
info.name = rgpi[i].strAppName;
info.pid = rgpi[i].Process.dwProcessId;
info.exe_path = L"unknown";
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, rgpi[i].Process.dwProcessId);
if (hProcess) {
FILETIME ftCreate, ftExit, ftKernel, ftUser;
if (GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser) && CompareFileTime(&rgpi[i].Process.ProcessStartTime, &ftCreate) == 0) {
WCHAR sz[MAX_PATH];
DWORD cch{MAX_PATH};
if (QueryFullProcessImageNameW(hProcess, 0, sz, &cch) && cch <= MAX_PATH) {
info.exe_path = sz;
}
}
CloseHandle(hProcess);
}
}
RmEndSession(dwSession);
return true;
error_exit:
if(dwSession)
RmEndSession(dwSession);
return false;
}
#if 0
enum LabelDefault { LABEL_MAX };
enum BrushDefault { BRUSH_MAX };
template <typename Labels = LabelDefault, typename Brush = BrushDefault>
class Win32Window {
public:
template <typename Label>
using WithLabels = Win32Window<Label, Brush>;
template <typename Brush>
using WithBrush = Win32Window<Labels, Brush>;
Win32Window();
protected:
std::array<HWND, Labels::LABEL_MAX> hLabel{};
std::array<HBRUSH, Brush::BRUSH_MAX> hBrush{};
};
static void a() {
auto window = new Win32Window();
}
#endif
class FileInUseWindow {
public:
static constexpr auto ClassName = "FileInUseWindow";
static bool register_class();
explicit FileInUseWindow(HWND);
virtual ~FileInUseWindow();
bool initialize();
void finalize();
void set_file(const std::wstring&);
void update_blocking_info();
ui::FileBlockedResult result{ui::FileBlockedResult::UNSET};
bool deleteOnClose{true};
private:
static constexpr auto kBackgroundColor = RGB(240, 240, 240);
static LRESULT CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR window_proc_color_static(HWND hElement, HDC hDC);
INT_PTR window_proc_command(HWND hwnd, DWORD cID);
void set_blocking_info(const std::wstring_view&, const std::vector<BlockingProcess>&);
enum Label {
LABEL_FIRST_LINE,
LABEL_FILE_NAME,
LABEL_SECOND_LINE,
LABEL_MAX
};
enum Brush {
BRUSH_BACKGROUND,
BRUSH_MAX
};
enum Font {
FONT_TEXT,
FONT_FILE_NAME,
FONT_PROCESS_INFO,
FONT_MAX
};
enum Tooltip {
TOOLTIP_FILE,
TOOLTIP_MAX
};
enum Button {
BUTTON_CANCEL,
BUTTON_CONTINUE,
BUTTON_REFRESH,
BUTTON_MAX
};
HWND hWindow;
std::array<HWND, Label::LABEL_MAX> hLabels{};
std::array<HWND, Tooltip::TOOLTIP_MAX> hTooltips{};
std::array<HWND, Button::BUTTON_MAX> hButton{};
HWND hListBox{};
std::array<HBRUSH, Brush::BRUSH_MAX> hBrush{};
std::array<HFONT, Font::FONT_MAX> hFont{};
std::wstring blocking_file{};
bool window_active{false};
bool update_exit{false};
bool force_update{false};
std::thread update_thread{};
std::condition_variable update_cv{};
std::mutex update_mutex{};
};
bool FileInUseWindow::register_class() {
WNDCLASS wc = {};
wc.lpfnWndProc = FileInUseWindow::window_proc;
wc.hInstance = GetModuleHandle(nullptr);
wc.lpszClassName = "FileInUseWindow";
return RegisterClass(&wc);
}
LRESULT FileInUseWindow::window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
auto window = (FileInUseWindow*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch(uMsg) {
case WM_CREATE:
window = new FileInUseWindow(hwnd);
if(!window->initialize())
assert(false);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) window);
/* Certain window data is cached, so changes you make using SetWindowLongPtr will not take effect until you call the SetWindowPos function */
SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
return 0;
case WM_DESTROY:
window->finalize();
if(window->deleteOnClose)
delete window;
PostQuitMessage(0);
return 0;
case WM_CLOSE:
if(window->result == ui::FileBlockedResult::UNSET) {
if (MessageBoxW(hwnd, L"Do you really want to cancel the update?", L"Are you sure?", MB_OKCANCEL) == IDOK) {
window->result = ui::FileBlockedResult::CANCELED;
DestroyWindow(hwnd);
}
} else {
DestroyWindow(hwnd);
}
return 0;
case WM_CTLCOLORSTATIC:
return window->window_proc_color_static((HWND) lParam, (HDC) wParam);
case WM_COMMAND:
return window->window_proc_command((HWND) lParam, LOWORD(wParam));
case WM_ACTIVATE:
if(!window) return 0;
if(wParam == WA_INACTIVE) {
window->window_active = false;
} else if(wParam == WA_ACTIVE) {
window->window_active = true;
window->force_update = true;
window->update_cv.notify_all(); /* update the list */
}
return 0;
default:
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
}
}
HWND CreateToolTip(HWND window, HWND hwnd, PTSTR pszText) {
HWND hwndTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
hwnd, NULL,
(HINSTANCE) GetWindowLongPtr(hwnd, GWLP_WNDPROC), NULL);
if(!hwndTip) return nullptr;
// Associate the tooltip with the tool.
TOOLINFO toolInfo = { 0 };
toolInfo.cbSize = sizeof(toolInfo);
toolInfo.uId = 22;
toolInfo.hwnd = hwnd;
toolInfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS;
toolInfo.lpszText = pszText;
SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
SendMessage(hwndTip, TTM_ACTIVATE, TRUE, 0);
return hwndTip;
}
HWND CreateAHorizontalScrollBar(HWND hwndParent, int sbHeight)
{
RECT rect;
// Get the dimensions of the parent window's client area;
if (!GetClientRect(hwndParent, &rect))
return NULL;
// Create the scroll bar.
return (CreateWindowExW(
0, // no extended styles
L"SCROLLBAR", // scroll bar control class
nullptr, // no window text
WS_CHILD | WS_VISIBLE // window styles
| SBS_HORZ, // horizontal scroll bar style
rect.left, // horizontal position
rect.bottom - sbHeight - 12, // vertical position
rect.right, // width of the scroll bar
sbHeight, // height of the scroll bar
hwndParent, // handle to main window
(HMENU) NULL, // no menu
(HINSTANCE) GetWindowLongPtr(hwndParent, GWLP_WNDPROC), // instance owning this window
(PVOID) NULL // pointer not needed
));
}
FileInUseWindow::FileInUseWindow(HWND hwnd) : hWindow{hwnd} {}
FileInUseWindow::~FileInUseWindow() = default;
bool FileInUseWindow::initialize() {
::SetWindowLong(this->hWindow, GWL_STYLE, GetWindowLong(this->hWindow, GWL_STYLE) & ~ (WS_SIZEBOX | WS_MINIMIZEBOX | WS_MAXIMIZEBOX));
this->hBrush[Brush::BRUSH_BACKGROUND] = CreateSolidBrush(kBackgroundColor);
this->hFont[Font::FONT_TEXT] = CreateFontW(16, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Arial");
this->hFont[Font::FONT_FILE_NAME] = CreateFontW(16, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Consolas");
this->hFont[Font::FONT_PROCESS_INFO] = CreateFontW(16, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Consolas");
SetClassLongPtr(this->hWindow, GCLP_HBRBACKGROUND, (LONG_PTR) this->hBrush[Brush::BRUSH_BACKGROUND]);
{
this->hLabels[Label::LABEL_FIRST_LINE] = CreateWindow(WC_STATIC, "Failed to unlock the following file:",
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
10, 10, 400, 20,
this->hWindow, nullptr,
(HINSTANCE) GetWindowLongPtr(this->hWindow, GWLP_WNDPROC), nullptr);
this->hLabels[Label::LABEL_FILE_NAME] = CreateWindow(WC_STATIC, "<file name here>",
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
10, 30, 400, 20,
this->hWindow, nullptr,
(HINSTANCE) GetWindowLongPtr(this->hWindow, GWLP_WNDPROC), nullptr);
this->hTooltips[Tooltip::TOOLTIP_FILE] = CreateToolTip(this->hWindow, this->hLabels[Label::LABEL_FILE_NAME], "Hello World"); //FIXME: Tooltip not working...
this->hLabels[Label::LABEL_SECOND_LINE] = CreateWindow(WC_STATIC, "Please close these processes:",
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
10, 60, 400, 20,
this->hWindow, nullptr,
(HINSTANCE) GetWindowLongPtr(this->hWindow, GWLP_WNDPROC), nullptr);
for(auto& hLabel : this->hLabels)
SendMessage(hLabel, WM_SETFONT, (WPARAM) this->hFont[Font::FONT_TEXT], TRUE);
SendMessage(this->hLabels[Label::LABEL_FILE_NAME], WM_SETFONT, (WPARAM) this->hFont[Font::FONT_FILE_NAME], TRUE);
}
{
this->hListBox = CreateWindow("ListBox", nullptr, WS_VISIBLE | WS_CHILD | LBS_STANDARD | LBS_NOTIFY | WS_HSCROLL , 10, 80, 565, 200, this->hWindow, nullptr,
(HINSTANCE) GetWindowLongPtr(this->hWindow, GWLP_WNDPROC), nullptr);
SendMessage(this->hListBox, WM_SETFONT, (WPARAM) this->hFont[Font::FONT_PROCESS_INFO], TRUE);
}
{
#if 0
this->hButton[Button::BUTTON_CANCEL] = CreateWindowW(
WC_BUTTONW, // Predefined class; Unicode assumed
L"Cancel", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
10, // x position
280, // y position
100, // Button width
30, // Button height
this->hWindow, // Parent window
nullptr, // No menu.
(HINSTANCE)GetWindowLongPtr(this->hWindow, GWLP_HINSTANCE),
nullptr); // Pointer not needed.
#endif
this->hButton[Button::BUTTON_REFRESH] = CreateWindowW(
WC_BUTTONW, // Predefined class; Unicode assumed
L"Refresh", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
10, // x position
280, // y position
100, // Button width
30, // Button height
this->hWindow, // Parent window
nullptr, // No menu.
(HINSTANCE)GetWindowLongPtr(this->hWindow, GWLP_HINSTANCE),
nullptr); // Pointer not needed.
this->hButton[Button::BUTTON_CONTINUE] = CreateWindowW(
WC_BUTTONW, // Predefined class; Unicode assumed
L"Continue", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
475, // x position
280, // y position
100, // Button width
30, // Button height
this->hWindow, // Parent window
nullptr, // No menu.
(HINSTANCE)GetWindowLongPtr(this->hWindow, GWLP_HINSTANCE),
nullptr); // Pointer not needed.
}
ShowWindow(this->hWindow, SW_SHOWNORMAL);
this->window_active = true;
this->update_thread = std::thread([&]{
while(!this->update_exit) {
{
std::unique_lock update_lock{this->update_mutex};
this->update_cv.wait_for(update_lock, std::chrono::milliseconds{1000});
if(this->update_exit) return;
}
if(this->window_active && !this->force_update) continue; /* only auto update in the background */
this->force_update = false;
this->update_blocking_info();
}
});
return true;
}
INT_PTR FileInUseWindow::window_proc_color_static(HWND hElement, HDC hDC) {
SetBkColor(hDC, kBackgroundColor);
return (INT_PTR) this->hBrush[Brush::BRUSH_BACKGROUND];
}
INT_PTR FileInUseWindow::window_proc_command(HWND hwnd, DWORD cID) {
if(hwnd == this->hButton[Button::BUTTON_CANCEL]) {
SendMessageA(this->hWindow, WM_CLOSE, (WPARAM) nullptr, (LPARAM) nullptr);
} else if(hwnd == this->hButton[Button::BUTTON_REFRESH]) {
this->force_update = true;
this->update_cv.notify_all();
} else if(hwnd == this->hButton[Button::BUTTON_CONTINUE]) {
this->result = ui::FileBlockedResult::PROCESSES_CLOSED;
SendMessageA(this->hWindow, WM_CLOSE, (WPARAM) nullptr, (LPARAM) nullptr);
}
return 0;
}
void FileInUseWindow::finalize() {
this->update_exit = true;
this->update_cv.notify_all();
if(this->update_thread.joinable())
this->update_thread.join();
//TODO: Free resources?
}
static inline std::wstring_view app_type_prefix(RM_APP_TYPE type) {
switch (type) {
case RM_APP_TYPE::RmOtherWindow:
return L"Child App : ";
case RM_APP_TYPE::RmMainWindow:
return L"Application: ";
case RM_APP_TYPE::RmConsole:
return L"Console App: ";
case RM_APP_TYPE::RmService:
return L"NT Service : ";
case RM_APP_TYPE::RmExplorer:
return L"Explorer : ";
case RM_APP_TYPE::RmCritical:
return L"System : ";
default:
return L" ";
}
}
void FileInUseWindow::set_file(const std::wstring &file) {
this->blocking_file = file;
this->update_blocking_info();
}
void FileInUseWindow::update_blocking_info() {
std::wstring error{};
std::vector<BlockingProcess> result_{};
if(!blocking_processes(result_, error, this->blocking_file)) {
MessageBoxW(this->hWindow, (L"Failed to get file info:\n" + error).c_str(), L"Failed to query file info", MB_OK | MB_ICONERROR);
SendMessageA(this->hWindow, WM_CLOSE, (WPARAM) nullptr, (LPARAM) nullptr);
return;
}
this->set_blocking_info(this->blocking_file, result_);
}
void FileInUseWindow::set_blocking_info(const std::wstring_view &file, const std::vector<BlockingProcess> &processes) {
SetWindowTextW(this->hLabels[Label::LABEL_FILE_NAME], file.data());
std::vector<BlockingProcess> output_processes{};
for(const auto& type : {
RM_APP_TYPE::RmMainWindow,
RM_APP_TYPE::RmConsole,
RM_APP_TYPE::RmService,
RM_APP_TYPE::RmCritical,
RM_APP_TYPE::RmOtherWindow
}) {
for(const auto& proc : processes) {
if(proc.type != type) continue;
auto it = std::find_if(output_processes.begin(), output_processes.end(), [&](const BlockingProcess& entry) {
return entry.name == proc.name && proc.exe_path == entry.exe_path;
});
if(it != output_processes.end())
continue;
output_processes.emplace_back(proc);
}
}
HDC hDC = GetDC(NULL);
SelectFont(hDC, this->hFont[Font::FONT_PROCESS_INFO]);
size_t width{0};
SendMessage(this->hListBox, LB_RESETCONTENT, 0, 0);
for(auto& process : output_processes) {
auto message = L" " + std::wstring{app_type_prefix(process.type)} + process.name + L" " + std::to_wstring(process.pid) + L" (" + process.exe_path + L") ";
SendMessageW(this->hListBox, LB_ADDSTRING, 0, (LPARAM) message.c_str());
RECT r = { 0, 0, 0, 0 };
DrawTextW(hDC, message.data(), message.length(), &r, DT_CALCRECT);
if(r.right - r.left > width)
width = r.right - r.left;
}
SendMessage(this->hListBox, LB_SETHORIZONTALEXTENT, (WPARAM) width, 0);
ReleaseDC(NULL, hDC);
EnableWindow(this->hButton[Button::BUTTON_CONTINUE], output_processes.empty());
}
inline std::wstring mbtow(const std::string_view& str) {
int length = MultiByteToWideChar(CP_UTF8, 0, str.data(), -1, nullptr, 0);
std::wstring result{};
result.resize(length + 1);
MultiByteToWideChar(CP_UTF8, 0, str.data(), -1, result.data() , length);
return result;
}
#define IDR_APP_ICON 101
ui::FileBlockedResult ui::open_file_blocked(const std::string& file) {
FileInUseWindow::register_class();
auto hInstance = GetModuleHandle(nullptr);
auto hWindow = CreateWindowEx(
0, // Optional window styles.
FileInUseWindow::ClassName, // Window class
"One or more files are still in use", // Window text
WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME | WS_SYSMENU, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, 600, 360,
nullptr, // Parent window
nullptr, // Menu
hInstance, // Instance handle
nullptr // Additional application data
);
if (hWindow == nullptr)
return ui::FileBlockedResult::INTERNAL_ERROR;
auto hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_APP_ICON));
SendMessage(hWindow, WM_SETICON, ICON_BIG, (LPARAM) hIcon);
SendMessage(hWindow, WM_SETICON, ICON_SMALL, (LPARAM) hIcon);
DestroyIcon(hIcon);
auto window = (FileInUseWindow*) GetWindowLongPtr(hWindow, GWLP_USERDATA);
window->deleteOnClose = false;
window->set_file(mbtow(file));
MSG msg = { };
while (GetMessage(&msg, hWindow, 0, 0))
{
if (msg.message == WM_NULL)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
auto result = window->result;
delete window;
return result;
}