1

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?

Shaun Roselt
  • 1,233
  • 4
  • 14
  • 31
  • 6
    possible duplicate (not Delphi but same question): https://stackoverflow.com/questions/31153664/how-can-i-detect-when-windows-10-enters-tablet-mode-in-a-windows-forms-applicati#32417768 – Remko May 27 '19 at 07:33
  • @Remko I've seen that question, but mine is specific to Delphi. – Shaun Roselt May 27 '19 at 07:34
  • 5
    The answer applies to Delphi, sometimes you have to go native. – Anders May 27 '19 at 09:49

2 Answers2

1

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(&currentMode);

    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

Img0

CristiFati
  • 28,721
  • 9
  • 41
  • 63
1

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?

Rita Han
  • 8,827
  • 1
  • 7
  • 20