2018-08-15 07:42
WPF framework handles touch devices and events mostly using its own code and COM components instead of using the windows message loop. Unfortunately, there may be some bugs in the WPF touch handling codes. So we sometimes suffer from the WPF touch failures. This changes after Microsoft introducing .NET Framework 4.7, but the developers have to switch on the Pointer
message manually with some compliant issues.
In this article, I’ll post some codes of WPF to present its potential bugs of touch failure.
This post is written in multiple languages. Please select yours:
中文 English
Even if you write a very simple WPF application which contains only a button, you’ll be suffering from the touch failure issue.
What you need is:
The actions above helps reproduce touch failure with a small probability. But if you want a larger probability, you should:
Probably there may be other conditions such as the .NET Framework version and the Windows version but I’m not sure.
When you put them together, you’ll get a full touch failure issue description.
WPF use two different threads to collect touch information from tablet devices and convert them to the Stylus
and Mouse
event that most of us are familiar to.
The WPF stylus code uses the windows message loop to handle these messages:
The Stylus Input thread is created by the PenThreadWorker
class. The PenThreadWorker
call GetPenEvent
and GetPenEventMultiple
in the thread loop to fetch the whole touch events of tablet devices and then it will pass the raw touch data to other touch modules to translate them into regular Stylus/Touch/Mouse events. One of the touch modules is the WorkerOperationGetTabletsInfo
class which contains an OnDoWork
method to fetch tablet device count through COM components.
The touch failure comes from the code of the Stylus Input thread.
_handles
array is passed into the GetPenEventMultiple
method of PenThreadWorker
and this action may cause infinite waiting.COMException
or ArgumentException
may happen when the OnDoWork
of WorkerOperationGetTabletsInfo
is running. This method will catch the exceptions and returns an empty array which will cause the WPF application get empty tablet devices by mistake even if there are tablet devices in actual.I’ve simplified the core .NET Framework code of the stylus handling above. You may understand what I mean more clearly by reading these codes:
// PenThreadWorker.ThreadProc
while(There are two loops in real)
{
// The `break` below only exit one loop, not two.
if (this._handles.Length == 1)
{
if (!GetPenEvent(this._handles[0], otherArgs))
{
break;
}
}
else if (!GetPenEventMultiple(this._handles, otherArgs))
{
break;
}
// Other logics.
}
// WorkerOperationGetTabletsInfo.OnDoWork
try
{
_tabletDeviceInfo = PenThreadWorker.GetTabletInfoHelper(pimcTablet);
}
catch(COMException)
{
_tabletDevicesInfo = new TabletDeviceInfo[0];
}
catch(ArgumentException)
{
_tabletDevicesInfo = new TabletDeviceInfo[0];
}
// Other exception handling.
I can definitely sure that the ArgumentException
is the result of the thread-safety issue but I’m not sure whether COMException
is the same. The handles
argument is the handles of ResetEvent
s which are used for the thread syncing between managed code and unmanaged code. So the infinite waiting of GetPenEventMultiple
is actually a deadlock which is also a thread-safety issue.
Remember that we can make a high CPU usage to increase the probability of reproduction, we can infer that the touch failure issue is caused by the thread-safety issue of WPF stylus handling.
After inferring the preliminary reason, there are only two fundamental solutions left for us:
A more thorough solution is that both of them need to be fixed, but both can only be done by Microsoft.
So what can we non-Microsoft developers do?
But what can I do if I’m only a normal user?
The above conclusions come from the reading and debugging of the .NET Framework source code.
Since WPF touch details involve more types and source code which requires a lot of descriptions, so it is not explained in this article. Read the following article to get a deeper understanding of the touch failure (all of the links are under translating):
All of the .NET Framework source code in this article is decompiled by dnSpy, and the analysis process is basically based on the dnSpy’s no-PDB debugging feature.
本文会经常更新,请阅读原文: https://walterlv.com/post/wpf-touch-fails-when-tablet-device-changed-en.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com) 。