Skip to content

Commit

Permalink
Taskbar10: Fixed a bug where SCOOBE would repeatedly crash Explorer w…
Browse files Browse the repository at this point in the history
…hen Language Switcher is set to anything other than Windows 11 (the default) ^2
  • Loading branch information
Amrsatrio committed Mar 12, 2024
1 parent fe7f800 commit 5c35f58
Show file tree
Hide file tree
Showing 4 changed files with 339 additions and 178 deletions.
5 changes: 5 additions & 0 deletions ExplorerPatcher/ExplorerPatcher.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="InputSwitch.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="Localization.cpp" />
<ClCompile Include="lvt.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
Expand Down Expand Up @@ -334,6 +338,7 @@
<ClInclude Include="hooking.h" />
<ClInclude Include="ep_private.h" />
<ClInclude Include="ImmersiveFlyouts.h" />
<ClInclude Include="InputSwitch.h" />
<ClInclude Include="Localization.h" />
<ClInclude Include="lvt.h" />
<ClInclude Include="osutility.h" />
Expand Down
198 changes: 198 additions & 0 deletions ExplorerPatcher/InputSwitch.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#include "InputSwitch.h"

#include <shellscalingapi.h>
#include <wrl/implements.h>
#include <wil/result_macros.h>

#define TB_POS_NOWHERE 0
#define TB_POS_BOTTOM 1
#define TB_POS_TOP 2
#define TB_POS_LEFT 3
#define TB_POS_RIGHT 4
extern "C" UINT GetTaskbarLocationAndSize(POINT ptCursor, RECT* rc);

extern "C" __MIDL___MIDL_itf_inputswitchserver_0000_0000_0001 dwIMEStyle;
extern "C" HRESULT CInputSwitchControl_ModifyAnchor(UINT dwNumberOfProfiles, RECT* lpRect);

HRESULT CInputSwitchControl_ModifyAnchor(UINT dwNumberOfProfiles, RECT* lpRect)
{
if (!dwIMEStyle) // impossible case (this is not called for the Windows 11 language switcher), but just in case
{
return S_FALSE;
}

HWND hWndTaskbar = FindWindowW(L"Shell_TrayWnd", NULL);

UINT dpiX = 96, dpiY = 96;
HRESULT hr = GetDpiForMonitor(
MonitorFromWindow(hWndTaskbar, MONITOR_DEFAULTTOPRIMARY),
MDT_DEFAULT,
&dpiX,
&dpiY
);
double dpix = dpiX / 96.0;
double dpiy = dpiY / 96.0;

//printf("RECT %d %d %d %d - %d %d\n", lpRect->left, lpRect->right, lpRect->top, lpRect->bottom, dwNumberOfProfiles, a3);

RECT rc;
GetWindowRect(hWndTaskbar, &rc);
POINT pt;
pt.x = rc.left;
pt.y = rc.top;
UINT tbPos = GetTaskbarLocationAndSize(pt, &rc);
if (tbPos == TB_POS_BOTTOM)
{
}
else if (tbPos == TB_POS_TOP)
{
if (dwIMEStyle == 1) // Windows 10 (with Language preferences link)
{
lpRect->top = rc.top + (rc.bottom - rc.top) + (UINT)(((double)dwNumberOfProfiles * (60.0 * dpiy)) + (5.0 * dpiy * 4.0) + (dpiy) + (48.0 * dpiy));
}
else if (dwIMEStyle == 2 || dwIMEStyle == 3 || dwIMEStyle == 4 || dwIMEStyle == 5) // LOGONUI, UAC, Windows 10, OOBE
{
lpRect->top = rc.top + (rc.bottom - rc.top) + (UINT)(((double)dwNumberOfProfiles * (60.0 * dpiy)) + (5.0 * dpiy * 2.0));
}
}
else if (tbPos == TB_POS_LEFT)
{
if (dwIMEStyle == 1 || dwIMEStyle == 2 || dwIMEStyle == 3 || dwIMEStyle == 4 || dwIMEStyle == 5)
{
lpRect->right = rc.left + (rc.right - rc.left) + (UINT)((double)(300.0 * dpix));
lpRect->top += (lpRect->bottom - lpRect->top);
}
}
if (tbPos == TB_POS_RIGHT)
{
if (dwIMEStyle == 1 || dwIMEStyle == 2 || dwIMEStyle == 3 || dwIMEStyle == 4 || dwIMEStyle == 5)
{
lpRect->right = lpRect->right - (rc.right - rc.left);
lpRect->top += (lpRect->bottom - lpRect->top);
}
}

if (dwIMEStyle == 4)
{
lpRect->right -= (UINT)((double)(300.0 * dpix)) - (lpRect->right - lpRect->left);
}

return S_OK;
}

