Advertisement
Guest User

EdgeCloser v1.0

a guest
Jan 26th, 2017
847
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 12.91 KB | None | 0 0
  1. // EDGECLOSER v1.0
  2. // ---------------
  3. // The Windows 10 start menu wants to open Edge always, ignoring my choice of default browser.
  4. // This utility scans for opened web pages in Edge, closes it and reopens the page in your default browser.
  5. // EdgeCloser runs with a hidden window, so to stop it you need to kill it from Task Manager.
  6. // I am too lazy to add a taskbar icon.
  7. // No copyright, use this for whatever you want.
  8. // Phoenix
  9.  
  10. #include <windows.h>
  11. #include <UIAutomation.h>
  12. #include <time.h>
  13. #include <string>
  14. #include <algorithm>
  15.  
  16. #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
  17. #define CHECK_PTR(p) { if(SUCCEEDED(hr) && ((p) == NULL)) hr = E_FAIL; }
  18.  
  19. wchar_t *szWindowClass = L"EdgeCloserWndClass";
  20.  
  21. #define STRBUF_LEN 4095
  22.  
  23. const int TIMER_ID        = 1;
  24. const int ACTION_INTERVAL = 3500; // Courtesy delay in the polling after each browser triggering
  25. const int TIMER_INTERVAL  = 80;   // Poll windows every x ms
  26.  
  27. const long long MAX_DEFAULT_BROWSER_CHECK_INTERVAL = 4;
  28.  
  29. IUIAutomation *uiAutomation;
  30. HINSTANCE hInst;
  31.  
  32. int nextTrigger = 0;
  33.  
  34. VARIANT s_searchClassStr;
  35.  
  36. void LowerCase(std::wstring &str)
  37. {
  38.     std::transform(str.begin(), str.end(), str.begin(), ::tolower);
  39. }
  40.  
  41. bool GetRegistryString (HKEY rootKey,
  42.                         const std::wstring &keyName,
  43.                         const std::wstring &valueName,
  44.                         std::wstring &target)
  45. {
  46.     bool rc = false;
  47.     target.clear();
  48.  
  49.     HKEY hKey = 0;
  50.     if (ERROR_SUCCESS == RegOpenKeyExW(rootKey, keyName.c_str(), 0, KEY_READ, &hKey))
  51.     {
  52.         WCHAR buffer[STRBUF_LEN];
  53.         DWORD bufferSize = sizeof(buffer);
  54.  
  55.         if (ERROR_SUCCESS == RegQueryValueEx(hKey, valueName.c_str(), 0, NULL, (LPBYTE)buffer, &bufferSize))
  56.         {
  57.             target = buffer;
  58.             rc = true;
  59.         }
  60.  
  61.         RegCloseKey(hKey);
  62.     }
  63.  
  64.     return rc;
  65. }
  66.  
  67. bool ExpandEnvironmentStrings(std::wstring const &str, std::wstring &target)
  68. {
  69.     bool rc = false;
  70.     target.clear();
  71.     wchar_t buffer[STRBUF_LEN + 1];
  72.  
  73.     if (ExpandEnvironmentStrings(str.c_str(), buffer, STRBUF_LEN) != 0)
  74.     {
  75.         target = buffer;
  76.         rc = true;
  77.     }
  78.  
  79.     return rc;
  80. }
  81.  
  82. bool LoadDllString(const std::wstring &dllPath, UINT stringId, std::wstring &target)
  83. {
  84.     bool rc = false;
  85.     target.clear();
  86.  
  87.     HMODULE hDll = LoadLibrary(dllPath.c_str());
  88.     if (hDll != NULL)
  89.     {
  90.         wchar_t *strReadOnlyPointer = NULL;
  91.         if (LoadString(hDll, stringId, (LPWSTR)&strReadOnlyPointer, 0) > 0)
  92.         {
  93.             if (strReadOnlyPointer != NULL)
  94.             {
  95.                 target = strReadOnlyPointer;
  96.                 rc = true;
  97.             }
  98.         }
  99.         FreeLibrary(hDll);
  100.     }
  101.  
  102.     return rc;
  103. }
  104.  
  105. void HandleRegistryDllString(std::wstring &str)
  106. {
  107.     // Handle resource strings
  108.     // Format: "@<PE-path>,-<stringID>[;<optionalComment>]"
  109.     // Example: @C:\Windows\System32\ieframe.dll,-55175
  110.  
  111.     if (str.find(L"@") != 0) // First character must be '@'
  112.         return;
  113.  
  114.     const int MIN_DLL_PATH_LEN = 5;
  115.     size_t commaPos = str.find(L",-");
  116.     if ((commaPos != std::wstring::npos) && (commaPos >= MIN_DLL_PATH_LEN))
  117.     {
  118.         std::wstring dllPath  = str.substr(1, commaPos - 1);
  119.         std::wstring stringId = str.substr(commaPos + 2);
  120.  
  121.         // Remove any optional comments
  122.         size_t semiColPos = stringId.find(L";");
  123.         if ((semiColPos != std::wstring::npos) && (semiColPos >= 1)) // Require at least one digit
  124.             stringId = stringId.substr(0, semiColPos);
  125.  
  126.         long long strId = _wcstoi64(stringId.c_str(), NULL, 10);
  127.         if (strId > 0)
  128.         {
  129.             std::wstring dllPathEnvExpanded;
  130.             if (ExpandEnvironmentStrings(dllPath, dllPathEnvExpanded))
  131.             {
  132.                 std::wstring appNameFromDll;
  133.                 if (LoadDllString(dllPathEnvExpanded, (UINT)strId, appNameFromDll))
  134.                     str = appNameFromDll;
  135.             }
  136.         }
  137.     }
  138. }
  139.  
  140.  
  141. // Cache the result of inspecting the progId as this operation can be slow and resource consuming
  142. std::wstring browserCheckLastProgId;
  143. bool         browserCheckLastResult = false;
  144. __time64_t   lastBrowserCheck = 0;
  145.  
  146. bool IsDefaultBrowserEdge()
  147. {
  148.     bool isEdge = false;
  149.  
  150.     // Don't do this check too frequently
  151.     __time64_t now = _time64(NULL);
  152.     if(abs(now - lastBrowserCheck) < MAX_DEFAULT_BROWSER_CHECK_INTERVAL)
  153.         return browserCheckLastResult;
  154.     lastBrowserCheck = now;
  155.  
  156.     std::wstring progId;
  157.     if (GetRegistryString(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice", L"ProgId", progId))
  158.     {
  159.         const int MIN_PROGID_LENGTH = 3;
  160.         if (progId.length() >= MIN_PROGID_LENGTH)
  161.         {
  162.             if (progId == browserCheckLastProgId)
  163.             {
  164.                 // We recognize this progId, so use the cached result
  165.                 isEdge = browserCheckLastResult;
  166.             }
  167.             else
  168.             {
  169.                 std::wstring appNameKey = progId;
  170.                 appNameKey += L"\\Application";
  171.                 std::wstring appName;
  172.                 if (GetRegistryString(HKEY_CLASSES_ROOT, appNameKey, L"ApplicationName", appName))
  173.                 {
  174.                     HandleRegistryDllString(appName);
  175.  
  176.                     const size_t MINIMUM_APPNAME_LEN_SANITY_CHECK = 4;
  177.  
  178.                     size_t equalsPos = appName.find(L"=");
  179.                     if ((equalsPos != std::wstring::npos) && (equalsPos >= MINIMUM_APPNAME_LEN_SANITY_CHECK))
  180.                         appName = appName.substr(0, equalsPos);
  181.  
  182.                     LowerCase(appName);
  183.                     isEdge = (appName.find(L"edge") != std::wstring::npos);
  184.                 }
  185.  
  186.                 browserCheckLastProgId = progId;
  187.                 browserCheckLastResult = isEdge;
  188.             }
  189.         }
  190.     }
  191.  
  192.     return isEdge;
  193. }
  194.  
  195. BSTR UIAutomationSearcher(HWND hWnd)
  196. {
  197.     BSTR foundUri = NULL;
  198.     HRESULT hr = S_OK;
  199.  
  200.     IUIAutomationElement *edgeRoot = NULL;
  201.     IUIAutomationCacheRequest *cacheRequest = NULL;
  202.     IUIAutomationCondition *conditionUri = NULL;
  203.     IUIAutomationElement *uriElement = NULL;
  204.  
  205.     if (SUCCEEDED(hr))
  206.         hr = uiAutomation->ElementFromHandle(hWnd, &edgeRoot);
  207.     CHECK_PTR(edgeRoot);
  208.  
  209.     if (SUCCEEDED(hr))
  210.         hr = uiAutomation->CreateCacheRequest(&cacheRequest);
  211.     CHECK_PTR(cacheRequest);
  212.  
  213.     if (SUCCEEDED(hr))
  214.         hr = cacheRequest->AddProperty(UIA_NamePropertyId);
  215.  
  216.     if (SUCCEEDED(hr))
  217.         hr = uiAutomation->CreatePropertyCondition(UIA_ClassNamePropertyId, s_searchClassStr, &conditionUri);
  218.     CHECK_PTR(conditionUri);
  219.  
  220.     if (SUCCEEDED(hr))
  221.         hr = edgeRoot->FindFirstBuildCache(TreeScope_Descendants, conditionUri, cacheRequest, &uriElement);
  222.     CHECK_PTR(uriElement);
  223.  
  224.     if (SUCCEEDED(hr))
  225.     {
  226.         if (FAILED(uriElement->get_CachedName(&foundUri)))
  227.             foundUri = NULL; // Probably NULL already but make sure
  228.     }
  229.  
  230.     SAFE_RELEASE(uriElement);
  231.     SAFE_RELEASE(conditionUri);
  232.     SAFE_RELEASE(cacheRequest);
  233.     SAFE_RELEASE(edgeRoot);
  234.  
  235.     return foundUri;
  236. }
  237.  
  238. BOOL CALLBACK EdgeChildFinder(HWND hWnd, LPARAM lParam)
  239. {
  240.     BSTR *foundUri = (BSTR *)lParam;
  241.  
  242.     wchar_t buf[STRBUF_LEN + 1];
  243.  
  244.     if ((nextTrigger >= 0) && hWnd && ::IsWindowVisible(hWnd))
  245.     {
  246.         if (::GetClassName(hWnd, buf, STRBUF_LEN))
  247.         {
  248.             if (::wcscmp(buf, L"Windows.UI.Core.CoreWindow") == 0)
  249.             {
  250.                 if (::GetWindowText(hWnd, buf, STRBUF_LEN))
  251.                 {
  252.                     if (::wcscmp(buf, L"Microsoft Edge") == 0)
  253.                     {
  254.                         BSTR uri = UIAutomationSearcher(hWnd);
  255.                         if (uri != NULL)
  256.                         {
  257.                             // Allow manual start of Edge and an initial search
  258.                             if ((::wcsstr(uri, L"msn.com/spartan") != NULL) ||
  259.                                 (::wcsstr(uri, L"bing.com/AS/API") != NULL) ||
  260.                                 (::wcsstr(uri, L"about:blank") != NULL) ||
  261.                                 (::wcsstr(uri, L"bing.com/search?") != NULL))
  262.                             {
  263.  
  264.                             }
  265.                             else
  266.                             {
  267.                                 if ((*foundUri) == NULL)
  268.                                     *foundUri = ::SysAllocString(uri);
  269.                             }
  270.                             ::SysFreeString(uri);
  271.                         }
  272.                     }
  273.                 }
  274.             }
  275.         }
  276.     }
  277.     return TRUE;
  278. }
  279.  
  280. bool StartDefaultBrowser(BSTR uriBSTR, HWND edgeWindow)
  281. {
  282.     std::wstring uri(uriBSTR, SysStringLen(uriBSTR));
  283.  
  284.     // Try to make sure we are not launching some hacker-crafted special uri string
  285.     if (uri.find('\"') == std::wstring::npos)
  286.     {
  287.         std::wstring uriTL(uri);
  288.         LowerCase(uriTL);
  289.  
  290.         if ((0 == uriTL.find(L"http:")) ||
  291.             (0 == uriTL.find(L"https:")))
  292.         {
  293.             // Close the Edge window
  294.             // FIX!! - Dialog pops up if there are multiple tabs...
  295.             ::SendMessage(edgeWindow, WM_CLOSE, 0, 0);
  296.  
  297.             // Start the default browser
  298.             ShellExecute(NULL, L"open", uri.c_str(), NULL, NULL, SW_SHOWNORMAL);
  299.             return true;
  300.         }
  301.     }
  302.     return false;
  303. }
  304.  
  305. BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
  306. {
  307.     wchar_t buf[STRBUF_LEN + 1];
  308.  
  309.     if ((nextTrigger >= 0) && hWnd && ::IsWindowVisible(hWnd))
  310.     {
  311.         if (GetWindowText(hWnd, buf, STRBUF_LEN))
  312.         {
  313.             if (::wcsstr(buf, L"Microsoft Edge") != NULL)
  314.             {
  315.                 BSTR foundUri = NULL;
  316.                 EnumChildWindows(hWnd, EdgeChildFinder, (LPARAM)&foundUri);
  317.                 if (foundUri != NULL)
  318.                 {
  319.                     if (!IsDefaultBrowserEdge())
  320.                     {
  321.                         if (StartDefaultBrowser(foundUri, hWnd))
  322.                             nextTrigger = -(ACTION_INTERVAL / TIMER_INTERVAL);
  323.                     }
  324.  
  325.                     ::SysFreeString(foundUri);
  326.                 }
  327.             }
  328.         }
  329.     }
  330.     return TRUE;
  331. }
  332.  
  333. BOOL InitializeUIAutomation()
  334. {
  335.     CoInitialize(NULL);
  336.     HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER,
  337.         __uuidof(IUIAutomation), (void**)&uiAutomation);
  338.     return (SUCCEEDED(hr));
  339. }
  340. static bool inTimer = false;
  341. LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  342. {
  343.     switch (message)
  344.     {
  345.         case WM_CREATE:
  346.         {
  347.             SetTimer(hWnd, TIMER_ID, TIMER_INTERVAL, NULL);
  348.             break;
  349.         }
  350.  
  351.         case WM_TIMER:
  352.         {
  353.             if ((wParam == TIMER_ID) && (!inTimer))
  354.             {
  355.                 inTimer = true;
  356.  
  357.                 if (nextTrigger < 0)
  358.                     nextTrigger++;
  359.  
  360.                 if (nextTrigger >= 0)
  361.                     ::EnumWindows(EnumWindowsProc, 0);
  362.  
  363.                 inTimer = false;
  364.             }
  365.             break;
  366.         }
  367.  
  368.         case WM_DESTROY:
  369.         {
  370.             PostQuitMessage(0);
  371.             break;
  372.         }
  373.  
  374.         default:
  375.             return DefWindowProc(hWnd, message, wParam, lParam);
  376.     }
  377.     return 0;
  378. }
  379.  
  380. ATOM MyRegisterClass(HINSTANCE hInstance)
  381. {
  382.     WNDCLASSEXW wcex;
  383.     wcex.cbSize = sizeof(WNDCLASSEX);
  384.  
  385.     wcex.style = CS_HREDRAW | CS_VREDRAW;
  386.     wcex.lpfnWndProc = WndProc;
  387.     wcex.cbClsExtra = 0;
  388.     wcex.cbWndExtra = 0;
  389.     wcex.hInstance = hInstance;
  390.     wcex.hIcon = NULL;
  391.     wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
  392.     wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  393.     wcex.lpszMenuName = NULL;
  394.     wcex.lpszClassName = szWindowClass;
  395.     wcex.hIconSm = NULL;
  396.  
  397.     return RegisterClassExW(&wcex);
  398. }
  399.  
  400. BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
  401. {
  402.     hInst = hInstance;
  403.  
  404.     HWND hWnd = CreateWindowW(szWindowClass, L"EdgeCloser Hidden Window", WS_OVERLAPPEDWINDOW,
  405.         CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
  406.  
  407.     if (!hWnd)
  408.         return FALSE;
  409.  
  410.     nCmdShow = SW_HIDE; // Remove this line when testing/debugging
  411.  
  412.     ShowWindow(hWnd, nCmdShow);
  413.     UpdateWindow(hWnd);
  414.  
  415.     return TRUE;
  416. }
  417.  
  418. int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
  419.     _In_opt_ HINSTANCE hPrevInstance,
  420.     _In_ LPWSTR    lpCmdLine,
  421.     _In_ int       nCmdShow)
  422. {
  423.     UNREFERENCED_PARAMETER(hPrevInstance);
  424.     UNREFERENCED_PARAMETER(lpCmdLine);
  425.  
  426.     InitializeUIAutomation();
  427.  
  428.     s_searchClassStr.vt = VT_BSTR;
  429.     s_searchClassStr.bstrVal = ::SysAllocString(L"Internet Explorer_Server");
  430.  
  431.     MyRegisterClass(hInstance);
  432.  
  433.     if (!InitInstance(hInstance, nCmdShow))
  434.         return FALSE;
  435.  
  436.     MSG msg;
  437.     while (GetMessage(&msg, nullptr, 0, 0))
  438.     {
  439.         TranslateMessage(&msg);
  440.         DispatchMessage(&msg);
  441.     }
  442.  
  443.     return (int)msg.wParam;
  444. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement