How to programmatically start an application on a specific monitor on Windows 10? - windows

I want to write a program that needs sometimes to start processes of another applications (mainly Sumatra PDF) on Windows 10, version 1803 (April 2018 Update).
These applications should be started on a specific monitor. I also want to be able to close the processes when needed.
The preferred languages are C# and Java, but any help is appreciated.
EDIT
I've tried to use the ShellExecuteExW function suggested by IInspectable in C++ code directly, but it doesn't work, as applications appear on the main monitor. I must have certainly made a mistake as I am absolutely new to WinAPI and know very little C++.
#include <Windows.h>
HMONITOR monitors[2]; // As it's only a test and I have currently only two monitors.
int monitorsCount = 0;
BOOL CALLBACK Monitorenumproc(HMONITOR hMonitor, HDC hdc, LPRECT lprect, LPARAM lparam)
{
monitors[monitorsCount] = hMonitor;
monitorsCount++;
return TRUE;
}
int main()
{
EnumDisplayMonitors(NULL, NULL, Monitorenumproc, 0);
_SHELLEXECUTEINFOW info;
ZeroMemory(&info, sizeof(info));
info.cbSize = sizeof(info);
info.fMask = SEE_MASK_HMONITOR;
//info.lpVerb = L"open";
info.lpFile = L"C:\\Windows\\System32\\cmd.exe";
info.nShow = SW_SHOW;
info.hMonitor = monitors[1]; // Trying to start on the second monitor.
ShellExecuteExW(&info);
return 0;
}

