├── .gitignore ├── LICENSE ├── README.md ├── RestoreWindows.sln ├── RestoreWindows ├── Main.cpp ├── RestoreWindows.vcxproj ├── RestoreWindows.vcxproj.filters └── RestoreWindows.vcxproj.user └── bin ├── RestoreWindows.exe └── vcredist_x86.exe /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.out 27 | *.app 28 | 29 | *.sdf 30 | *.opensdf 31 | *.suo 32 | *.pdb 33 | *.log 34 | *.aps 35 | 36 | 37 | Debug 38 | Release -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RestoreWindows 2 | A process that tracks window placements and restores them when display connections are back to normal 3 | 4 | ``` 5 | When a monitor connected with either DisplayPort or HDMI is turned off, it's 6 | disconnected from Windows as if the cable was removed (this behaviour depends 7 | on both monitor model, graphics card, and drivers) 8 | 9 | This utility continually tracks the position and size of desktop windows and 10 | restores them to their correct placement when all monitors are reconnected to 11 | the system. 12 | 13 | Usage: RestoreWindows [OPTIONS] 14 | 15 | --delay t The time in milliseconds between display reconnect and 16 | window restoration. Defaults to 1000 17 | --debuglog Writes a debug log to RestoreWindow.log 18 | ``` 19 | -------------------------------------------------------------------------------- /RestoreWindows.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RestoreWindows", "RestoreWindows\RestoreWindows.vcxproj", "{D319889C-58C2-477A-8234-3F920B308D56}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Release|Win32 = Release|Win32 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {D319889C-58C2-477A-8234-3F920B308D56}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {D319889C-58C2-477A-8234-3F920B308D56}.Debug|Win32.Build.0 = Debug|Win32 16 | {D319889C-58C2-477A-8234-3F920B308D56}.Release|Win32.ActiveCfg = Release|Win32 17 | {D319889C-58C2-477A-8234-3F920B308D56}.Release|Win32.Build.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /RestoreWindows/Main.cpp: -------------------------------------------------------------------------------- 1 | //#define _CRT_SECURE_NO_WARNINGS 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #define STRICT 5 | #define NOMINMAX 6 | //#define _WIN32_WINNT 0x0400 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | // _____________________________________________________________________ // 23 | // 24 | // Data 25 | // _____________________________________________________________________ // 26 | static std::vector DesiredMonitorPlacements; 27 | static std::map WindowPlacements; 28 | static std::map PendingUpdates0; 29 | static std::map PendingUpdates1; 30 | static bool IsUpdateScheduled; 31 | static bool IsPaused; 32 | static HWND DetectWindow; 33 | static UINT_PTR ResumeTimer; 34 | static int ResumeTimerValue = 1000; 35 | static int ScheduleTimerValue = 1000; 36 | static int logfile; 37 | 38 | 39 | 40 | // _____________________________________________________________________ // 41 | // 42 | // Helpers 43 | // _____________________________________________________________________ // 44 | int clamp(int min, int max, int value) 45 | { 46 | return std::min(std::max(min, value), max); 47 | } 48 | 49 | void Log(const char* fmt, ...) 50 | { 51 | static std::vector buf; 52 | va_list args; 53 | va_start(args, fmt); 54 | int len = _vscprintf(fmt, args); 55 | if(len >= (int)buf.size()) 56 | buf.resize(len + 1); 57 | len = _vsnprintf_s(buf.data(), buf.size(), len, fmt, args); 58 | va_end(args); 59 | if(len > 0) 60 | { 61 | OutputDebugStringA(buf.data()); 62 | _write(logfile, buf.data(), len); 63 | } 64 | } 65 | 66 | wchar_t* WndText(HWND hWnd) 67 | { 68 | static WCHAR title[200]; 69 | GetWindowText(hWnd, title, 200); 70 | return title; 71 | } 72 | 73 | char* PlacementText(const WINDOWPLACEMENT& placement) 74 | { 75 | static char text[200]; 76 | sprintf_s(text, sizeof(text), "%d, %d (%dx%d)\n", placement.rcNormalPosition.left, placement.rcNormalPosition.top, placement.rcNormalPosition.right-placement.rcNormalPosition.left, placement.rcNormalPosition.bottom-placement.rcNormalPosition.top); 77 | return text; 78 | } 79 | 80 | 81 | 82 | 83 | // _____________________________________________________________________ // 84 | // 85 | // Monitor placement 86 | // _____________________________________________________________________ // 87 | BOOL CALLBACK EnumMonitorProc(HMONITOR hMonitor, HDC hdc, LPRECT rect, LPARAM lParam) 88 | { 89 | std::vector* monitors = (std::vector*)lParam; 90 | monitors->push_back(*rect); 91 | return TRUE; 92 | } 93 | 94 | std::vector GetAllMonitors() 95 | { 96 | std::vector monitors; 97 | EnumDisplayMonitors(NULL, NULL, EnumMonitorProc, (LPARAM)&monitors); 98 | return monitors; 99 | } 100 | 101 | bool IsDesiredMonitorLayout() 102 | { 103 | std::vector monitors = GetAllMonitors(); 104 | if(monitors.size() != DesiredMonitorPlacements.size()) 105 | return false; 106 | // This assumes deterministic order which might not work on every system 107 | for(size_t i = 0; i < monitors.size(); i++) 108 | { 109 | const RECT& m0 = monitors[i]; 110 | const RECT& m1 = DesiredMonitorPlacements[i]; 111 | 112 | // Only care about the size 113 | if(m0.right - m0.left != m1.right - m1.left) 114 | return false; 115 | if(m0.top - m0.bottom != m1.top - m1.bottom) 116 | return false; 117 | } 118 | return true; 119 | } 120 | 121 | bool IsIdenticalMonitorLayout() 122 | { 123 | std::vector monitors = GetAllMonitors(); 124 | if(monitors.size() != DesiredMonitorPlacements.size()) 125 | return false; 126 | // This assumes deterministic order which might not work on every system 127 | for(size_t i = 0; i < monitors.size(); i++) 128 | { 129 | const RECT& m0 = monitors[i]; 130 | const RECT& m1 = DesiredMonitorPlacements[i]; 131 | 132 | if(memcmp(&m0, &m1, sizeof(m0))) 133 | return false; 134 | } 135 | return true; 136 | } 137 | 138 | 139 | 140 | 141 | // _____________________________________________________________________ // 142 | // 143 | // Window placement 144 | // _____________________________________________________________________ // 145 | bool IsApplicationWindow(HWND hWnd) 146 | { 147 | return IsWindowVisible(hWnd) && !GetParent(hWnd); 148 | } 149 | 150 | BOOL GetProperWindowPlacement(HWND hWnd, WINDOWPLACEMENT *placement) 151 | { 152 | placement->length = sizeof(WINDOWPLACEMENT); 153 | if(GetWindowPlacement(hWnd, placement)) 154 | { 155 | if(placement->showCmd == SW_SHOWNORMAL) 156 | { 157 | // If the window is "docked" the normalposition contains the last non-docked position. 158 | // That is not where we want to restore it, so extract the current rect with GetWindowRect 159 | // and translate the position to client coordinates 160 | GetWindowRect(hWnd, &placement->rcNormalPosition); 161 | 162 | // From MSDN WINDOWPLACEMENT reference: 163 | // "If the window is a top-level window that does not have the WS_EX_TOOLWINDOW window style, 164 | // then the coordinates represented by the following members are in workspace coordinates" 165 | // "Otherwise, these members are in screen coordinates" 166 | 167 | DWORD exStyle = GetWindowLong(hWnd, GWL_EXSTYLE); 168 | if(!(exStyle & WS_EX_TOOLWINDOW)) 169 | { 170 | MONITORINFO monitorInfo; 171 | monitorInfo.cbSize = sizeof(MONITORINFO); 172 | 173 | HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); 174 | GetMonitorInfo(hMonitor, &monitorInfo); 175 | // 176 | int dx = monitorInfo.rcMonitor.left - monitorInfo.rcWork.left; 177 | int dy = monitorInfo.rcMonitor.top - monitorInfo.rcWork.top; 178 | placement->rcNormalPosition.left += dx; 179 | placement->rcNormalPosition.top += dy; 180 | placement->rcNormalPosition.right += dx; 181 | placement->rcNormalPosition.bottom += dy; 182 | } 183 | } 184 | return TRUE; 185 | } 186 | return FALSE; 187 | } 188 | 189 | void AddWindow(HWND hWnd) 190 | { 191 | WINDOWPLACEMENT placement; 192 | if(GetProperWindowPlacement(hWnd, &placement)) 193 | { 194 | WindowPlacements[hWnd] = placement; 195 | if(GetWindowTextLength(hWnd)) 196 | Log("Adding %S\n\tat %s\n", WndText(hWnd), PlacementText(placement)); 197 | } 198 | } 199 | 200 | void RemoveWindow(HWND hWnd) 201 | { 202 | if(WindowPlacements.erase(hWnd)) 203 | { 204 | if(GetWindowTextLength(hWnd)) 205 | Log("Removing %S\n", WndText(hWnd)); 206 | PendingUpdates0.erase(hWnd); 207 | PendingUpdates1.erase(hWnd); 208 | } 209 | } 210 | 211 | bool HasWindow(HWND hWnd) 212 | { 213 | return WindowPlacements.find(hWnd) != WindowPlacements.end(); 214 | } 215 | 216 | BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) 217 | { 218 | std::vector* handles = (std::vector*)lParam; 219 | handles->push_back(hwnd); 220 | return TRUE; 221 | } 222 | 223 | void LoadAllWindowPlacements() 224 | { 225 | std::vector handles; 226 | EnumWindows(EnumWindowsProc, (LPARAM)&handles); 227 | 228 | for(HWND hWnd : handles) 229 | { 230 | if(IsApplicationWindow(hWnd)) 231 | AddWindow(hWnd); 232 | } 233 | } 234 | 235 | void CALLBACK UpdateWindowPlacements(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) 236 | { 237 | if(!IsUpdateScheduled) 238 | { 239 | KillTimer(hwnd, idEvent); 240 | return; 241 | } 242 | 243 | if(PendingUpdates1.size()) 244 | printf("Updating WindowPlacements\n"); 245 | 246 | for(auto it = PendingUpdates1.begin(); it != PendingUpdates1.end(); ++it) 247 | { 248 | WindowPlacements[it->first] = it->second; 249 | } 250 | PendingUpdates1.clear(); 251 | PendingUpdates0.swap(PendingUpdates1); 252 | if(PendingUpdates1.size()) 253 | { 254 | printf("Scheduling update\n"); 255 | SetTimer(0, 0, ScheduleTimerValue, UpdateWindowPlacements); 256 | } 257 | else 258 | { 259 | KillTimer(hwnd, idEvent); 260 | IsUpdateScheduled = false; 261 | } 262 | } 263 | 264 | void ScheduleWindowPlacementUpdate(HWND hWnd) 265 | { 266 | WINDOWPLACEMENT placement; 267 | placement.length = sizeof(WINDOWPLACEMENT); 268 | if(GetProperWindowPlacement(hWnd, &placement)) 269 | { 270 | PendingUpdates0[hWnd] = placement; 271 | 272 | if(!IsUpdateScheduled) 273 | { 274 | printf("Scheduling update\n"); 275 | IsUpdateScheduled = true; 276 | SetTimer(0, 0, ScheduleTimerValue, UpdateWindowPlacements); 277 | } 278 | } 279 | } 280 | 281 | void RestoreWindowPlacements() 282 | { 283 | for(auto it = WindowPlacements.begin(); it != WindowPlacements.end(); ++it) 284 | { 285 | WINDOWPLACEMENT& placement = it->second; 286 | 287 | // The only thing that change when a monitor is connected or disconnected is the 288 | // window's "normal position". Everything else remain the same (Z order, 289 | // maximized/minimized status, focus, etc)" 290 | 291 | // Since the maximized status cannot be catched with SetWinEventHook we have 292 | // to extract the placement again 293 | WINDOWPLACEMENT current; 294 | GetProperWindowPlacement(it->first, ¤t); 295 | 296 | // No need to restore it if it's already in its correct position 297 | if(!memcmp(¤t, &placement, sizeof(current))) 298 | continue; 299 | 300 | if(GetWindowTextLength(it->first)) 301 | { 302 | Log("%S\n", WndText(it->first)); 303 | Log("\tRestore %s to %s\n", current.showCmd == SW_SHOWMINIMIZED ? "minimized" : (current.showCmd == SW_SHOWMAXIMIZED ? "maximized" : ""), PlacementText(placement)); 304 | } 305 | 306 | if(current.showCmd == SW_SHOWMINIMIZED) 307 | { 308 | // Restore its minimized position 309 | placement.showCmd = SW_SHOWMINNOACTIVE; 310 | placement.flags |= WPF_ASYNCWINDOWPLACEMENT; 311 | SetWindowPlacement(it->first, &placement); 312 | } 313 | else if(current.showCmd == SW_SHOWMAXIMIZED) 314 | { 315 | // The window was maximized 316 | 317 | // In order to restore the window on the correct display we have to move it to its normal position first, and then maximize it. 318 | // If we only maximize it (without moving it first) it will be maximized on the display it's currently on. 319 | // Before we maximize it we have to show it in its normal state. Otherwise it will retain its size on the new monitor which 320 | // will be incorrect if the new monitor has a different resolution 321 | 322 | // Restore 323 | ShowWindowAsync(it->first, SW_SHOWNOACTIVATE); 324 | // Move 325 | placement.showCmd = SW_SHOWNOACTIVATE; 326 | placement.flags |= WPF_ASYNCWINDOWPLACEMENT; 327 | SetWindowPlacement(it->first, &placement); 328 | // Maximize 329 | ShowWindowAsync(it->first, SW_SHOWMAXIMIZED); 330 | } 331 | else 332 | { 333 | // Restore its normal position 334 | placement.showCmd = SW_SHOWNOACTIVATE; 335 | placement.flags |= WPF_ASYNCWINDOWPLACEMENT; 336 | SetWindowPlacement(it->first, &placement); 337 | } 338 | } 339 | } 340 | 341 | void PauseWindowTracking() 342 | { 343 | if(IsPaused) 344 | return; 345 | Log(" =========== PAUSE WINDOW TRACKING ===========\n"); 346 | 347 | PendingUpdates0.clear(); 348 | PendingUpdates1.clear(); 349 | 350 | IsPaused = true; 351 | } 352 | 353 | void CALLBACK ResumeWindowTracking(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) 354 | { 355 | KillTimer(hwnd, idEvent); 356 | 357 | if(!IsPaused) 358 | return; 359 | 360 | // The layout could've changed since the call to ScheduleResumeWindowTracking() 361 | if(!IsDesiredMonitorLayout()) 362 | return; 363 | 364 | Log(" =========== RESUME WINDOW TRACKING ===========\n"); 365 | IsPaused = false; 366 | 367 | RestoreWindowPlacements(); 368 | } 369 | 370 | void ScheduleResumeWindowTracking() 371 | { 372 | ResumeTimer = SetTimer(0, ResumeTimer, ResumeTimerValue, ResumeWindowTracking); 373 | } 374 | 375 | 376 | 377 | 378 | // _____________________________________________________________________ // 379 | // 380 | // Dummy window for message handling 381 | // _____________________________________________________________________ // 382 | LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 383 | { 384 | if(msg == WM_DISPLAYCHANGE) 385 | { 386 | if(!IsPaused && IsIdenticalMonitorLayout()) 387 | { 388 | // On some systems it's possible to disconnect and reconnect a monitor without 389 | // triggering a monitor layout change, but WM_DISPLAYCHANGE is still sent. 390 | // Do a pause and immediate resume to catch that case 391 | Log("Forced restore\n"); 392 | PauseWindowTracking(); 393 | ScheduleResumeWindowTracking(); 394 | } 395 | else if(IsDesiredMonitorLayout()) 396 | { 397 | // Extract monitor placements again in case the positions were changed 398 | DesiredMonitorPlacements = GetAllMonitors(); 399 | 400 | ScheduleResumeWindowTracking(); 401 | } 402 | else 403 | { 404 | PauseWindowTracking(); 405 | } 406 | } 407 | 408 | return DefWindowProc(hWnd, msg, wParam, lParam); 409 | } 410 | 411 | HWND CreateDetectionWnd() 412 | { 413 | HMODULE hInstance = GetModuleHandle(0); 414 | 415 | WNDCLASS wc = {}; 416 | wc.lpfnWndProc = (WNDPROC)WindowProc; 417 | wc.hInstance = hInstance; 418 | wc.lpszClassName = L"RestoreWindows detection window class"; 419 | ATOM hClass = RegisterClass(&wc); 420 | 421 | DWORD style = WS_DISABLED; 422 | HWND hWnd = CreateWindow((LPCWSTR)hClass, L"RestoreWindows detection window", WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, HWND_DESKTOP, (HMENU)NULL, hInstance, (LPARAM)NULL); 423 | 424 | if(!hWnd) 425 | { 426 | DWORD error = GetLastError(); 427 | error = error; 428 | } 429 | return hWnd; 430 | } 431 | 432 | 433 | 434 | 435 | // _____________________________________________________________________ // 436 | // 437 | // Global event listener 438 | // _____________________________________________________________________ // 439 | void CALLBACK WinEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime) 440 | { 441 | if(IsPaused) 442 | return; 443 | 444 | switch(event) 445 | { 446 | case EVENT_OBJECT_LOCATIONCHANGE: 447 | if(!idObject && HasWindow(hwnd)) 448 | ScheduleWindowPlacementUpdate(hwnd); 449 | break; 450 | 451 | case EVENT_OBJECT_CREATE: 452 | case EVENT_OBJECT_SHOW: 453 | if(!HasWindow(hwnd) && IsApplicationWindow(hwnd)) 454 | AddWindow(hwnd); 455 | break; 456 | 457 | case EVENT_OBJECT_DESTROY: 458 | case EVENT_OBJECT_HIDE: 459 | if(!idObject) 460 | RemoveWindow(hwnd); 461 | break; 462 | 463 | case EVENT_OBJECT_PARENTCHANGE: 464 | if(HasWindow(hwnd)) 465 | { 466 | if(!IsApplicationWindow(hwnd)) 467 | RemoveWindow(hwnd); 468 | } 469 | else if(IsApplicationWindow(hwnd)) 470 | { 471 | AddWindow(hwnd); 472 | } 473 | break; 474 | } 475 | } 476 | 477 | 478 | 479 | 480 | // _____________________________________________________________________ // 481 | // 482 | // MAIN 483 | // _____________________________________________________________________ // 484 | int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) 485 | { 486 | // One instance is enough 487 | HANDLE hGlobalLock = CreateMutex(NULL, TRUE, L"RestoreWindowsMutex"); 488 | if(hGlobalLock == INVALID_HANDLE_VALUE || GetLastError() == ERROR_ALREADY_EXISTS) 489 | return 0; 490 | 491 | const char* tok = 0; 492 | char* context = NULL; 493 | const char* delims = " =\t"; 494 | tok = strtok_s(lpCmdLine, delims, &context); 495 | while(tok) 496 | { 497 | if(!_stricmp(tok, "--debuglog")) 498 | { 499 | _sopen_s(&logfile, "RestoreWindows.log", _O_WRONLY | _O_BINARY | _O_TRUNC | _O_CREAT, _SH_DENYNO, _S_IWRITE); 500 | } 501 | else if(!_stricmp(tok, "--delay")) 502 | { 503 | tok = strtok_s(0, delims, &context); 504 | if(tok) 505 | ResumeTimerValue = clamp(0, 1000*60*10, atoi(tok)); 506 | } 507 | tok = strtok_s(0, delims, &context); 508 | } 509 | 510 | DesiredMonitorPlacements = GetAllMonitors(); 511 | 512 | Log("Delay: %d\n\n", ResumeTimerValue); 513 | for(auto it = DesiredMonitorPlacements.begin(); it != DesiredMonitorPlacements.end(); ++it) 514 | Log("Monitor: %d, %d\n", it->right-it->left, it->bottom-it->top); 515 | Log("\n"); 516 | 517 | LoadAllWindowPlacements(); 518 | 519 | HWINEVENTHOOK hWinEventHook = SetWinEventHook(EVENT_MIN, EVENT_MAX, NULL, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT); 520 | DetectWindow = CreateDetectionWnd(); 521 | 522 | MSG msg; 523 | while(GetMessage(&msg, NULL, 0, 0) > 0) 524 | { 525 | DispatchMessage(&msg); 526 | } 527 | 528 | // Usually never gets here... 529 | UnhookWinEvent(hWinEventHook); 530 | _close(logfile); 531 | CloseHandle(hGlobalLock); 532 | 533 | return 0; 534 | } 535 | 536 | -------------------------------------------------------------------------------- /RestoreWindows/RestoreWindows.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {D319889C-58C2-477A-8234-3F920B308D56} 15 | Win32Proj 16 | RestoreWindows 17 | 10.0.16299.0 18 | 19 | 20 | 21 | Application 22 | true 23 | v141 24 | Unicode 25 | 26 | 27 | Application 28 | false 29 | v141 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | true 45 | 46 | 47 | false 48 | 49 | 50 | 51 | 52 | 53 | Level3 54 | Disabled 55 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 56 | 57 | 58 | 59 | 60 | 61 | Windows 62 | true 63 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 64 | 65 | 66 | 67 | 68 | Level3 69 | 70 | 71 | MinSpace 72 | true 73 | true 74 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 75 | 76 | 77 | MultiThreadedDLL 78 | false 79 | Sync 80 | true 81 | None 82 | 83 | 84 | 85 | Windows 86 | false 87 | true 88 | true 89 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 90 | 91 | 92 | copy $(OutDir)$(TargetName)$(TargetExt) $(OutDir)..\bin\$(TargetName)$(TargetExt) 93 | 94 | 95 | Copy to bin 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /RestoreWindows/RestoreWindows.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /RestoreWindows/RestoreWindows.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | WindowsLocalDebugger 7 | $(TargetPath) 8 | 9 | 10 | --delay 4000 --debuglog 11 | WindowsLocalDebugger 12 | 13 | -------------------------------------------------------------------------------- /bin/RestoreWindows.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gurrhack/RestoreWindows/30b6ee2b4eff877fc329c66ce2228af532ff0502/bin/RestoreWindows.exe -------------------------------------------------------------------------------- /bin/vcredist_x86.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gurrhack/RestoreWindows/30b6ee2b4eff877fc329c66ce2228af532ff0502/bin/vcredist_x86.exe --------------------------------------------------------------------------------