class CInputSwitchControlProxy : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IInputSwitchControl>
{
public:
CInputSwitchControlProxy()
: m_type((__MIDL___MIDL_itf_inputswitchserver_0000_0000_0001)-1)
{
}

HRESULT RuntimeClassInitialize(IInputSwitchControl* original)
{
m_original = original;
return S_OK;
}

STDMETHODIMP Init(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0001 type) override
{
m_type = type;
return m_original->Init(type == ISCT_IDL_DESKTOP && dwIMEStyle != ISCT_IDL_DESKTOP ? dwIMEStyle : type);
}

STDMETHODIMP ShowInputSwitch(const RECT* rect) override
{
RECT myRect = *rect;
if (m_type == ISCT_IDL_DESKTOP)
{
UINT dwNumberOfProfiles = 0;
BOOL bImePresent = FALSE;
m_original->GetProfileCount(&dwNumberOfProfiles, &bImePresent);
CInputSwitchControl_ModifyAnchor(dwNumberOfProfiles, &myRect);
}
return m_original->ShowInputSwitch(&myRect);
}

STDMETHODIMP SetCallback(IInputSwitchCallback* callback) override { return m_original->SetCallback(callback); }
STDMETHODIMP GetProfileCount(UINT* count, BOOL* bOutImePresent) override { return m_original->GetProfileCount(count, bOutImePresent); }
STDMETHODIMP GetCurrentProfile(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0002* data) override { return m_original->GetCurrentProfile(data); }
STDMETHODIMP RegisterHotkeys() override { return m_original->RegisterHotkeys(); }
STDMETHODIMP ClickImeModeItem(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0004 type, POINT point, const RECT* rect) override { return m_original->ClickImeModeItem(type, point, rect); }
STDMETHODIMP ForceHide() override { return m_original->ForceHide(); }
STDMETHODIMP ShowTouchKeyboardInputSwitch(const RECT* rect, __MIDL___MIDL_itf_inputswitchserver_0000_0000_0006 align, int a3, DWORD a4, __MIDL___MIDL_itf_inputswitchserver_0000_0000_0005 a5) override { return m_original->ShowTouchKeyboardInputSwitch(rect, align, a3, a4, a5); }
STDMETHODIMP GetContextFlags(DWORD* flags) override { return m_original->GetContextFlags(flags); }
STDMETHODIMP SetContextOverrideMode(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0008 mode) override { return m_original->SetContextOverrideMode(mode); }
STDMETHODIMP GetCurrentImeModeItem(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0003* data) override { return m_original->GetCurrentImeModeItem(data); }
STDMETHODIMP ActivateInputProfile(const WCHAR* profile) override { return m_original->ActivateInputProfile(profile); }
STDMETHODIMP SetUserSid(const WCHAR* sid) override { return m_original->SetUserSid(sid); }

private:
Microsoft::WRL::ComPtr<IInputSwitchControl> m_original;
__MIDL___MIDL_itf_inputswitchserver_0000_0000_0001 m_type;
};

HRESULT CInputSwitchControlProxy_CreateInstance(IInputSwitchControl* original, REFIID riid, void** ppvObject)
{
Microsoft::WRL::ComPtr<CInputSwitchControlProxy> proxy;
RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize<CInputSwitchControlProxy>(&proxy, original));
RETURN_HR(proxy.CopyTo(riid, ppvObject));
}