As suggested by others, this is intended behavior of Windows and for the good reasons.
Also, you can not rely on default window placement atleast for SumatraPDF, since it surely does not use CW_USEDEFAULT, and instead stores these values in :
%USERPROFILE%\AppData\Roaming\SumatraPDF\SumatraPDF-settings.txt
There are multiple options though:
Use third party tools that monitor top level windows and based on pre-configured rules moves them to specified display. E.g. DisplayFusion, etc.
Use ligher weight solutions like AutoHotkey/AutoIt.
Try and do this in code itself. Following is a working solution. I smoke tested on my box.
Disclaimer: I have not written the entire code, for saving time I pulled it up from couple of sources, tweaked it, glued it together, and tested with SumantraPDF. Do also note, that this code is not of highest standards, but solves your problem, and will act as a can-be-done example.
C++ code: (scroll down for C# code)
#include <Windows.h>
#include <vector>
// 0 based index for preferred monitor
static const int PREFERRED_MONITOR = 1;
struct ProcessWindowsInfo
{
DWORD ProcessID;
std::vector<HWND> Windows;
ProcessWindowsInfo(DWORD const AProcessID)
: ProcessID(AProcessID)
{
}
};
struct MonitorInfo
{
HMONITOR hMonitor;
RECT rect;
};
BOOL WINAPI EnumProcessWindowsProc(HWND hwnd, LPARAM lParam)
{
ProcessWindowsInfo *info = reinterpret_cast<ProcessWindowsInfo*>(lParam);
DWORD WindowProcessID;
GetWindowThreadProcessId(hwnd, &WindowProcessID);
if (WindowProcessID == info->ProcessID)
{
if (GetWindow(hwnd, GW_OWNER) == (HWND)0 && IsWindowVisible(hwnd))
{
info->Windows.push_back(hwnd);
}
}
return true;
}
BOOL CALLBACK Monitorenumproc(HMONITOR hMonitor, HDC hdc, LPRECT lprect, LPARAM lParam)
{
std::vector<MonitorInfo> *info = reinterpret_cast<std::vector<MonitorInfo>*>(lParam);
MonitorInfo monitorInfo = { 0 };
monitorInfo.hMonitor = hMonitor;
monitorInfo.rect = *lprect;
info->push_back(monitorInfo);
return TRUE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
// NOTE: for now this code works only when the window is not already visible
// could be easily modified to terminate existing process as required
SHELLEXECUTEINFO info = { 0 };
info.cbSize = sizeof(info);
info.fMask = SEE_MASK_NOCLOSEPROCESS;
info.lpFile = L"C:\\Program Files\\SumatraPDF\\SumatraPDF.exe";
info.nShow = SW_SHOW;
std::vector<MonitorInfo> connectedMonitors;
// Get all available displays
EnumDisplayMonitors(NULL, NULL, Monitorenumproc, reinterpret_cast<LPARAM>(&connectedMonitors));
if (ShellExecuteEx(&info))
{
WaitForInputIdle(info.hProcess, INFINITE);
ProcessWindowsInfo Info(GetProcessId(info.hProcess));
// Go though all windows from that process
EnumWindows((WNDENUMPROC)EnumProcessWindowsProc, reinterpret_cast<LPARAM>(&Info.ProcessID));
if (Info.Windows.size() == 1)
{
// only if we got at most 1 window
// NOTE: applications can have more than 1 top level window. But at least for SumtraPDF this works!
if (connectedMonitors.size() >= PREFERRED_MONITOR)
{
// only move the window if we were able to successfully detect available monitors
SetWindowPos(Info.Windows.at(0), 0, connectedMonitors.at(PREFERRED_MONITOR).rect.left, connectedMonitors.at(PREFERRED_MONITOR).rect.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
}
CloseHandle(info.hProcess);
}
return 0;
}
To emphasize one of my comments in code. This code will only work if the process in question is not already running. You can tweak the code as per your requirements otherwise.
Update: Added C# code below, as I realized OP prefers C#. This code also has the termination logic cooked in.
C# code:
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
private const int SWP_NOSIZE = 0x0001;
private const int SWP_NOZORDER = 0x0004;
private const int PREFERRED_MONITOR = 1;
static void Main(string[] args)
{
// NOTE: you will have to reference System.Windows.Forms and System.Drawing (or
// equivalent WPF assemblies) for Screen and Rectangle
// Terminate existing SumatraPDF process, else we will not get the MainWindowHandle by following method.
List<Process> existingProcesses = Process.GetProcessesByName("SumatraPDF").ToList();
foreach (var existingProcess in existingProcesses)
{
// Ouch! Ruthlessly kill the existing SumatraPDF instances
existingProcess.Kill();
}
// Start the new instance of SumantraPDF
Process process = Process.Start(#"C:\Program Files\SumatraPDF\SumatraPDF.exe");
// wait max 5 seconds for process to be active
process.WaitForInputIdle(5000);
if (Screen.AllScreens.Length >= PREFERRED_MONITOR)
{
SetWindowPos(process.MainWindowHandle,
IntPtr.Zero,
Screen.AllScreens[PREFERRED_MONITOR].WorkingArea.Left,
Screen.AllScreens[PREFERRED_MONITOR].WorkingArea.Top,
0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
}

SEE_MASK_HMONITOR is only a request. Applications can choose their own window placement. SEE_MASK_HMONITOR only works when the executed application relies on default window placement, i.e. it creates its first top-level window with CW_USEΒ­DEFAULT
So what you want is not generally possible. Your code is as good as you can get, if you don't control the launched application.

Related

What's so special about "ride.exe" anyway?

I just bumped into an obscure issue, while implementing a prototype for a native Win32 UI using Direct2D/DirectWrite for high performance text rendering. Things went fine and looked promising: Resizing the application window was devoid of any lag, and the entire text rendering was as solid as one could expect.
That is, until I decided to rename the binary to "ride.exe". At that point, the OS opted to force my application into "glitch mode". Resizing suddenly introduced a very noticeable lag, combined with gaps when sizing up, that would only eventually fill again. Plus, text output got all jumpy, with the perceived font size varying. Which is odd considering that I only ever create the font resource at application startup.
All those artifacts instantly go away, if I rename the binary to anything else, and instantly return, when I choose to call it "ride.exe" again. No recompilation or linking involved, it's literally just a rename operation.
I'm lost at this point, though it looks a lot like the OS is using some heuristic that involves the image name to decide, how to execute the code. So, what's going on here?
This is a complete repro to illustrate the issue:
#include <Windows.h>
#include <d2d1.h>
#include <d2d1_1helper.h>
#include <dwrite.h>
#pragma comment(lib, "D2d1.lib")
#pragma comment(lib, "Dwrite.lib")
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void create_graphics_resources(HWND const hwnd) noexcept;
void discard_graphics_resources() noexcept;
void resize_render_target(HWND const hwnd) noexcept;
void create_dw_resources() noexcept;
void discard_dw_resources() noexcept;
ID2D1Factory1* pd2d_factory { nullptr };
ID2D1HwndRenderTarget* prender_target { nullptr };
ID2D1SolidColorBrush* ptext_color_brush { nullptr };
IDWriteFactory* pdw_factory { nullptr };
IDWriteTextFormat* pdw_text_format { nullptr };
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int nCmdShow)
{
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
WNDCLASSEXW wcex {};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszClassName = L"d2d_mcve";
RegisterClassExW(&wcex);
auto const hWnd { CreateWindowExW(0, L"d2d_mcve", L"D2D MCVE", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT,
0, nullptr, nullptr, hInstance, nullptr) };
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg {};
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
CoUninitialize();
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE: {
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, D2D1_FACTORY_OPTIONS {}, &pd2d_factory);
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(pdw_factory),
reinterpret_cast<IUnknown**>(&pdw_factory));
}
break;
case WM_DESTROY:
discard_dw_resources();
discard_graphics_resources();
pdw_factory->Release();
pd2d_factory->Release();
PostQuitMessage(0);
break;
case WM_SIZE:
resize_render_target(hWnd);
InvalidateRect(hWnd, nullptr, FALSE);
break;
case WM_PAINT: {
PAINTSTRUCT ps {};
BeginPaint(hWnd, &ps);
create_graphics_resources(hWnd);
prender_target->BeginDraw();
prender_target->Clear(D2D1::ColorF(D2D1::ColorF::Black));
create_dw_resources();
auto const target_size { prender_target->GetSize() };
prender_target->SetTransform(D2D1::Matrix3x2F::Identity());
auto const& placeholder_text { L"Lorem ipsum dolor sit amet, consectetur adipiscing elit." };
prender_target->DrawTextW(placeholder_text, ARRAYSIZE(placeholder_text) - 1, pdw_text_format,
D2D1::RectF(0.0f, 0.0f, target_size.width, target_size.height),
ptext_color_brush);
auto const hr { prender_target->EndDraw() };
if (hr == D2DERR_RECREATE_TARGET)
{
discard_graphics_resources();
InvalidateRect(hWnd, nullptr, FALSE);
}
EndPaint(hWnd, &ps);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
void create_graphics_resources(HWND const hwnd) noexcept
{
if (prender_target == nullptr)
{
RECT rc {};
GetClientRect(hwnd, &rc);
auto const size { D2D1::SizeU(static_cast<UINT32>(rc.right), static_cast<UINT32>(rc.bottom)) };
pd2d_factory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(hwnd, size), &prender_target);
prender_target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White, 1.0f), &ptext_color_brush);
}
}
void discard_graphics_resources() noexcept
{
if (ptext_color_brush != nullptr)
{
ptext_color_brush->Release();
ptext_color_brush = nullptr;
}
if (prender_target != nullptr)
{
prender_target->Release();
prender_target = nullptr;
}
}
void resize_render_target(HWND const hwnd) noexcept
{
if (prender_target != nullptr)
{
RECT rc {};
GetClientRect(hwnd, &rc);
auto const size { D2D1::SizeU(static_cast<UINT32>(rc.right), static_cast<UINT32>(rc.bottom)) };
prender_target->Resize(size);
}
}
void create_dw_resources() noexcept
{
if (pdw_text_format == nullptr)
{
pdw_factory->CreateTextFormat(L"Courier New", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL, 48.0f, L"en-US", &pdw_text_format);
}
}
void discard_dw_resources() noexcept
{
if (pdw_text_format != nullptr)
{
pdw_text_format->Release();
pdw_text_format = nullptr;
}
}
Save the source code as "main.cpp".
Compile the code using the following command line:
cl.exe /O2 /EHsc /D "NDEBUG" /D "UNICODE" /MD main.cpp user32.lib ole32.lib
Create a directory named "choosing_a_trident_for_a_buttplug_is_not_prudent" anywhere in the filesystem and copy "main.exe" into that directory.
Launch "main.exe" and observe the behavior of the application when resizing the main window.
Rename the copy of "main.exe" under "choosing_a_trident_for_a_buttplug_is_not_prudent" to "ride.exe", launch "ride.exe" and observe the behavior when resizing the main window.
The last step exhibits all sorts of visual glitches on my system (Windows 10 19041.330). The application instantly returns to normal operation when either renaming the executable image to anything but "ride.exe", or renaming every parent directory to no longer contain the phrase "ride". Casing doesn't make a difference.
I haven't done a thorough analysis on what exactly is happening, yet it appears that the system is using a - dare I say - somewhat (?) crude strategy to fix broken applications. As usual, the Hall of Shame is to be found in the registry:
On my system there's a key named 8e89aac5-6105-47bd-bfb3-6ed01bda07b0 under HKU\<sid>\System\GameConfigStore\Children. Leaving the obvious WTF aside for a minute, that GameConfigStore is seemingly a System-vital key for my user account, that key has values named ExeParentDirectory and Flags, with associated data RIDE (REG_SZ) and 0x11 (REG_DWORD), respectively.
The way I read this is: If a user identified by <sid> attempts to launch a binary called ride.exe (hardcoded, somewhere), that resides under a directory whose name contains RIDE, first apply the flags 0x11 to something, for sports, just to get their attention, before allowing the primary thread to execute. This is to ensure that broken app X continues to appear not broken, when <user> decides to change the environment that would otherwise exhibit the brokenness.
Even if this were to break other, properly written applications.
Now this may sound bad, but you have to give Microsoft credit where credit is due: Out of all possible combinations of characters that form a valid registry key name to communicate with us, devs, they have opted to keep this down to earth, matter of fact, professional, and use GUIDs.
I mean...
They could have just as well chosen to following:
Quality doesn't matter, ROI does
What do you mean, you did RTFM?!
Dude, what contractual guarantees? LOL, n00b
This is just a friendly FU. But no, we don't care about devs that actually TRY not to suck
But they didn't! So... thanks? I guess.

GDI - Can I use the new Windows 10 Segoe UI Emoji colored font with DrawText?

I'm creating a c++ project using Embarcadero RAD Studio (10.2 Tokyo starter) and the Windows GDI to draw text, via the DrawText() function.
I recently saw that Windows 10 provides a new "Segoe UI Emoji" font, that potentially allows text functions to draw colored emojis. I found several examples using Direct2D, but none with pure GDI functions.
I also tried a simple code, like this:
HDC hDC = ::GetDC(Handle);
std::auto_ptr<TCanvas> pCanvas(new TCanvas());
pCanvas->Handle = hDC;
pCanvas->Brush->Color = clWhite;
pCanvas->Brush->Style = bsSolid;
pCanvas->FillRect(TRect(0, 0, ClientWidth, ClientHeight));
const std::wstring text = L"Test πŸ˜€ 😬 😁 πŸ˜‚ πŸ˜ƒ πŸ˜„ πŸ˜… πŸ˜†";
TRect textRect(10, 10, ClientWidth - 10, ClientHeight - 10);
hFont = ::CreateFont(-40,
0,
0,
0,
FW_DONTCARE,
FALSE,
FALSE,
FALSE,
DEFAULT_CHARSET,
OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY,
VARIABLE_PITCH,
L"Segoe UI Emoji");
::SelectObject(hDC, hFont);
::DrawTextW(hDC,
text.c_str(),
text.length(),
&textRect,
DT_LEFT | DT_TOP | DT_SINGLELINE);
::DeleteObject(hFont);
The output result sounds good in terms of symbols, but they are drawn in black&white, without colors, as you can see on the screenshot below:
I could not find any additional options that may allow the text to be drawn using colored symbols instead of black&white. Is there a way to activate the support of the color in GDI DrawText() function, and if yes, how to do that? Or only Direct2D may draw colored emojis?
EDITED on 30.10.2017
As the GDI cannot do the job (unfortunately, and as I thought) I publish here the Direct2D version of the above code, that worked for me.
const std::wstring text = L"Test πŸ˜€ 😬 😁 πŸ˜‚ πŸ˜ƒ πŸ˜„ πŸ˜… πŸ˜†";
HDC hDC = ::GetDC(Handle);
std::auto_ptr<TCanvas> pGDICanvas(new TCanvas());
pGDICanvas->Handle = hDC;
pGDICanvas->Brush->Color = clWhite;
pGDICanvas->Brush->Style = bsSolid;
pGDICanvas->FillRect(TRect(0, 0, ClientWidth, ClientHeight));
::D2D1_RECT_F textRect;
textRect.left = 10;
textRect.top = 10;
textRect.right = ClientWidth - 10;
textRect.bottom = ClientHeight - 10;
std::auto_ptr<TDirect2DCanvas> pCanvas(new TDirect2DCanvas(hDC, TRect(0, 0, ClientWidth, ClientHeight)));
// configure Direct2D font
pCanvas->Font->Size = 40;
pCanvas->Font->Name = L"Segoe UI Emoji";
pCanvas->Font->Orientation = 0;
pCanvas->Font->Pitch = System::Uitypes::TFontPitch::fpVariable;
pCanvas->Font->Style = TFontStyles();
// get DirectWrite text format object
_di_IDWriteTextFormat pFormat = pCanvas->Font->Handle;
if (!pFormat)
return;
pCanvas->RenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
::D2D1_COLOR_F color;
color.r = 0.0f;
color.g = 0.0f;
color.b = 0.0f;
color.a = 1.0f;
::ID2D1SolidColorBrush* pBrush = NULL;
// create solid color brush, use pen color if rect is completely filled with outline
pCanvas->RenderTarget->CreateSolidColorBrush(color, &pBrush);
if (!pBrush)
return;
// set horiz alignment
pFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
// set vert alignment
pFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
// set reading direction
pFormat->SetReadingDirection(DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
// set word wrapping mode
pFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
IDWriteInlineObject* pInlineObject = NULL;
::DWRITE_TRIMMING trimming;
trimming.delimiter = 0;
trimming.delimiterCount = 0;
trimming.granularity = DWRITE_TRIMMING_GRANULARITY_NONE;
// set text trimming
pFormat->SetTrimming(&trimming, pInlineObject);
pCanvas->BeginDraw();
pCanvas->RenderTarget->DrawText(text.c_str(), text.length(), pFormat, textRect, pBrush,
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
pCanvas->EndDraw();
Of course this code will draw colored emojis only on the currently most recent versions of Windows 10, and above. On previous versions the text will be drawn as above (and the code may not compile).
Bonus Reading
MSDN: Color Fonts with DirectWrite, Direct2D, and Win2D
GDI does not support color fonts (even if you go the full Uniscribe route), you have to use Direct2D if you want color font support. It makes sense that the simpler GDI APIs don't support color fonts as color fonts require using OpenType tags and none of DrawText/TextOut provide that level of control, Uniscribe allows for such tags but has simply not been extended to support color fonts.
You can use DirectWrite to draw colored emojis onto a bitmap in memory DC, then BitBlt() to your destination DC.
Basically, you need to implement a custom IDWriteTextRenderer class and call IDWriteTextLayout::Draw() with your renderer, then copy the result.
In your class, you retrieve IDWriteGdiInterop from IDWriteFactory and call IDWriteGdiInterop::CreateBitmapRenderTarget() to get the bitmap render target; call IDWriteFactory::CreateMonitorRenderingParams() to get the rendering parameters, and call IDWriteFactory::CreateTextFormat() to set up your text format.
The only significant method is DrawGlyphRun(), where you get IDWriteColorGlyphRunEnumerator with IDWriteFactory2::TranslateColorGlyphRun() and with each color run, call IDWriteBitmapRenderTarget::DrawGlyphRun() to do the work for you.
Just remember to update the render target/parameters when the window size/position changes.
You may reference this MSDN documentation:
Render to a GDI Surface
https://msdn.microsoft.com/en-us/library/windows/desktop/ff485856(v=vs.85).aspx
As mentioned by #SoronelHaetir's answer above, the win32 graphics device interface (GDI) that is used when working with static window components (via WC_STATIC) doesn't support colorized fonts. In order to display colored emojis and/or "fancier" text (i.e. colored text, etc.), you'll need to use the Direct2D API.
The example above provided by original poster (OP) #Jean-Milost Reymond isn't something that is immediately able to be compiled and tried out by the reader. Also, it uses the TCanvas class, which isn't strictly needed when working directly with the Win32 API.
For people looking for a complete example that works on the bare-metal Win32 API and can be immediately copied and pasted and compiled, then here is the code that will compile in Visual Studio:
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <wchar.h>
#include <math.h>
#include <d2d1.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <wincodec.h>
#include <string>
#include <cassert>
#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "Dwrite.lib")
HWND WindowHandle = nullptr;
IDWriteFactory * DWriteFactory = nullptr;
ID2D1Factory * Direct2dFactory = nullptr;
ID2D1HwndRenderTarget * RenderTarget = nullptr;
ID2D1SolidColorBrush * TextBlackBrush = nullptr;
const std::wstring DISPLAY_TEXT = L"Test πŸ˜€ 😬 😁 πŸ˜‚ πŸ˜ƒ πŸ˜„ πŸ˜… πŸ˜†";
template<class Interface>
inline void SafeRelease (Interface ** ppInterfaceToRelease);
LRESULT CALLBACK WndProc (HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
HRESULT CreateDeviceIndependentResources ();
HRESULT InitInstance (HINSTANCE hInstance, int nCmdShow);
void DiscardDeviceResources ();
HRESULT OnRender ();
HRESULT CreateDeviceResources ();
template<class Interface>
inline void SafeRelease (Interface ** ppInterfaceToRelease)
{
if (*ppInterfaceToRelease != NULL)
{
(*ppInterfaceToRelease)->Release ();
(*ppInterfaceToRelease) = NULL;
}
}
HRESULT OnRender ()
{
HRESULT Result = S_OK;
D2D1_SIZE_F RenderCanvasArea = { 0 };
IDWriteTextFormat * TextFormat = nullptr;
D2D1_RECT_F TextCanvasArea = { 0 };
Result = CreateDeviceResources ();
if (SUCCEEDED (Result))
{
RenderTarget->BeginDraw ();
RenderCanvasArea = RenderTarget->GetSize ();
RenderTarget->Clear (D2D1::ColorF (D2D1::ColorF::White));
if (SUCCEEDED (Result))
{
Result = DWriteFactory->CreateTextFormat (L"Segoe UI",
nullptr,
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
25.0f,
L"en-us",
&TextFormat);
TextFormat->SetTextAlignment (DWRITE_TEXT_ALIGNMENT_LEADING);
TextFormat->SetParagraphAlignment (DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
TextFormat->SetReadingDirection (DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
TextFormat->SetWordWrapping (DWRITE_WORD_WRAPPING_WRAP);
if (SUCCEEDED (Result) &&
TextFormat != nullptr)
{
TextCanvasArea = D2D1::RectF (0,
0,
RenderCanvasArea.width,
RenderCanvasArea.height);
RenderTarget->DrawTextW (DISPLAY_TEXT.c_str (),
static_cast <UINT32> (DISPLAY_TEXT.size ()),
TextFormat,
TextCanvasArea,
TextBlackBrush,
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
}
}
Result = RenderTarget->EndDraw ();
}
if (Result == D2DERR_RECREATE_TARGET)
{
DiscardDeviceResources ();
Result = S_OK;
}
return Result;
}
HRESULT CreateDeviceResources ()
{
HRESULT Result = S_OK;
RECT rc = { 0 };
if (!RenderTarget)
{
GetClientRect (WindowHandle,
&rc);
D2D1_SIZE_U size = D2D1::SizeU (rc.right - rc.left,
rc.bottom - rc.top);
// Create a Direct2D render target.
Result = Direct2dFactory->CreateHwndRenderTarget (D2D1::RenderTargetProperties (),
D2D1::HwndRenderTargetProperties (WindowHandle, size),
&RenderTarget);
if (SUCCEEDED (Result))
{
// Create a blue brush.
Result = RenderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::Black),
&TextBlackBrush);
}
}
return Result;
}
void DiscardDeviceResources ()
{
SafeRelease (&RenderTarget);
SafeRelease (&TextBlackBrush);
}
HRESULT InitInstance (HINSTANCE hInstance,
int nCmdShow)
{
HRESULT Result = S_OK;
// Create the window.
WindowHandle = CreateWindow (L"D2DTextDemo",
L"Direct2D Text Demo Application",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
600,
200,
nullptr,
nullptr,
hInstance,
nullptr);
if (WindowHandle == nullptr)
{
Result = E_POINTER;
}
else
{
ShowWindow (WindowHandle,
nCmdShow);
UpdateWindow (WindowHandle);
}
return Result;
}
HRESULT CreateDeviceIndependentResources ()
{
HRESULT Result = S_OK;
Result = D2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED,
&Direct2dFactory);
if (SUCCEEDED (Result))
{
Result = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED,
__uuidof (IDWriteFactory),
reinterpret_cast <IUnknown **> (&DWriteFactory));
}
return Result;
}
LRESULT CALLBACK WndProc (HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
LRESULT Result = 0;
switch (message)
{
case WM_SIZE:
{
UINT width = LOWORD (lParam);
UINT height = HIWORD (lParam);
if (RenderTarget != nullptr)
{
// Note: This method can fail, but it's okay to ignore the
// error here, because the error will be returned again
// the next time EndDraw is called.
RenderTarget->Resize (D2D1::SizeU (width,
height));
}
}
break;
case WM_DISPLAYCHANGE:
{
InvalidateRect (hwnd, nullptr, FALSE);
}
break;
case WM_PAINT:
{
OnRender ();
ValidateRect (hwnd,
nullptr);
}
break;
case WM_DESTROY:
{
PostQuitMessage (0);
Result = 1;
}
break;
default:
{
Result = DefWindowProc (hwnd,
message,
wParam,
lParam);
}
break;
}
return Result;
}
int APIENTRY wWinMain (_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER (hInstance);
UNREFERENCED_PARAMETER (hPrevInstance);
UNREFERENCED_PARAMETER (lpCmdLine);
UNREFERENCED_PARAMETER (nCmdShow);
HRESULT ExitCode = S_OK;
MSG NextMessage = { 0 };
WNDCLASSEX wcex = { 0 };
ATOM WindowClassId = 0;
wcex.cbSize = sizeof (WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof (LONG_PTR);
wcex.hInstance = hInstance;
wcex.hbrBackground = nullptr;
wcex.lpszMenuName = nullptr;
wcex.hCursor = LoadCursor (nullptr, IDI_APPLICATION);
wcex.lpszClassName = L"D2DTextDemo";
if (SUCCEEDED (CoInitialize (nullptr)))
{
WindowClassId = RegisterClassEx (&wcex);
if (WindowClassId == 0)
{
ExitCode = HRESULT_FROM_WIN32 (GetLastError ());
}
if (SUCCEEDED (ExitCode))
{
ExitCode = CreateDeviceIndependentResources ();
}
if (SUCCEEDED (ExitCode))
{
ExitCode = InitInstance (hInstance,
nCmdShow);
}
if (SUCCEEDED (ExitCode))
{
while (GetMessage (&NextMessage,
nullptr,
0,
0))
{
TranslateMessage (&NextMessage);
DispatchMessage (&NextMessage);
}
}
CoUninitialize ();
SafeRelease (&Direct2dFactory);
SafeRelease (&DWriteFactory);
SafeRelease (&RenderTarget);
}
return ExitCode;
}
(The above example doesn't have perfect error handling, so it's important to audit this example code if the following is used in any project the reader is working on.)
Before attempting to compile this in Visual Studio, make sure your project has the "SubSystem" linker option set to Windows /SUBSYSTEM:WINDOWS.
Once compiled successfully, the following application window will appear:
I tested this coded example in Visual Studio 2022 Community Edition on Windows 11 with success.
Reference(s):
Creating a Simple Direct2D Application
Tutorial: Getting Started with DirectWrite

Show touch keyboard (TabTip.exe) in Windows 10 Anniversary edition

In Windows 8 and Windows 10 before Anniversary update it was possible to show touch keyboard by starting
C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe
It no longer works in Windows 10 Anniversary update; the TabTip.exe process is running, but the keyboard is not shown.
Is there a way to show it programmatically?
UPDATE
I found a workaround - fake mouse click on touch keyboard icon in system tray. Here is code in Delphi
// Find tray icon window
function FindTrayButtonWindow: THandle;
var
ShellTrayWnd: THandle;
TrayNotifyWnd: THandle;
begin
Result := 0;
ShellTrayWnd := FindWindow('Shell_TrayWnd', nil);
if ShellTrayWnd > 0 then
begin
TrayNotifyWnd := FindWindowEx(ShellTrayWnd, 0, 'TrayNotifyWnd', nil);
if TrayNotifyWnd > 0 then
begin
Result := FindWindowEx(TrayNotifyWnd, 0, 'TIPBand', nil);
end;
end;
end;
// Post mouse click messages to it
TrayButtonWindow := FindTrayButtonWindow;
if TrayButtonWindow > 0 then
begin
PostMessage(TrayButtonWindow, WM_LBUTTONDOWN, MK_LBUTTON, $00010001);
PostMessage(TrayButtonWindow, WM_LBUTTONUP, 0, $00010001);
end;
UPDATE 2
Another thing I found is that setting this registry key restores old functionality when starting TabTip.exe shows touch keyboard
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\TabletTip\1.7\EnableDesktopModeAutoInvoke=1
OK, I reverse engineered what explorer does when the user presses that button in the system tray.
Basically it creates an instance of an undocumented interface ITipInvocation and calls its Toggle(HWND) method, passing desktop window as an argument. As the name suggests, the method either shows or hides the keyboard depending on its current state.
Please note that explorer creates an instance of ITipInvocation on every button click. So I believe the instance should not be cached. I also noticed that explorer never calls Release() on the obtained instance. I'm not too familiar with COM, but this looks like a bug.
I tested this in Windows 8.1, Windows 10 & Windows 10 Anniversary Edition and it works perfectly. Here's a minimal example in C that obviously lacks some error checks.
#include <initguid.h>
#include <Objbase.h>
#pragma hdrstop
// 4ce576fa-83dc-4F88-951c-9d0782b4e376
DEFINE_GUID(CLSID_UIHostNoLaunch,
0x4CE576FA, 0x83DC, 0x4f88, 0x95, 0x1C, 0x9D, 0x07, 0x82, 0xB4, 0xE3, 0x76);
// 37c994e7_432b_4834_a2f7_dce1f13b834b
DEFINE_GUID(IID_ITipInvocation,
0x37c994e7, 0x432b, 0x4834, 0xa2, 0xf7, 0xdc, 0xe1, 0xf1, 0x3b, 0x83, 0x4b);
struct ITipInvocation : IUnknown
{
virtual HRESULT STDMETHODCALLTYPE Toggle(HWND wnd) = 0;
};
int WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HRESULT hr;
hr = CoInitialize(0);
ITipInvocation* tip;
hr = CoCreateInstance(CLSID_UIHostNoLaunch, 0, CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER, IID_ITipInvocation, (void**)&tip);
tip->Toggle(GetDesktopWindow());
tip->Release();
return 0;
}
Here's the C# version as well:
class Program
{
static void Main(string[] args)
{
var uiHostNoLaunch = new UIHostNoLaunch();
var tipInvocation = (ITipInvocation)uiHostNoLaunch;
tipInvocation.Toggle(GetDesktopWindow());
Marshal.ReleaseComObject(uiHostNoLaunch);
}
[ComImport, Guid("4ce576fa-83dc-4F88-951c-9d0782b4e376")]
class UIHostNoLaunch
{
}
[ComImport, Guid("37c994e7-432b-4834-a2f7-dce1f13b834b")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface ITipInvocation
{
void Toggle(IntPtr hwnd);
}
[DllImport("user32.dll", SetLastError = false)]
static extern IntPtr GetDesktopWindow();
}
Update: per #EugeneK comments, I believe that tabtip.exe is the COM server for the COM component in question, so if your code gets REGDB_E_CLASSNOTREG, it should probably run tabtip.exe and try again.
I had the same problem too. It took me much time and headache, but Thanks to Alexei and Torvin I finally got it working on Win 10 1709. Visibility check was the difficulty. Maybe The OSKlib Nuget could be updated. Let me sum up the complete sulotion (For sure my code has some unnecessary lines now):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
using Osklib.Interop;
using System.Runtime.InteropServices;
using System.Threading;
namespace OSK
{
public static class OnScreenKeyboard
{
static OnScreenKeyboard()
{
var version = Environment.OSVersion.Version;
switch (version.Major)
{
case 6:
switch (version.Minor)
{
case 2:
// Windows 10 (ok)
break;
}
break;
default:
break;
}
}
private static void StartTabTip()
{
var p = Process.Start(#"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe");
int handle = 0;
while ((handle = NativeMethods.FindWindow("IPTIP_Main_Window", "")) <= 0)
{
Thread.Sleep(100);
}
}
public static void ToggleVisibility()
{
var type = Type.GetTypeFromCLSID(Guid.Parse("4ce576fa-83dc-4F88-951c-9d0782b4e376"));
var instance = (ITipInvocation)Activator.CreateInstance(type);
instance.Toggle(NativeMethods.GetDesktopWindow());
Marshal.ReleaseComObject(instance);
}
public static void Show()
{
int handle = NativeMethods.FindWindow("IPTIP_Main_Window", "");
if (handle <= 0) // nothing found
{
StartTabTip();
Thread.Sleep(100);
}
// on some devices starting TabTip don't show keyboard, on some does Β―\_(ツ)_/Β―
if (!IsOpen())
{
ToggleVisibility();
}
}
public static void Hide()
{
if (IsOpen())
{
ToggleVisibility();
}
}
public static bool Close()
{
// find it
int handle = NativeMethods.FindWindow("IPTIP_Main_Window", "");
bool active = handle > 0;
if (active)
{
// don't check style - just close
NativeMethods.SendMessage(handle, NativeMethods.WM_SYSCOMMAND, NativeMethods.SC_CLOSE, 0);
}
return active;
}
public static bool IsOpen()
{
return GetIsOpen1709() ?? GetIsOpenLegacy();
}
[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr after, string className, string title = null);
[DllImport("user32.dll", SetLastError = false)]
private static extern uint GetWindowLong(IntPtr wnd, int index);
private static bool? GetIsOpen1709()
{
// if there is a top-level window - the keyboard is closed
var wnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, WindowClass1709, WindowCaption1709);
if (wnd != IntPtr.Zero)
return false;
var parent = IntPtr.Zero;
for (;;)
{
parent = FindWindowEx(IntPtr.Zero, parent, WindowParentClass1709);
if (parent == IntPtr.Zero)
return null; // no more windows, keyboard state is unknown
// if it's a child of a WindowParentClass1709 window - the keyboard is open
wnd = FindWindowEx(parent, IntPtr.Zero, WindowClass1709, WindowCaption1709);
if (wnd != IntPtr.Zero)
return true;
}
}
private static bool GetIsOpenLegacy()
{
var wnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, WindowClass);
if (wnd == IntPtr.Zero)
return false;
var style = GetWindowStyle(wnd);
return style.HasFlag(WindowStyle.Visible)
&& !style.HasFlag(WindowStyle.Disabled);
}
private const string WindowClass = "IPTip_Main_Window";
private const string WindowParentClass1709 = "ApplicationFrameWindow";
private const string WindowClass1709 = "Windows.UI.Core.CoreWindow";
private const string WindowCaption1709 = "Microsoft Text Input Application";
private enum WindowStyle : uint
{
Disabled = 0x08000000,
Visible = 0x10000000,
}
private static WindowStyle GetWindowStyle(IntPtr wnd)
{
return (WindowStyle)GetWindowLong(wnd, -16);
}
}
[ComImport]
[Guid("37c994e7-432b-4834-a2f7-dce1f13b834b")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface ITipInvocation
{
void Toggle(IntPtr hwnd);
}
internal static class NativeMethods
{
[DllImport("user32.dll", EntryPoint = "FindWindow")]
internal static extern int FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "SendMessage")]
internal static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow", SetLastError = false)]
internal static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
internal static extern int GetWindowLong(int hWnd, int nIndex);
internal const int GWL_STYLE = -16;
internal const int GWL_EXSTYLE = -20;
internal const int WM_SYSCOMMAND = 0x0112;
internal const int SC_CLOSE = 0xF060;
internal const int WS_DISABLED = 0x08000000;
internal const int WS_VISIBLE = 0x10000000;
}
}
The only solution I've found to work is by sending PostMessage as you've mentioned in answer 1. Here's the C# version of it in case someone needs it.
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr FindWindow(string sClassName, string sAppName);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
var trayWnd = FindWindow("Shell_TrayWnd", null);
var nullIntPtr = new IntPtr(0);
if (trayWnd != nullIntPtr)
{
var trayNotifyWnd = FindWindowEx(trayWnd, nullIntPtr, "TrayNotifyWnd", null);
if (trayNotifyWnd != nullIntPtr)
{
var tIPBandWnd = FindWindowEx(trayNotifyWnd, nullIntPtr, "TIPBand", null);
if (tIPBandWnd != nullIntPtr)
{
PostMessage(tIPBandWnd, (UInt32)WMessages.WM_LBUTTONDOWN, 1, 65537);
PostMessage(tIPBandWnd, (UInt32)WMessages.WM_LBUTTONUP, 1, 65537);
}
}
}
public enum WMessages : int
{
WM_LBUTTONDOWN = 0x201,
WM_LBUTTONUP = 0x202,
WM_KEYDOWN = 0x100,
WM_KEYUP = 0x101,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14,
}
I detect 4 situations when trying to open Touch Keyboard on Windows 10 Anniversary Update
Keyboard is Visible - when "IPTIP_Main_Window" is present, NOT disabled and IS visible
Keyboard is not visible - when "IPTIP_Main_Window" is present but disabled
Keyboard is not visible - when "IPTIP_Main_Window" is present but NOT disabled and NOT visible
Keyboard is not visible - when "IPTIP_Main_Window" is NOT present
1 - nothing to do
2+3 - activating via COM
4 - most interesting scenario. In some devices starting TabTip process opens touch keyboard, on some - not. So we must start TabTip process, wait for appearing window "IPTIP_Main_Window", check it for visibility and activate it via COM if nessesary.
I make small library for my project, you can use it - osklib
The following code will always work since it uses latest MS Api
I put it into a dll (Needed for a Delphi project) but it is a plain C
Also useful for obtaining the keyboard size and adjusting application layout
//*******************************************************************
//
// RETURNS KEYBOARD RECTANGLE OR EMPTY ONE IF KEYBOARD IS NOT VISIBLE
//
//*******************************************************************
RECT __stdcall GetKeyboardRect()
{
IFrameworkInputPane *inputPane = NULL;
RECT prcInputPaneScreenLocation = { 0,0,0,0 };
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_FrameworkInputPane, NULL, CLSCTX_INPROC_SERVER, IID_IFrameworkInputPane, (LPVOID*)&inputPane);
if (SUCCEEDED(hr))
{
hr=inputPane->Location(&prcInputPaneScreenLocation);
if (!SUCCEEDED(hr))
{
}
inputPane->Release();
}
}
CoUninitialize();
return prcInputPaneScreenLocation;
}
There is still some mystery about how the touch keyboard is set visible by Windows 10 Anniversary Update. I'm actually having the exact same issue and here are the lastest infos i've found :
Windows 10 1607 works in two modes : Desktop and Tablet. While in Desktop mode, TabTip.exe can be called but won't show. While in Tablet mode, everything works fine : TabTip.exe shows itself when called. So a 100% working workaround is to set your computer in Tablet Mode but who wants his desktop/laptop to work in tablet mode ? Not me anyway !
You can use the "EnableDesktopModeAutoInvoke" Key (HKCU, DWORD set to 1) and on some computers running 1607 it worked great while in Desktop Mode. But for some unknown reasons, it is not working on my HP touchpad.
Please note that this registry value is the "Show touch keyboard on desktop mode if there is no attached keyboard" option in Windows parameters > touch
You can use Torvin's code to show TabTip.exe (as mentioned TabTip.exe should be running when you do the COM stuff), it is working fine on some computers running 1607 (including my HP touchpad ! yay !) But it will do nothing on some others comps with the same windows Build.
So far tested on 4 different computers and i'm unable to get something working fine on all...
The problem seems to be with setting of Windows OS. I have faced same issue with the app I was developing. With Windows 8 and 10 (before update) code that called keyboard worked fine, but after update failed to work. After reading this article, I did following:
Pressed Win+I to open the Settings app
Clicked on Devices > Typing
Turned "Automatically show the touch keyboard in windowed apps when there’s no keyboard attached to your device" ON.
Right after that keyboard starting showing up in Windows 10 also.
Implementing the IValueProvider/ITextProvider in your control is a correct way to achieve this, as described here: https://stackoverflow.com/a/43886052/1184950
I tried multiple things that did not work. But I discovered that I can use the Shortcut Keys Windows/Ctrl/O to open the On Screen Key Board.
Also there is a Nuget package: Input Simulator by Michael Noonan.
If you install the InputSimulator NuGet package in your Winforms project - then add code like this to an event, like a button:
private void button1_Click(object sender, EventArgs e)
{
var simu = new InputSimulator();
simu.Keyboard.ModifiedKeyStroke(new[] { VirtualKeyCode.LWIN, VirtualKeyCode.CONTROL }, VirtualKeyCode.VK_O);
}
You will also need to add these using statements:
using WindowsInput;
using WindowsInput.Native;
Run your app and the button will display the keyboard and also hit it again and it will remove it.
I am on Windows 10 and vs 2019.
Set oShell = CreateObject("WScript.Shell")
oShell.AppActivate "Program Manager"
oShell.Run "tabtip.exe", 0, true
oShell.SendKeys "%{TAB}"
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\TabletTip\1.7]
"EnableDesktopModeAutoInvoke"=dword:00000001
Following code works well on 21h1 version tablet and notebook,thanks for #torvin method
#include <dwmapi.h>
#include <windows.h>
#pragma comment(lib,"dwmapi.lib")
void toggleKeyboardShowState()
{
if(FindWindowEx(nullptr,nullptr,L"IPTip_Main_Window",nullptr)!=nullptr)
{
ITipInvocation *tip_invocation_;
if(CoCreateInstance(CLSID_UIHostNoLaunch,nullptr,CLSCTX_INPROC_HANDLER|CLSCTX_LOCAL_SERVER,IID_ITipInvocation,(void **)&tip_invocation_)==S_OK)
tip_invocation_->Toggle(GetDesktopWindow());
return;
}
PVOID OldValue=nullptr;
BOOL bRet=Wow64DisableWow64FsRedirection(&OldValue);
ShellExecute(nullptr,L"open",L"C:\\Program Files\\Common Files\\microsoft shared\\ink\\TabTip.exe",nullptr,nullptr,SW_SHOWNORMAL);
if(bRet)
Wow64RevertWow64FsRedirection(OldValue);
}
bool keyboardIsShow()
{
int cloaked=0;
return DwmGetWindowAttribute(FindWindowExW(FindWindowExW(nullptr,nullptr,L"ApplicationFrameWindow",nullptr),nullptr,L"Windows.UI.Core.CoreWindow",L"Microsoft Text Input Application"),DWMWA_CLOAKED,&cloaked,DWM_CLOAKED_INHERITED)==S_OK?0==cloaked:false;
}
It will start and show taptip and show it default if there it isn't in background when call toggleKeyboardShowState(),otherwise change taptip show state.
start tabtip process or hide->show
if(!keyboardIsShow())
toggleKeyboardShowState();
show->hide
if(keyboardIsShow())
toggleKeyboardShowState();
The keyboard show state can be switched even without if judgment.
Use this method:
Create osk.bat file and save it under your program folder ie. C:\My Software\osk.bat
Type in this osk.bat the following cmd:
"C:\Program Files\Common Files\Microsoft Shared\Ink\Tabtip.exe"
Use Windows Script to run this bat file
oWSH = CREATEOBJECT("wscript.shell")
oWSH.Run("osk.bat", 0, .T.)
In Win10 Ver 1803, DesktopMode, there is no reliable way to
toggle the "Touch Keyboard" on|off [ ITipInvocation.Toggle() ];
nor can you reliably discover if it's "up" ( on screen )
[ IFrameworkInputPane.Location() ]; both routines fail randomly.
Instead, ensure that "TabTIP.EXE" and "....InputApp.EXE"
only run when the keyboard is "up" ( on screen ).
To toggle the keyboard on and off ( from X.CPP in Jeff-Relf.Me/X.ZIP ):
if ( WM == WM_HOTKEY && C == 'K' ) {
// A mouse button takes me here. Jeff-Relf.Me/g600.PNG
if ( KillProc = 1, Running( L"TabTIP.EXE" ), KillProc = 1, Running(
L"WindowsInternal.ComposableShell.Experiences.TextInput.InputApp.EXE"
) )
// The keyboard was _On_ ( i.e. its processes were running ),
// so it was "turned _Off_" (killed); and we're done.
goto Done ;
// The keyboard was _Off_ ( i.e. no running processes ).
// Turn it _On_:
Launch( L"%CommonProgramFiles%/microsoft shared/ink/TabTIP.EXE" );
Sleep(99);
static const GUID CLSID_UIHostNoLaunch = { 0x4CE576FA, 0x83DC,
0x4f88, 0x95, 0x1C, 0x9D, 0x07, 0x82, 0xB4, 0xE3, 0x76 };
static const GUID IID_ITipInvocation = { 0x37c994e7, 0x432b,
0x4834, 0xa2, 0xf7, 0xdc, 0xe1, 0xf1, 0x3b, 0x83, 0x4b };
static struct ITipInvocation : IUnknown { virtual HRESULT
STDMETHODCALLTYPE Toggle( HWND wnd ) = 0 ; } * Tog ;
Tog = 0, CoCreateInstance( CLSID_UIHostNoLaunch, 0, CLSCTX_INPROC_HANDLER
| CLSCTX_LOCAL_SERVER, IID_ITipInvocation, (void**) & Tog );
// Firefox and Chrome need this:
Tog ? Tog->Toggle( GetDesktopWindow() ), Tog->Release() : 0 ; }
- - - - - - - - - - - - -
// To get the process list, and kill stuff:
#include <tlhelp32.H>
int KillProc ;
int Running( wchar * EXE ) { int Found ; HANDLE PIDs, aProc ;
PROCESSENTRY32 aPID = { sizeof aPID };
PIDs = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
Process32First( PIDs, &aPID );
while ( Found = !strCmpI( aPID.szExeFile, EXE ),
KillProc && Found && (
aProc = OpenProcess( PROCESS_TERMINATE, 0, aPID.th32ProcessID ),
aProc ? TerminateProcess( aProc, 9 ), CloseHandle( aProc ) : 0 ),
!Found && Process32Next( PIDs, &aPID ) );
KillProc = 0, CloseHandle( PIDs ); return Found ; }
Launch( wchar * Cmd ) { wchar _Cmd[333]; static PROCESS_INFORMATION Stat ;
static STARTUPINFO SU = { sizeof SU };
SetEnvironmentVariable( L"__compat_layer", L"RunAsInvoker" );
ExpandEnvironmentStrings( Cmd, _Cmd, 333 ), Cmd = _Cmd ;
if ( CreateProcess( 0, Cmd, 0,0,1,0,0,0, &SU , &Stat ) )
CloseHandle( Stat.hProcess ), CloseHandle( Stat.hThread ); }
// CoInitialize(0);

Garbage Collector Crashing when using WinAPI Functions

In D, my garbage collector is crashing every time I launch my application.
Windows Module:
pragma(lib, "user32.lib");
import std.string;
extern(Windows) {
void* CreateWindowExW(uint extendedStyle ,
const char* classname,
const char* title,
uint style,
int x, int y,
int width, int height,
void* parentHandle,
void* menuHandle,
void* moduleInstance,
void* lParam);
}
class Window {
private void* handle;
private string title;
this(string title, const int x, const int y, const int width, const int height) {
this.title = title;
handle = CreateWindowExW(0, null, toStringz(this.title), 0, x, y, width, height, null, null, null, null);
if(handle == null)
throw new Exception("Error while creating Window (WinAPI)");
}
}
Main Module:
import std.stdio;
version(Windows) {
import windows;
extern (Windows) {
int WinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int nCmdShow) {
import core.runtime;
Runtime.initialize();
scope(exit) Runtime.terminate();
auto window = new Window("Hello", 0, 0, 0, 0);
writeln("test");
return 0;
}
}
}
This gives me an Access Violation in location 0. When I view the dissassembly, it's crashing in
0040986F mov ecx,dword ptr [eax]
This assembly is located inside _gc_malloc.
EDIT: Here is the new code:
Windows Module:
pragma(lib, "user32.lib");
import std.utf;
extern(Windows) {
void* CreateWindowExW(uint extendedStyle ,
const wchar* classname,
const wchar* title,
uint style,
int x, int y,
int width, int height,
void* parentHandle,
void* menuHandle,
void* moduleInstance,
void* lParam);
}
class Window {
private void* handle;
private wstring title;
this(wstring title, const int x, const int y, const int width, const int height) {
this.title = title;
handle = CreateWindowExW(0, null, toUTFz!(wchar*)(this.title), 0, x, y, width, height, null, null, null, null);
if(handle == null)
throw new Exception("Error while creating Window (WinAPI)");
}
}
WinMain:
int WinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int nCmdShow) {
import core.runtime;
try {
Runtime.initialize();
scope(exit) Runtime.terminate();
auto window = new Window("Hello", 0, 0, 0, 0);
writeln("test");
} catch(Exception ex) {
writeln(ex.toString);
}
return 0;
}
When I run this second code, I also get an Access Violation, on a random (to me) address.
Dissasembly (inside __d_createTrace):
0040C665 cmp dword ptr [ecx+1Ch],0
David Heffernan's post has good information, but won't fix the main problem here, which is that the D runtime is not initialized. You code should throw an exception, your arguments to create window are wrong, but it should not be an access violation.
The easiest way to solve this is to define a regular main function instead of WinMain. WinMains are valid in D, but if you define your own, it skips the druntime initialization functions. (The runtime, src/druntime/src/rt/main2.d if you're interested) define a C main function which does setup tasks, then calls your D main function. The C main btw is, in turn, called from WinMain by the C runtime.
If you need the arguments for the instance or command line, you can get with with the Windows API function GetModuleHandle(null) and GetCommandLineW().
Alternatively, you can initialize the runtime yourself in your WinMain function:
extern (Windows) {
int WinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int nCmdShow) {
import core.runtime; // the Runtime functions are in here
Runtime.initialize(); // initialize it!
auto window = new Window("Hello", 0, 0, 0, 0);
return 0;
}
}
The other thing you should do is terminate it and catch exceptions. An uncaught exception on Windows by default will trigger the system debugger, so it isn't all bad, but typically in D programs, you expect a nicer message. So do something like this:
int WinMain(void* hInstance, void* hPrevInstance, char* lpCmdLine, int nCmdShow) {
import core.runtime;
Runtime.initialize();
scope(exit) Runtime.terminate();
try
auto window = new Window("Hello", 0, 0, 100, 100);
catch(Throwable t)
MessageBoxW(null, toUTFz!(wchar*)(t.toString()), null, 0);
return 0;
}
So I initialized it there, terminated when the function returned, and also caught the exception and put it in a message box for easier reading. I put a prototype to MessageBoxW in myself, just like you did for CreateWindow. You can also fetch more complete win32 bindings here http://www.dsource.org/projects/bindings/browser/trunk/win32 (I think that's the up to date link anyway.)
Again though, you can also just use a D main function which does this kind of thing for you. WinMain gives more control but isn't required by D.
CreateWindowExW takes 16-bit strings rather than 8 bit strings. I'm not sure how to achieve that in D. I assume that char is 8 bit as it is in C++ on Windows? You could use CreateWindowExA I suppose, but it would be better to pass 16-bit UTF-16 text.
It must be an error to pass null for the lpClassName parameter. A window does need a window class.

Not able to get the coordinates of a particular window (I've tried the examples...still ) in V C++

I have been trying various code snippets here and there, but still not successful.I am simply trying to find one of the open windows (it is the Browser window) using FindWindow(NULL,WINDOWTITLE), with a specific Title string. Once I get the handle of the window, I need the coordinates using GetWindowRect.
This code gets me the coordinates, but seems like it's in an infinite loop, there are about a 100 lines of output with the coordindates, should be just 1. I don't see any while construct (originally a Java programmer)... wonder why it's repeating...
struct WindowInfo
{
HWND m_hWnd;
string m_title;
WindowInfo(HWND hwnd, string title) : m_hWnd(hwnd), m_title(title) {}
};
BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)
{
vector<WindowInfo*> & windows = *(vector<WindowInfo*>*)lParam;
char title[256];
HANDLE wndHandle;
LPCWSTR WINDOWTITLE = L"eBargain 2 Share - Windpos Internet Explorer";
RECT rRect;
LPRECT lpRect;
RECT rc;
hwnd = FindWindow(NULL,WINDOWTITLE);
GetWindowRect(hwnd,&rc);
printf("Position: %d x %d\tSize: %d x %d\n",rc.left,rc.top,rc.right- rc.left,rc.bottom-rc.top);
/* Enumerating through all the windows tells me that I am on the right track... (Should I just try to find the TITLE STRING by comparing every title from the following enumeration ?
*/
GetWindowTextA(hwnd, title, 256);
windows.push_back(new WindowInfo(hwnd,title));
// printf("%s\n", title);
return TRUE;
}
int main()
{
vector<WindowInfo*> windows;
BOOL ret = EnumWindows(EnumWindowsProc, (LPARAM) &windows);
if ( ret )
{
//windows have windowinfo of all enumerated windows
}
}
Your EnumWindowsProc seems to be a bit confused - are you enumerating or using FindWindow?
If you enumerate, simply get the window title and compare to the string you search for:
BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)
{
char title[256];
if (GetWindowTextA(hwnd, title, 256)) {
if (strcmp(title, "eBargain 2 Share - Windpos Internet Explorer") == 0) {
/* GetWindowRect ... */
}
}
return TRUE;
}
Or, if you're using FindWindow, no need to enumerate:
int main() {
HWND hwnd = FindWindowA(0, "eBargain 2 Share - Windpos Internet Explorer");
if (hwnd) {
/* GetWindowRect ... */
}
}

Resources