Reputation: 3242
How would someone detect when a user enters tablet mode on a Windows 10 device with Delphi code?
Can someone show a code example for this?
I don't want to detect if the user has a tablet or not. I simply want to see whether they're in tablet mode or not. What would be the best way to do this?
Upvotes: 2
Views: 1213
Reputation: 41137
I deleted the previous variant (based on [SO]: How can I detect when Windows 10 enters tablet mode in a Windows Forms application? (@CheeseLover's answer) (pointed out by @Remko's comment)) because it's a totally different scenario (doesn't have anything to do with Win running on desktop).
I spent some time on [MS.DevBlogs]: Raymond - How can I detect whether my PC is in tablet mode? (pointed out in @RitaHan-MSFT's answer (+1)), and clearly, that's the way to go.
I don't know how to "translate" the code into Delphi, as many years passed since I wrote significant amounts of code in it (but I'm sure it's possible), so I did the next best thing: wrote a C++ .dll (containing a modified / improved version of Raymond's code) that is called from Delphi.
Note: VStudio is required to build the .dll, I used 2015 Community Edition, which is free and can be downloaded from [VStudio]: Visual Studio 2015 and other Products (you need an MS account though).
dll.cpp:
#include <wrl/client.h>
#include <windows.ui.viewmanagement.h>
#include <UIViewSettingsInterop.h>
#include <wrl/wrappers/corewrappers.h>
namespace WRL = Microsoft::WRL;
namespace VM = ABI::Windows::UI::ViewManagement;
class Backend {
public:
static Backend &instance()
{
static Backend m_instance;
return m_instance;
}
WRL::ComPtr<IUIViewSettingsInterop> interop() {return m_interop;}
private:
Backend()
{
HRESULT res = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
m_comInit = (res == S_OK) || (res == S_FALSE);
if (m_comInit || (res == RPC_E_CHANGED_MODE)) {
res = Windows::Foundation::GetActivationFactory(WRL::Wrappers::HStringReference(
RuntimeClass_Windows_UI_ViewManagement_UIViewSettings).Get(), &m_interop);
}
}
Backend(const Backend &other) = delete;
Backend &operator =(const Backend &other) = delete;
~Backend()
{
if (m_interop) {m_interop.Reset();}
if (m_comInit) {CoUninitialize();}
}
bool m_comInit = false;
WRL::ComPtr<IUIViewSettingsInterop> m_interop = nullptr;
};
/*!
Gets Tablet mode value.
\param hwnd Window handle to get the mode for
\returns:
1 - Tablet mode ON
0 - Tablet mode OFF
-X - Error
*/
extern "C" __declspec(dllexport) int GetTabletMode(HWND hwnd)
{
WRL::ComPtr<IUIViewSettingsInterop> interop = Backend::instance().interop();
if (!interop) {
return -3;
}
WRL::ComPtr<VM::IUIViewSettings> viewSettings;
HRESULT res = interop->GetForWindow(hwnd != NULL ? hwnd : GetConsoleWindow(), IID_PPV_ARGS(&viewSettings));
if (!viewSettings) {
return -2;
}
VM::UserInteractionMode currentMode;
res = viewSettings->get_UserInteractionMode(¤tMode);
int ret = -1;
switch (currentMode) {
case VM::UserInteractionMode_Mouse: ret = 0; break;
case VM::UserInteractionMode_Touch: ret = 1; break;
default: ret = -1;
}
viewSettings.Reset();
return ret;
}
Below is the Delphi relevant code (only the unit, as the rest can easily be manufactured, and there's no point placing it all here).
Unit0.pas:
unit Unit0;
interface
uses
Forms, Dialogs, Controls, StdCtrls, Classes;
type
TForm0 = class(TForm)
CheckButton: TButton;
procedure CheckButtonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form0: TForm0;
function GetTabletMode(hwnd: THandle): Integer cdecl; external 'TabletUtils.dll';
implementation
{$R *.dfm}
procedure TForm0.CheckButtonClick(Sender: TObject);
var
TabletModeStr: String;
begin
case GetTabletMode(Self.Handle) of
0 : TabletModeStr := 'OFF';
1 : TabletModeStr := 'ON';
else TabletModeStr := 'ERROR';
end;
MessageDlg('Tablet Mode: ' + TabletModeStr, mtInformation, [mbOK], 0);
end;
end.
Output:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q056321591]> sopr.bat ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [prompt]> "c:\Install\x86\Microsoft\Visual Studio Community\2015\vc\vcvarsall.bat" x86 [prompt]> dir /b App0.cfg App0.dof App0.dpr App0.exe App0.res dll.cpp other Unit0.dcu Unit0.ddp Unit0.dfm Unit0.pas [prompt]> cl /nologo /DDLL /DNDEBUG /DUSRDLL /D_WINDOWS /MT dll.cpp /link /NOLOGO /DLL /OUT:TabletUtils.dll ole32.lib runtimeobject.lib dll.cpp Creating library TabletUtils.lib and object TabletUtils.exp [prompt]> dir /b App0.cfg App0.dof App0.dpr App0.exe App0.res dll.cpp dll.obj other TabletUtils.dll TabletUtils.exp TabletUtils.lib Unit0.dcu Unit0.ddp Unit0.dfm Unit0.pas [prompt]> App0.exe [prompt]>
In the screenshot below, I ran the application:
On my laptop (Win 10) with Desktop mode (right side)
On a Win 10 VM with Tablet mode (left side). Note that I had to copy:
App0.exe
TabletUtils.dll
Upvotes: 1
Reputation: 9700
You can use UIViewSettings.UserInteractionMode API. Please refer to @Raymond blog: "How can I detect whether my PC is in tablet mode?", there are UWP and desktop ways in C++ you can refer to.
More detailed information you can check this thread.
But you need find out how to do in Delphi. There are some related issues hope they are helpful for you:
delphi - call external WinAPI function
Can we call Native Windows API from Delphi?
Upvotes: 1