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;
 | 
						|
} |