From b316e4ea68ecbb0d3fde26d6c87eab266b71478c Mon Sep 17 00:00:00 2001 From: Eun Date: Thu, 3 Mar 2016 18:12:20 +0100 Subject: [PATCH] Features of issues #3, #4 implemented --- .gitignore | 5 +- Hook/Hook.vcxproj | 3 + Hook/Hook.vcxproj.filters | 5 + Hook/VirtualDesktops.h | 2 +- Hook/dll.cpp | 25 --- Hook/hook.cpp | 259 ++++++++++++++++++++-------- Hook/hook.h | 64 +++++++ LICENSE | 2 +- MoveToDesktop.ini | 6 + MoveToDesktop.sln | 8 +- MoveToDesktop/MoveToDesktop.vcxproj | 6 + MoveToDesktop/hideim.h | 2 +- MoveToDesktop/main.cpp | 131 ++++++++++---- MoveToDesktop/resource.aps | Bin 122644 -> 0 bytes MoveToDesktop/resource.h | 2 +- MoveToDesktop/resource.rc | Bin 3452 -> 3472 bytes README.md | 12 +- Utils/close_all.cpp | 147 ++++++++++++++++ 18 files changed, 532 insertions(+), 147 deletions(-) delete mode 100644 Hook/dll.cpp create mode 100644 Hook/hook.h create mode 100644 MoveToDesktop.ini delete mode 100644 MoveToDesktop/resource.aps create mode 100644 Utils/close_all.cpp diff --git a/.gitignore b/.gitignore index 064db0e..aba634f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ Release/ Debug/ x64/ +Utils/*.obj +Utils/*.exe *.opensdf *.sdf *.suo -*.dll \ No newline at end of file +*.dll +*.aps \ No newline at end of file diff --git a/Hook/Hook.vcxproj b/Hook/Hook.vcxproj index b32a68d..74c357e 100644 --- a/Hook/Hook.vcxproj +++ b/Hook/Hook.vcxproj @@ -169,6 +169,9 @@ + + + diff --git a/Hook/Hook.vcxproj.filters b/Hook/Hook.vcxproj.filters index c6375b3..de85191 100644 --- a/Hook/Hook.vcxproj.filters +++ b/Hook/Hook.vcxproj.filters @@ -25,4 +25,9 @@ + + + Header Files + + \ No newline at end of file diff --git a/Hook/VirtualDesktops.h b/Hook/VirtualDesktops.h index 66c83bf..e830012 100644 --- a/Hook/VirtualDesktops.h +++ b/Hook/VirtualDesktops.h @@ -1,7 +1,7 @@ /** * MoveToDesktop * -* Copyright (C) 2015 by Tobias Salzmann +* Copyright (C) 2015-2016 by Tobias Salzmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/Hook/dll.cpp b/Hook/dll.cpp deleted file mode 100644 index 17b74cc..0000000 --- a/Hook/dll.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/** -* MoveToDesktop -* -* Copyright (C) 2015 by Tobias Salzmann -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - - -#include -#include "hook.h" - - - diff --git a/Hook/hook.cpp b/Hook/hook.cpp index 907ea4c..6e20dcf 100644 --- a/Hook/hook.cpp +++ b/Hook/hook.cpp @@ -1,7 +1,7 @@ /** * MoveToDesktop * -* Copyright (C) 2015 by Tobias Salzmann +* Copyright (C) 2015-2016 by Tobias Salzmann * Copyright (C) 2008-2011 by Manuel Meitinger * * This program is free software: you can redistribute it and/or modify @@ -22,48 +22,9 @@ #include #include - +#include #include "VirtualDesktops.h" - -#ifdef _DEBUG -#define LOGFILE "%temp%\\MoveToDesktop.log" -void Log(char *message) -{ - FILE *file; - - TCHAR logFile[MAX_PATH] = {0}; - ExpandEnvironmentStrings(LOGFILE, logFile, _countof(logFile)); - file = fopen(logFile, "a"); - - if (file == NULL) { - return; - } - else - { - char exepath[MAX_PATH]; - char msg[1024] = ""; - GetModuleFileName(0, exepath, MAX_PATH); - sprintf(msg, "%s: %s\n", exepath, message); - fputs(msg, file); - fclose(file); - } - - if (file) - fclose(file); -} -#else -#define Log -#endif - - - -#define MAXDESKTOPS 255 -#define MOVETOMENU_ID (0xDE100) -#define MOVETOMENU_NEW (MOVETOMENU_ID + 1) -#define MOVETOMENU_START (MOVETOMENU_NEW + 1) -#define MOVETOMENU_LAST (MOVETOMENU_START + MAXDESKTOPS) - - +#include "hook.h" IServiceProvider* pServiceProvider = nullptr; IVirtualDesktopManager *pDesktopManager = nullptr; @@ -79,21 +40,37 @@ enum EComStatus int ComStatus = COMSTATUS_UNINITIALIZED; bool bAddedMenu = false; +bool bReadIni = false; +bool bSwitchDesktopAfterMove = false; + +#define INIFILE "%APPDATA%\\MoveToDesktop.ini" BOOL InitCom() { + Log("Initalizing Com"); if (ComStatus == COMSTATUS_INITIALIZED) + { + Log("> Allready Initialized!"); return true; + } else if (ComStatus == COMSTATUS_ERROR) + { + Log("> Allready tried to initialize but it failed."); return false; + } ComStatus = COMSTATUS_ERROR; - ::CoInitialize(NULL); - - HRESULT hr = ::CoCreateInstance(CLSID_ImmersiveShell, NULL, CLSCTX_LOCAL_SERVER, __uuidof(IServiceProvider), (PVOID*)&pServiceProvider); + HRESULT hr = ::CoInitialize(NULL); + if (FAILED(hr)) + { + Log("> CoInitialize failed: %X", hr); + return FALSE; + } + hr = ::CoCreateInstance(CLSID_ImmersiveShell, NULL, CLSCTX_LOCAL_SERVER, __uuidof(IServiceProvider), (PVOID*)&pServiceProvider); if (FAILED(hr)) { + Log("> CoCreateInstance failed: %X", hr); return FALSE; } @@ -102,7 +79,7 @@ BOOL InitCom() hr = pServiceProvider->QueryService(__uuidof(IVirtualDesktopManager), &pDesktopManager); if (FAILED(hr)) { - + Log("> QueryService(DesktopManager) failed"); pServiceProvider->Release(); pServiceProvider = nullptr; return FALSE; @@ -113,7 +90,7 @@ BOOL InitCom() hr = pServiceProvider->QueryService(CLSID_VirtualDesktopAPI_Unknown, &pDesktopManagerInternal); if (FAILED(hr)) { - + Log("> QueryService(DesktopManagerInternal) failed"); pDesktopManager->Release(); pDesktopManager = nullptr; pServiceProvider->Release(); @@ -121,7 +98,6 @@ BOOL InitCom() return FALSE; } - ComStatus = COMSTATUS_INITIALIZED; return TRUE; } @@ -137,6 +113,17 @@ VOID FreeCom() } } +VOID ReadIni() +{ + if (bReadIni == true) + return; + bReadIni = true; + Log("Reading Ini"); + TCHAR iniFile[MAX_PATH] = { 0 }; + ExpandEnvironmentStrings(INIFILE, iniFile, _countof(iniFile)); + bSwitchDesktopAfterMove = (GetPrivateProfileInt("MoveToDesktop", "SwitchDesktopAfterMove", 0, iniFile) != 0); + Log("Ini: SwitchDesktopAfterMove = %d", (bSwitchDesktopAfterMove ? 1 : 0)); +} INT GetIndexOfItem(HMENU menu, UINT id) { @@ -151,8 +138,7 @@ INT GetIndexOfItem(HMENU menu, UINT id) return -1; } - -void AddMenu(HWND hwnd, HMENU menu) +VOID AddMenu(HWND hwnd, HMENU menu) { if (bAddedMenu == true) return; @@ -207,7 +193,7 @@ void AddMenu(HWND hwnd, HMENU menu) MoveToItem.fMask = MIIM_SUBMENU | MIIM_STATE | MIIM_ID | MIIM_STRING; Log("Add MoveToMenu"); MoveToItem.wID = MOVETOMENU_ID; - MoveToItem.dwTypeData = TEXT("Move To"); + MoveToItem.dwTypeData = TEXT("Move &To"); MoveToItem.hSubMenu = CreateMenu(); InsertMenuItem(systemMenu, SC_CLOSE, FALSE, &MoveToItem); @@ -221,7 +207,7 @@ void AddMenu(HWND hwnd, HMENU menu) char desktopName[64] = { 0 }; - sprintf_s(desktopName, sizeof(desktopName), "Desktop %d", i + 1); + sprintf_s(desktopName, sizeof(desktopName), "Desktop &%d", i + 1); MENUITEMINFO item = { 0 }; item.cbSize = sizeof(item); @@ -240,7 +226,7 @@ void AddMenu(HWND hwnd, HMENU menu) item.fMask = MIIM_ID | MIIM_STRING; item.fState = MFS_UNCHECKED; item.wID = MOVETOMENU_NEW; - item.dwTypeData = TEXT("New Desktop"); + item.dwTypeData = TEXT("&New Desktop"); InsertMenuItem(MoveToItem.hSubMenu, -1, FALSE, &item); } @@ -253,7 +239,7 @@ void AddMenu(HWND hwnd, HMENU menu) } } -void RemoveMenu(HWND hwnd, HMENU menu) +VOID RemoveMenu(HWND hwnd, HMENU menu) { if (bAddedMenu == false) return; @@ -279,16 +265,80 @@ void RemoveMenu(HWND hwnd, HMENU menu) } Log("Remove MoveToMenu"); DestroyMenu(MoveToItem.hSubMenu); - DeleteMenu(systemMenu, MoveToItem.wID, MF_BYCOMMAND); - - - + DeleteMenu(systemMenu, MoveToItem.wID, MF_BYCOMMAND); +} + +INT GetCurrentDesktopIndex(UINT *count) +{ + *count = 0; + IObjectArray *pObjectArray = nullptr; + IVirtualDesktop *pCurrentDesktop = nullptr; + + if (!InitCom()) + { + Log("InitCom failed"); + return -1; + } + + HRESULT hr = pDesktopManagerInternal->GetDesktops(&pObjectArray); + if (FAILED(hr)) + { + Log("pDesktopManagerInternal->GetDesktops failed %x", hr); + return -1; + } + + hr = pObjectArray->GetCount(count); + if (FAILED(hr)) + { + Log("pObjectArray->GetCount failed %x", hr); + pObjectArray->Release(); + return -1; + } + + + hr = pDesktopManagerInternal->GetCurrentDesktop(&pCurrentDesktop); + if (FAILED(hr)) + { + Log("pDesktopManagerInternal->GetCurrentDesktop failed %x", hr); + pObjectArray->Release(); + return -1; + } + + int index = -1; + for (UINT i = 0; i < *count && i < MAXDESKTOPS && index == -1; ++i) + { + IVirtualDesktop *pDesktop = nullptr; + + if (FAILED(pObjectArray->GetAt(i, __uuidof(IVirtualDesktop), (void**)&pDesktop))) + continue; + if (pDesktop == pCurrentDesktop) + { + index = MOVETOMENU_START + i; + } + pDesktop->Release(); + } + + pObjectArray->Release(); + + if (pCurrentDesktop != nullptr) + { + pCurrentDesktop->Release(); + } + return index; } void HandleSysCommand(WPARAM wParam, HWND hwnd) { if (wParam == MOVETOMENU_NEW) { + Log("Getting RootWindow of %X", hwnd); + HWND rootHwnd = GetAncestor(hwnd, GA_ROOTOWNER); + if (rootHwnd != NULL) + { + hwnd = rootHwnd; + } + + Log("Moving %X to new", hwnd); IVirtualDesktop *pNewDesktop = nullptr; HRESULT hr = pDesktopManagerInternal->CreateDesktopW(&pNewDesktop); if (FAILED(hr)) @@ -299,23 +349,37 @@ void HandleSysCommand(WPARAM wParam, HWND hwnd) hr = pNewDesktop->GetID(&id); if (SUCCEEDED(hr)) { - pDesktopManager->MoveWindowToDesktop(hwnd, id); + Log("pDesktopManager->MoveWindowToDesktop(%X, %X)", hwnd, id); + hr = pDesktopManager->MoveWindowToDesktop(hwnd, id); + if (SUCCEEDED(hr)) + { + if (bSwitchDesktopAfterMove) + { + pDesktopManagerInternal->SwitchDesktop(pNewDesktop); + } + } + else + { + Log("Error %d on moving %X to %X", hr, hwnd, id); + } } pNewDesktop->Release(); } else if (wParam >= MOVETOMENU_START && wParam <= MOVETOMENU_LAST) { - HMENU systemMenu; - if ((systemMenu = GetSystemMenu(hwnd, FALSE)) == NULL) + Log("Getting RootWindow of %X", hwnd); + HWND rootHwnd = GetAncestor(hwnd, GA_ROOTOWNER); + if (rootHwnd != NULL) { - return; + hwnd = rootHwnd; } - + Log("Moving %X to %X", hwnd, wParam); IObjectArray *pObjectArray = nullptr; HRESULT hr = pDesktopManagerInternal->GetDesktops(&pObjectArray); if (FAILED(hr)) { + Log("Failed to get desktops for %X", hwnd); return; } @@ -327,12 +391,59 @@ void HandleSysCommand(WPARAM wParam, HWND hwnd) if (SUCCEEDED(hr)) { - pDesktopManager->MoveWindowToDesktop(hwnd, id); + Log("pDesktopManager->MoveWindowToDesktop(%X, %X)", hwnd, id); + hr = pDesktopManager->MoveWindowToDesktop(hwnd, id); + if (SUCCEEDED(hr)) + { + if (bSwitchDesktopAfterMove) + { + pDesktopManagerInternal->SwitchDesktop(pDesktop); + } + } + else + { + Log("Error %X on moving %X to %X", hr, hwnd, id); + } } pDesktop->Release(); } pObjectArray->Release(); } + else if (wParam == MOVETOMENU_LEFT) + { + UINT count; + int index = GetCurrentDesktopIndex(&count); + Log("Current Index is %d", index); + Log("Current Count is %d", count); + if (index == -1) + return; + if (index == MOVETOMENU_START) + return; + Log("Switch to %d", index - 1); + HandleSysCommand(--index, hwnd); + } + else if (wParam == MOVETOMENU_RIGHT) + { + UINT count; + int index = GetCurrentDesktopIndex(&count); + Log("Current Index is %d", index); + Log("Current Count is %d", count); + if (index == -1) + return; + if (index == MOVETOMENU_LAST) + return; + if ((++index) <= (int)(count + MOVETOMENU_NEW)) + { + Log("Switch to %d", index); + HandleSysCommand(index, hwnd); + + } + else + { + Log("Create new desktop"); + HandleSysCommand(MOVETOMENU_NEW, hwnd); + } + } } LRESULT CALLBACK CallWndProc(INT code, WPARAM wParam, LPARAM lParam) @@ -392,7 +503,7 @@ LRESULT CALLBACK CallWndProc(INT code, WPARAM wParam, LPARAM lParam) // Do the command case WM_SYSCOMMAND: { - Log("WM_SYSCOMMAND"); + Log("WM_SYSCOMMAND %X %X", msg->wParam, msg->hwnd); HandleSysCommand(msg->wParam, msg->hwnd); break; } @@ -405,21 +516,27 @@ LRESULT CALLBACK CallWndProc(INT code, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK GetMsgProc(INT code, WPARAM wParam, LPARAM lParam) { #define msg ((PMSG)lParam) - - if (code == HC_ACTION && msg->message == WM_SYSCOMMAND) + if (code == HC_ACTION) { - Log("WM_SYSCOMMAND"); - HandleSysCommand(msg->wParam, msg->hwnd); + switch (msg->message) + { + case WM_SYSCOMMAND: + { + Log("WM_SYSCOMMAND"); + HandleSysCommand(msg->wParam, msg->hwnd); + break; + } + } } - return CallNextHookEx(NULL, code, wParam, lParam); + return CallNextHookEx(NULL, code, wParam, lParam); #undef msg } - BOOL WINAPI DllMain(HINSTANCE handle, DWORD dwReason, LPVOID reserved) { if (dwReason == DLL_PROCESS_ATTACH) { + ReadIni(); } else if (dwReason == DLL_PROCESS_DETACH) { diff --git a/Hook/hook.h b/Hook/hook.h new file mode 100644 index 0000000..dbbdb85 --- /dev/null +++ b/Hook/hook.h @@ -0,0 +1,64 @@ +/** +* MoveToDesktop +* +* Copyright (C) 2015-2016 by Tobias Salzmann +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +#define MAXDESKTOPS 255 +#define MOVETOMENU_ID (0xDE100) +#define MOVETOMENU_NEW (MOVETOMENU_ID + 1) +#define MOVETOMENU_START (MOVETOMENU_NEW + 1) +#define MOVETOMENU_LAST (MOVETOMENU_START + MAXDESKTOPS) +#define MOVETOMENU_LEFT (MOVETOMENU_LAST + 1) +#define MOVETOMENU_RIGHT (MOVETOMENU_LEFT + 1) + + +#ifdef _DEBUG +#define LOGFILE "%temp%\\MoveToDesktop.log" +#include +inline void Log(char *message, ...) +{ + FILE *file; + + TCHAR logFile[MAX_PATH] = { 0 }; + ExpandEnvironmentStrings(LOGFILE, logFile, _countof(logFile)); + fopen_s(&file, logFile, "a"); + + if (file == NULL) { + return; + } + else + { + char exepath[MAX_PATH]; + char inpmsg[1024] = ""; + char finalmsg[1024 + MAX_PATH] = ""; + va_list ptr; + va_start(ptr, message); + vsprintf_s(inpmsg, sizeof(inpmsg), message, ptr); + va_end(ptr); + GetModuleFileName(0, exepath, MAX_PATH); + + sprintf_s(finalmsg, sizeof(finalmsg), "%s: %s\n", exepath, inpmsg); + fputs(finalmsg, file); + fclose(file); + } + + if (file) + fclose(file); +} +#else +#define Log +#endif \ No newline at end of file diff --git a/LICENSE b/LICENSE index 8cb79ca..b136b75 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ /** * MoveToDesktop * -* Copyright (C) 2015 by Tobias Salzmann +* Copyright (C) 2015-2016 by Tobias Salzmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/MoveToDesktop.ini b/MoveToDesktop.ini new file mode 100644 index 0000000..c18e41a --- /dev/null +++ b/MoveToDesktop.ini @@ -0,0 +1,6 @@ +; +; Place this file in %AppData% +; +[MoveToDesktop] +; Switch Desktop after window was moved +SwitchDesktopAfterMove=1 \ No newline at end of file diff --git a/MoveToDesktop.sln b/MoveToDesktop.sln index 64fa05f..50a3157 100644 --- a/MoveToDesktop.sln +++ b/MoveToDesktop.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MoveToDesktop", "MoveToDesktop\MoveToDesktop.vcxproj", "{47BF4015-D8CF-4B07-B657-7E7ABDB72FDC}" ProjectSection(ProjectDependencies) = postProject @@ -12,30 +12,24 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hook", "Hook\Hook.vcxproj", EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {47BF4015-D8CF-4B07-B657-7E7ABDB72FDC}.Debug|Any CPU.ActiveCfg = Debug|Win32 {47BF4015-D8CF-4B07-B657-7E7ABDB72FDC}.Debug|x64.ActiveCfg = Debug|x64 {47BF4015-D8CF-4B07-B657-7E7ABDB72FDC}.Debug|x64.Build.0 = Debug|x64 {47BF4015-D8CF-4B07-B657-7E7ABDB72FDC}.Debug|x86.ActiveCfg = Debug|Win32 {47BF4015-D8CF-4B07-B657-7E7ABDB72FDC}.Debug|x86.Build.0 = Debug|Win32 - {47BF4015-D8CF-4B07-B657-7E7ABDB72FDC}.Release|Any CPU.ActiveCfg = Release|Win32 {47BF4015-D8CF-4B07-B657-7E7ABDB72FDC}.Release|x64.ActiveCfg = Release|x64 {47BF4015-D8CF-4B07-B657-7E7ABDB72FDC}.Release|x64.Build.0 = Release|x64 {47BF4015-D8CF-4B07-B657-7E7ABDB72FDC}.Release|x86.ActiveCfg = Release|Win32 {47BF4015-D8CF-4B07-B657-7E7ABDB72FDC}.Release|x86.Build.0 = Release|Win32 - {88D0BB04-305D-47DC-B4BD-9D3398FAFFE6}.Debug|Any CPU.ActiveCfg = Debug|Win32 {88D0BB04-305D-47DC-B4BD-9D3398FAFFE6}.Debug|x64.ActiveCfg = Debug|x64 {88D0BB04-305D-47DC-B4BD-9D3398FAFFE6}.Debug|x64.Build.0 = Debug|x64 {88D0BB04-305D-47DC-B4BD-9D3398FAFFE6}.Debug|x86.ActiveCfg = Debug|Win32 {88D0BB04-305D-47DC-B4BD-9D3398FAFFE6}.Debug|x86.Build.0 = Debug|Win32 - {88D0BB04-305D-47DC-B4BD-9D3398FAFFE6}.Release|Any CPU.ActiveCfg = Release|Win32 {88D0BB04-305D-47DC-B4BD-9D3398FAFFE6}.Release|x64.ActiveCfg = Release|x64 {88D0BB04-305D-47DC-B4BD-9D3398FAFFE6}.Release|x64.Build.0 = Release|x64 {88D0BB04-305D-47DC-B4BD-9D3398FAFFE6}.Release|x86.ActiveCfg = Release|Win32 diff --git a/MoveToDesktop/MoveToDesktop.vcxproj b/MoveToDesktop/MoveToDesktop.vcxproj index 985c3f9..0013945 100644 --- a/MoveToDesktop/MoveToDesktop.vcxproj +++ b/MoveToDesktop/MoveToDesktop.vcxproj @@ -86,6 +86,9 @@ + + taskkill /f /fi "IMAGENAME eq MoveToDesktop*" /im * + @@ -99,6 +102,9 @@ _DEBUG; _WIN64 + + taskkill /f /fi "IMAGENAME eq MoveToDesktop*" /im * + diff --git a/MoveToDesktop/hideim.h b/MoveToDesktop/hideim.h index 2fac7ab..2bbca00 100644 --- a/MoveToDesktop/hideim.h +++ b/MoveToDesktop/hideim.h @@ -1,7 +1,7 @@ /** * HideImports 1.0 * -* Copyright (C) 2015 by Tobias Salzmann +* Copyright (C) 2015-2016 by Tobias Salzmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/MoveToDesktop/main.cpp b/MoveToDesktop/main.cpp index 9d7cd97..d284ed2 100644 --- a/MoveToDesktop/main.cpp +++ b/MoveToDesktop/main.cpp @@ -1,7 +1,7 @@ /** * MoveToDesktop * -* Copyright (C) 2015 by Tobias Salzmann +* Copyright (C) 2015-2016 by Tobias Salzmann * Copyright (C) 2008-2011 by Manuel Meitinger * * This program is free software: you can redistribute it and/or modify @@ -23,6 +23,7 @@ #include "resource.h" #include "hideim.h" #include +#include "../Hook/hook.h" #ifdef _WIN64 @@ -33,6 +34,7 @@ #define TITLE "MoveToDesktop" #endif +bool bKeyDown = false; bool ExtractResource(const HINSTANCE hInstance, WORD resourceID, LPCTSTR szFilename) { @@ -60,7 +62,6 @@ bool ExtractResource(const HINSTANCE hInstance, WORD resourceID, LPCTSTR szFilen { return false; } - } BOOL IsWow64() @@ -88,6 +89,45 @@ BOOL IsWow64() } +LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam) +{ +#define msg ((PKBDLLHOOKSTRUCT)lParam) + if (code == HC_ACTION) + { + if (wParam == WM_SYSKEYDOWN) + { + if (msg->vkCode == VK_RIGHT) + { + if (bKeyDown == false && ((GetKeyState(VK_LMENU) && GetKeyState(VK_LWIN)) || (GetKeyState(VK_RMENU) && GetKeyState(VK_RWIN)))) + { + bKeyDown = true; + Log("Sending to %X: MOVETOMENU_RIGHT", GetForegroundWindow()); + PostMessage(GetForegroundWindow(), WM_SYSCOMMAND, MOVETOMENU_RIGHT, 0); + return 1; + } + } + else if (msg->vkCode == VK_LEFT) + { + if (bKeyDown == false && ((GetKeyState(VK_LMENU) && GetKeyState(VK_LWIN)) || (GetKeyState(VK_RMENU) && GetKeyState(VK_RWIN)))) + { + bKeyDown = true; + Log("Sending to %X: MOVETOMENU_LEFT", GetForegroundWindow()); + PostMessage(GetForegroundWindow(), WM_SYSCOMMAND, MOVETOMENU_LEFT, 0); + return 1; + } + } + } + else if (wParam == WM_SYSKEYUP) + { + if (msg->vkCode == VK_RIGHT || msg->vkCode == VK_LEFT) + { + bKeyDown = false; + } + } + } + return CallNextHookEx(NULL, code, wParam, lParam); +#undef msg +} INT WINAPI WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, INT cmdShow) { @@ -130,7 +170,6 @@ INT WINAPI WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, IN return 1; } - // Extract 64bit version #ifdef _WIN32 #ifndef _DEBUG @@ -152,90 +191,106 @@ INT WINAPI WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, IN #endif #endif - - - + DWORD error; HMODULE library; // load the hook library if ((library = LoadLibrary(szTempFileName)) == NULL) { - DWORD error = GetLastError(); + error = GetLastError(); char buffer[128] = ""; sprintf_s(buffer, sizeof(buffer), "Could not load hook!\nErrorCode: %d", error); MessageBox(0, buffer, TITLE, MB_OK | MB_ICONERROR); return error; } - - __try + try { HOOKPROC callWndProc; HHOOK callWndHook; if ((callWndProc = (HOOKPROC)GetProcAddress(library, "CallWndProc")) == NULL) { - DWORD error = GetLastError(); - MessageBox(0, "Could not find CallWndProc in hook!", TITLE, MB_OK | MB_ICONERROR); - return error; + error = GetLastError(); + throw "Could not find CallWndProc in hook!"; } if ((callWndHook = SetWindowsHookExA(WH_CALLWNDPROC, callWndProc, library, 0)) == NULL) { - DWORD error = GetLastError(); + error = GetLastError(); char buffer[128] = ""; sprintf_s(buffer, sizeof(buffer), "Error on calling SetWindowsHookEx(WH_CALLWNDPROC)!\nErrorCode: %d", error); - MessageBox(0, buffer, TITLE, MB_OK | MB_ICONERROR); - return error; + throw buffer; } - __try + try { HOOKPROC getMsgProc; HHOOK getMsgHook; if ((getMsgProc = (HOOKPROC)GetProcAddress(library, "GetMsgProc")) == NULL) { - DWORD error = GetLastError(); - MessageBox(0, "Could not find GetMsgProc in hook.dll!", TITLE, MB_OK | MB_ICONERROR); - return error; + error = GetLastError(); + throw "Could not find GetMsgProc in hook.dll!"; } if ((getMsgHook = SetWindowsHookExA(WH_GETMESSAGE, getMsgProc, library, 0)) == NULL) { - DWORD error = GetLastError(); + error = GetLastError(); char buffer[128] = ""; sprintf_s(buffer, sizeof(buffer), "Error on calling SetWindowsHookEx(WH_GETMESSAGE)!\nErrorCode: %d", error); - MessageBox(0, buffer, TITLE, MB_OK | MB_ICONERROR); - return error; + throw buffer; } - __try + + try { - MSG msg; - BOOL ret; + HHOOK lowLevelKeyboardHook; - // pump the messages until the end of the session - while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0) + if ((lowLevelKeyboardHook = SetWindowsHookExA(WH_KEYBOARD_LL, LowLevelKeyboardProc, library, 0)) == NULL) + { + error = GetLastError(); + char buffer[128] = ""; + sprintf_s(buffer, sizeof(buffer), "Error on calling SetWindowsHookEx(WH_KEYBOARD_LL)!\nErrorCode: %d", error); + throw buffer; + } + try { - if (ret == -1) + MSG msg; + BOOL ret; + + // pump the messages until the end of the session + while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0) { - return GetLastError(); + if (ret == -1) + { + return GetLastError(); + } + TranslateMessage(&msg); + DispatchMessage(&msg); } - TranslateMessage(&msg); - DispatchMessage(&msg); } + catch(char *buffer) + { + MessageBox(0, buffer, TITLE, MB_OK | MB_ICONERROR); + } + UnhookWindowsHookEx(lowLevelKeyboardHook); } - __finally + catch (char *buffer) { - UnhookWindowsHookEx(getMsgHook); + MessageBox(0, buffer, TITLE, MB_OK | MB_ICONERROR); } + UnhookWindowsHookEx(getMsgHook); } - __finally + catch (char *buffer) { - UnhookWindowsHookEx(callWndHook); + MessageBox(0, buffer, TITLE, MB_OK | MB_ICONERROR); } + UnhookWindowsHookEx(callWndHook); } - __finally { - FreeLibrary(library); + catch (char *buffer) + { + MessageBox(0, buffer, TITLE, MB_OK | MB_ICONERROR); } + FreeLibrary(library); + ReleaseMutex(mutex); // return success return ERROR_SUCCESS; -} \ No newline at end of file +} diff --git a/MoveToDesktop/resource.aps b/MoveToDesktop/resource.aps deleted file mode 100644 index 5ad98a9749702d719b390b06c47efb40c8accba5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 122644 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 diff --git a/MoveToDesktop/resource.h b/MoveToDesktop/resource.h index 825f5e0..305dc07 100644 --- a/MoveToDesktop/resource.h +++ b/MoveToDesktop/resource.h @@ -1,7 +1,7 @@ /** * MoveToDesktop * -* Copyright (C) 2015 by Tobias Salzmann +* Copyright (C) 2015-2016 by Tobias Salzmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/MoveToDesktop/resource.rc b/MoveToDesktop/resource.rc index b2d38024d743e9d93779023f37746efd07b91b4d..6152776f6f1b85f07469bd67a28225f48b87f8e3 100644 GIT binary patch delta 50 ycmew(H9>kp45u!G5rYAPA%oe*=wuE?!^!(OOc@O~f8|JIMo3MJR$pAh!w3LQ#SCcx delta 32 ncmbOr{YPp-%;YQ{wT(SC9E=8&PjZ+t8f^a0k;uFmD98u^!y5|6 diff --git a/README.md b/README.md index b0f4959..9f5634c 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,25 @@ Adds the Move to Desktop feature to the Windows 10 System menu: ![](https://raw.githubusercontent.com/Eun/MoveToDesktop/res/screenshot1.png) -[![Download](https://raw.githubusercontent.com/Eun/MoveToDesktop/res/download.png)](https://github.com/Eun/MoveToDesktop/releases/download/1.0/MoveToDesktop-1.0.zip) +[![Download](https://raw.githubusercontent.com/Eun/MoveToDesktop/res/download.png)](https://github.com/Eun/MoveToDesktop/releases/download/1.1/MoveToDesktop-1.1.zip) Installation Usage ====== Download and Run. +You can also move windows by using WIN+ALT+Left/Right You might need to install the x86 **and** x64 version of [Visual C++ Redistributable for Visual Studio 2015](https://www.microsoft.com/download/details.aspx?id=48145). +Settings +======== +You can place the [MoveToDesktop.ini](MoveToDesktop.ini) into `%AppData` and modify the settings in it. Changelog ========= +1.1: +* Hotkey +* Keyboard accelators +* Switch desktop after move +* Settings Ini File + 1.0: * Release diff --git a/Utils/close_all.cpp b/Utils/close_all.cpp new file mode 100644 index 0000000..f303d37 --- /dev/null +++ b/Utils/close_all.cpp @@ -0,0 +1,147 @@ +/** +* MoveToDesktop +* +* Copyright (C) 2015-2016 by Tobias Salzmann +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ +// compile with cl.exe close_all.cpp +// description: Closes all virtual desktops +#include +#include +#include "../Hook/VirtualDesktops.h" + +#pragma comment (lib, "ole32.lib") + +IServiceProvider* pServiceProvider = nullptr; +IVirtualDesktopManager *pDesktopManager = nullptr; +IVirtualDesktopManagerInternal* pDesktopManagerInternal = nullptr; + +enum EComStatus +{ + COMSTATUS_UNINITIALIZED, + COMSTATUS_INITIALIZED, + COMSTATUS_ERROR, +}; + +int ComStatus = COMSTATUS_UNINITIALIZED; + + +BOOL InitCom() +{ + if (ComStatus == COMSTATUS_INITIALIZED) + return true; + else if (ComStatus == COMSTATUS_ERROR) + return false; + + ComStatus = COMSTATUS_ERROR; + ::CoInitialize(NULL); + + HRESULT hr = ::CoCreateInstance(CLSID_ImmersiveShell, NULL, CLSCTX_LOCAL_SERVER, __uuidof(IServiceProvider), (PVOID*)&pServiceProvider); + + if (FAILED(hr)) + { + return FALSE; + } + + + + hr = pServiceProvider->QueryService(__uuidof(IVirtualDesktopManager), &pDesktopManager); + if (FAILED(hr)) + { + + pServiceProvider->Release(); + pServiceProvider = nullptr; + return FALSE; + } + + + + hr = pServiceProvider->QueryService(CLSID_VirtualDesktopAPI_Unknown, &pDesktopManagerInternal); + if (FAILED(hr)) + { + + pDesktopManager->Release(); + pDesktopManager = nullptr; + pServiceProvider->Release(); + pServiceProvider = nullptr; + return FALSE; + } + + + ComStatus = COMSTATUS_INITIALIZED; + return TRUE; +} + +VOID FreeCom() +{ + if (ComStatus == COMSTATUS_INITIALIZED) + { + pDesktopManager->Release(); + pDesktopManagerInternal->Release(); + pServiceProvider->Release(); + ComStatus = COMSTATUS_UNINITIALIZED; + } +} + +void main() +{ + InitCom(); + + + IObjectArray *pObjectArray = nullptr; + IVirtualDesktop *pCurrentDesktop = nullptr; + + HRESULT hr = pDesktopManagerInternal->GetDesktops(&pObjectArray); + if (FAILED(hr)) + { + return; + } + + UINT count; + hr = pObjectArray->GetCount(&count); + if (FAILED(hr)) + { + pObjectArray->Release(); + return; + } + + + hr = pDesktopManagerInternal->GetCurrentDesktop(&pCurrentDesktop); + if (FAILED(hr)) + { + pCurrentDesktop = nullptr; + } + + + + for (UINT i = 0; i < count; ++i) + { + IVirtualDesktop *pDesktop = nullptr; + + if (FAILED(pObjectArray->GetAt(i, __uuidof(IVirtualDesktop), (void**)&pDesktop))) + continue; + + if (pDesktop != pCurrentDesktop) + { + printf("Closing %d\n",i); + pDesktopManagerInternal->RemoveDesktop(pDesktop, pCurrentDesktop); + } + pDesktop->Release(); + } + + pObjectArray->Release(); + + FreeCom(); +} \ No newline at end of file