555 lines
21 KiB
C++
555 lines
21 KiB
C++
|
//
|
||
|
// 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;
|
||
|
}
|