class CInputSwitchControlProxySV2 : public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IInputSwitchControlSV2>
{
public:
CInputSwitchControlProxySV2()
: m_type((__MIDL___MIDL_itf_inputswitchserver_0000_0000_0001)-1)
{
}

HRESULT RuntimeClassInitialize(IInputSwitchControlSV2* original)
{
m_original = original;
return S_OK;
}

STDMETHODIMP Init(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0001 type) override
{
m_type = type;
return m_original->Init(type == ISCT_IDL_DESKTOP && dwIMEStyle != ISCT_IDL_DESKTOP ? dwIMEStyle : type);
}

STDMETHODIMP ShowInputSwitch(const RECT* rect) override
{
RECT myRect = *rect;
if (m_type == ISCT_IDL_DESKTOP)
{
UINT dwNumberOfProfiles = 0;
BOOL bImePresent = FALSE;
m_original->GetProfileCount(&dwNumberOfProfiles, &bImePresent);
CInputSwitchControl_ModifyAnchor(dwNumberOfProfiles, &myRect);
}
return m_original->ShowInputSwitch(&myRect);
}

STDMETHODIMP SetCallback(IInputSwitchCallback* callback) override { return m_original->SetCallback(callback); }
STDMETHODIMP GetProfileCount(UINT* count, BOOL* bOutImePresent) override { return m_original->GetProfileCount(count, bOutImePresent); }
STDMETHODIMP GetCurrentProfile(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0002* data) override { return m_original->GetCurrentProfile(data); }
STDMETHODIMP RegisterHotkeys() override { return m_original->RegisterHotkeys(); }
STDMETHODIMP ClickImeModeItem(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0004 type, POINT point, const RECT* rect) override { return m_original->ClickImeModeItem(type, point, rect); }
STDMETHODIMP ClickImeModeItemWithAnchor(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0004 type, IUnknown* anchor) override { return m_original->ClickImeModeItemWithAnchor(type, anchor); }
STDMETHODIMP ForceHide() override { return m_original->ForceHide(); }
STDMETHODIMP ShowTouchKeyboardInputSwitch(const RECT* rect, __MIDL___MIDL_itf_inputswitchserver_0000_0000_0006 align, int a3, DWORD a4, __MIDL___MIDL_itf_inputswitchserver_0000_0000_0005 a5) override { return m_original->ShowTouchKeyboardInputSwitch(rect, align, a3, a4, a5); }
STDMETHODIMP GetContextFlags(DWORD* flags) override { return m_original->GetContextFlags(flags); }
STDMETHODIMP SetContextOverrideMode(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0008 mode) override { return m_original->SetContextOverrideMode(mode); }
STDMETHODIMP GetCurrentImeModeItem(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0003* data) override { return m_original->GetCurrentImeModeItem(data); }
STDMETHODIMP ActivateInputProfile(const WCHAR* profile) override { return m_original->ActivateInputProfile(profile); }
STDMETHODIMP SetUserSid(const WCHAR* sid) override { return m_original->SetUserSid(sid); }

private:
Microsoft::WRL::ComPtr<IInputSwitchControlSV2> m_original;
__MIDL___MIDL_itf_inputswitchserver_0000_0000_0001 m_type;
};

HRESULT CInputSwitchControlProxySV2_CreateInstance(IInputSwitchControlSV2* original, REFIID riid, void** ppvObject)
{
Microsoft::WRL::ComPtr<CInputSwitchControlProxySV2> proxy;
RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize<CInputSwitchControlProxySV2>(&proxy, original));
RETURN_HR(proxy.CopyTo(riid, ppvObject));
}
122 changes: 122 additions & 0 deletions ExplorerPatcher/InputSwitch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#pragma once

#include <Windows.h>

DEFINE_GUID(CLSID_InputSwitchControl, 0xb9bc2a50, 0x43c3, 0x41aa, 0xa0, 0x86, 0x5d, 0xb1, 0x4e, 0x18, 0x4b, 0xae);
DEFINE_GUID(IID_IInputSwitchControl, 0xb9bc2a50, 0x43c3, 0x41aa, 0xa0, 0x82, 0x5d, 0xb1, 0x4e, 0x18, 0x4b, 0xae);

enum __MIDL___MIDL_itf_inputswitchserver_0000_0000_0001
{
ISCT_IDL_DESKTOP,
ISCT_IDL_TOUCHKEYBOARD,
ISCT_IDL_LOGONUI,
ISCT_IDL_UAC,
ISCT_IDL_SETTINGSPANE,
ISCT_IDL_OOBE,
ISCT_IDL_USEROOBE
};

struct __MIDL___MIDL_itf_inputswitchserver_0000_0000_0002
{
int dummy; // We don't need its contents
};

struct __MIDL___MIDL_itf_inputswitchserver_0000_0000_0003
{
WCHAR* pszTooltip;
HICON hIcon;
BOOL fDisabled;
BOOL fHidden;
WCHAR* pszIconGlyph;
};

enum __MIDL___MIDL_itf_inputswitchserver_0000_0000_0004
{
INPUT_SWITCH_IDL_IME_CLICK_TYPE_LEFT,
INPUT_SWITCH_IDL_IME_CLICK_TYPE_RIGHT,
INPUT_SWITCH_IDL_IME_CLICK_TYPE_LEFT_DISABLED
};

enum __MIDL___MIDL_itf_inputswitchserver_0000_0000_0005
{
INPUT_SWITCH_IDL_MODALITY_STANDARDKEYBOARD = 0x1,
INPUT_SWITCH_IDL_MODALITY_SPLITKEYBOARD = 0x2,
INPUT_SWITCH_IDL_MODALITY_CLASSICKEYBOARD = 0x4,
INPUT_SWITCH_IDL_MODALITY_HANDWRITING = 0x8,
INPUT_SWITCH_IDL_MODALITY_HIDE = 0x10,
INPUT_SWITCH_IDL_MODALITY_ONEHANDEDKEYBOARD = 0x20,
};

enum __MIDL___MIDL_itf_inputswitchserver_0000_0000_0006
{
INPUT_SWITCH_IDL_ALIGN_DEFAULT,
INPUT_SWITCH_IDL_ALIGN_RIGHT_EDGE,
INPUT_SWITCH_IDL_ALIGN_LEFT_EDGE,
};

enum __MIDL___MIDL_itf_inputswitchserver_0000_0000_0008
{
INPUT_SWITCH_IDL_CFOM_NO_OVERRIDE,
INPUT_SWITCH_IDL_CFOM_DESKTOP,
INPUT_SWITCH_IDL_CFOM_IMMERSIVE,
};

interface IInputSwitchCallback;

#ifdef __cplusplus
MIDL_INTERFACE("b9bc2a50-43c3-41aa-a082-5db14e184bae")
IInputSwitchControl : IUnknown
{
virtual HRESULT STDMETHODCALLTYPE Init(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0001) = 0;
virtual HRESULT STDMETHODCALLTYPE SetCallback(IInputSwitchCallback*) = 0;
virtual HRESULT STDMETHODCALLTYPE ShowInputSwitch(const RECT*) = 0;
virtual HRESULT STDMETHODCALLTYPE GetProfileCount(UINT*, BOOL*) = 0;
virtual HRESULT STDMETHODCALLTYPE GetCurrentProfile(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0002*) = 0;
virtual HRESULT STDMETHODCALLTYPE RegisterHotkeys() = 0;
virtual HRESULT STDMETHODCALLTYPE ClickImeModeItem(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0004, POINT, const RECT*) = 0;
virtual HRESULT STDMETHODCALLTYPE ForceHide() = 0;
virtual HRESULT STDMETHODCALLTYPE ShowTouchKeyboardInputSwitch(const RECT*, __MIDL___MIDL_itf_inputswitchserver_0000_0000_0006, int, DWORD, __MIDL___MIDL_itf_inputswitchserver_0000_0000_0005) = 0;
virtual HRESULT STDMETHODCALLTYPE GetContextFlags(DWORD*) = 0;
virtual HRESULT STDMETHODCALLTYPE SetContextOverrideMode(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0008) = 0;
virtual HRESULT STDMETHODCALLTYPE GetCurrentImeModeItem(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0003*) = 0;
virtual HRESULT STDMETHODCALLTYPE ActivateInputProfile(const WCHAR*) = 0;
virtual HRESULT STDMETHODCALLTYPE SetUserSid(const WCHAR*) = 0;
};
#else
typedef interface IInputSwitchControl IInputSwitchControl;
#endif

#ifdef __cplusplus
MIDL_INTERFACE("b9bc2a50-43c3-41aa-a082-5db14e184bae")
IInputSwitchControlSV2 : IUnknown
{
virtual HRESULT STDMETHODCALLTYPE Init(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0001) = 0;
virtual HRESULT STDMETHODCALLTYPE SetCallback(IInputSwitchCallback*) = 0;
virtual HRESULT STDMETHODCALLTYPE ShowInputSwitch(const RECT*) = 0;
virtual HRESULT STDMETHODCALLTYPE GetProfileCount(UINT*, BOOL*) = 0;
virtual HRESULT STDMETHODCALLTYPE GetCurrentProfile(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0002*) = 0;
virtual HRESULT STDMETHODCALLTYPE RegisterHotkeys() = 0;
virtual HRESULT STDMETHODCALLTYPE ClickImeModeItem(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0004, POINT, const RECT*) = 0;
virtual HRESULT STDMETHODCALLTYPE ClickImeModeItemWithAnchor(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0004, IUnknown*) = 0;
virtual HRESULT STDMETHODCALLTYPE ForceHide() = 0;
virtual HRESULT STDMETHODCALLTYPE ShowTouchKeyboardInputSwitch(const RECT*, __MIDL___MIDL_itf_inputswitchserver_0000_0000_0006, int, DWORD, __MIDL___MIDL_itf_inputswitchserver_0000_0000_0005) = 0;
virtual HRESULT STDMETHODCALLTYPE GetContextFlags(DWORD*) = 0;
virtual HRESULT STDMETHODCALLTYPE SetContextOverrideMode(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0008) = 0;
virtual HRESULT STDMETHODCALLTYPE GetCurrentImeModeItem(__MIDL___MIDL_itf_inputswitchserver_0000_0000_0003*) = 0;
virtual HRESULT STDMETHODCALLTYPE ActivateInputProfile(const WCHAR*) = 0;
virtual HRESULT STDMETHODCALLTYPE SetUserSid(const WCHAR*) = 0;
};
#else
typedef interface IInputSwitchControlSV2 IInputSwitchControlSV2;
#endif

#ifdef __cplusplus
extern "C" {
#endif

HRESULT CInputSwitchControlProxy_CreateInstance(IInputSwitchControl* original, REFIID riid, void** ppvObject);
HRESULT CInputSwitchControlProxySV2_CreateInstance(IInputSwitchControlSV2* original, REFIID riid, void** ppvObject);

#ifdef __cplusplus
}
#endif
Loading

0 comments on commit 5c35f58

Please sign in to comment.