日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

dotnet 读 WPF 源代码笔记 插入触摸设备的初始化获取设备信息

發布時間:2023/12/31 asp.net 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 dotnet 读 WPF 源代码笔记 插入触摸设备的初始化获取设备信息 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在 WPF 觸摸應用中,插入觸摸設備,即可在應用里面使用上插入的觸摸設備。在 WPF 使用觸摸設備的觸摸時,需要獲取到觸摸設備的信息,才能實現觸摸

獲取觸摸設備插入

在 WPF 中,通過 Windows 消息獲取觸摸設備插入事件,在 src\Microsoft.DotNet.Wpf\src\PresentationCore\System\Windows\Input\Stylus\Wisp\WispLogic.cs 的 HandleMessage 將獲取 Windows 消息,代碼如下

internal override void HandleMessage(WindowMessage msg, IntPtr wParam, IntPtr lParam){switch (msg){// 忽略代碼case WindowMessage.WM_TABLET_ADDED:OnTabletAdded((uint)NativeMethods.IntPtrToInt32(wParam));break;case WindowMessage.WM_TABLET_DELETED:OnTabletRemovedImpl((uint)NativeMethods.IntPtrToInt32(wParam), isInternalCall: true);break;}}

在 WPF 框架,使用 WM_TABLET_ADDED 和 WM_TABLET_DELETED 消息獲取設備的插入和刪除事件

如上面代碼,在設備插入時,將會調用 OnTabletAdded 方法。如 WM_TABLET_ADDED 官方文檔描述,以上代碼獲取的參數是 Wisptis 的 Index 序號。這是因為用戶可以插入多個觸摸設備,通過傳入序號可以拿到插入的設備

在 WPF 中,每次插入觸摸設備,都會重新更新所有的觸摸設備的信息,而不是只更新插入的設備。在 OnTabletAdded 方法里面,將會調用 GetDeviceCount 方法,在 GetDeviceCount 方法里面將通過 PenThread 的 WorkerGetTabletsInfo 更新所有觸摸設備的信息,代碼如下

private void OnTabletAdded(uint wisptisIndex){lock (__penContextsLock){WispTabletDeviceCollection tabletDeviceCollection = WispTabletDevices;// 忽略代碼// Update the last known device count._lastKnownDeviceCount = GetDeviceCount();uint tabletIndex = UInt32.MaxValue;// HandleTabletAdded returns true if we need to update contexts due to a change in tablet devices.if (tabletDeviceCollection.HandleTabletAdded(wisptisIndex, ref tabletIndex)){// Update all contexts with this new tablet device.foreach (PenContexts contexts in __penContextsMap.Values){contexts.AddContext(tabletIndex);}}}}private int GetDeviceCount(){PenThread penThread = null;// Get a PenThread by mimicking a subset of the code in TabletDeviceCollection.UpdateTablets().TabletDeviceCollection tabletDeviceCollection = TabletDevices;if (tabletDeviceCollection != null && tabletDeviceCollection.Count > 0){penThread = tabletDeviceCollection[0].As<WispTabletDevice>().PenThread;}if (penThread != null){// Use the PenThread to get the full, unfiltered tablets info to see how many there are.TabletDeviceInfo[] tabletdevices = penThread.WorkerGetTabletsInfo();return tabletdevices.Length;}else{// if there's no PenThread yet, return "unknown"return -1;} } // WPF 代碼格式化就是這樣

以上代碼調用 WorkerGetTabletsInfo 方法實際的獲取觸摸信息邏輯是放在觸摸線程,上面代碼需要先獲取觸摸線程 PenThread 然后調用觸摸線程類的 WorkerGetTabletsInfo 方法,在這個方法里面執行邏輯

觸摸線程

在 WPF 觸摸到事件 博客里面告訴大家,在 WPF 框架,為了讓觸摸的性能足夠強,將觸摸的獲取放在獨立的進程里面

在獲取觸摸信息時,也需要調度到觸摸線程執行。在 WPF 中,通過 PenThread 類的相關方法可以調度到觸摸線程

在調用 WorkerGetTabletsInfo 方法時,進入 WorkerGetTabletsInfo 方法依然是主線程,里面代碼如下

internal TabletDeviceInfo[] WorkerGetTabletsInfo(){// Set data up for this callWorkerOperationGetTabletsInfo getTablets = new WorkerOperationGetTabletsInfo();lock(_workerOperationLock){_workerOperation.Add(getTablets);}// Kick thread to do this work.MS.Win32.Penimc.UnsafeNativeMethods.RaiseResetEvent(_pimcResetHandle.Value);// Wait for this work to be completed.getTablets.DoneEvent.WaitOne();getTablets.DoneEvent.Close();return getTablets.TabletDevicesInfo;}

實際上以上代碼是放在 PenThreadWorker.cs 文件中,在 WPF 的觸摸線程設計上,觸摸線程是一個循環,將會等待 PenImc 層發送觸摸消息,或者等待 _pimcResetHandle 鎖被釋放。如上面代碼,先插入 WorkerOperationGetTabletsInfo 到 _workerOperation 列表中,然后調用 RaiseResetEvent 方法釋放 _pimcResetHandle 對象。觸摸線程將會因為 _pimcResetHandle 被釋放而跳出循環,然后獲取 _workerOperation 列表里面的項,進行執行邏輯

主線程將會在 getTablets.DoneEvent.WaitOne 方法里面進入鎖,等待觸摸線程執行 WorkerOperationGetTabletsInfo 完成之后釋放這個鎖,才能讓主線程繼續執行

觸摸線程的循環邏輯代碼大概如下

internal void ThreadProc(){Thread.CurrentThread.Name = "Stylus Input";while (!__disposed){// 忽略代碼WorkerOperation[] workerOps = null;lock(_workerOperationLock){if (_workerOperation.Count > 0){workerOps = _workerOperation.ToArray();_workerOperation.Clear();}}if (workerOps != null){for (int j=0; j<workerOps.Length; j++){workerOps[j].DoWork();}workerOps = null;}// 這是第二層循環while (true){// 忽略代碼if (!MS.Win32.Penimc.UnsafeNativeMethods.GetPenEvent(_handles[0], _pimcResetHandle.Value,out evt, out stylusPointerId,out cPackets, out cbPacket, out pPackets)){break;}}}

默認 WPF 的觸摸線程都會在第二層循環,在 GetPenEvent 方法里面等待 PenImc 發送觸摸消息或等待 _pimcResetHandle 釋放。在跳出第二層循環,將會去獲取 _workerOperation 的項,然后執行

WorkerOperation[] workerOps = null;lock(_workerOperationLock){if (_workerOperation.Count > 0){workerOps = _workerOperation.ToArray();_workerOperation.Clear();}}if (workerOps != null){for (int j=0; j<workerOps.Length; j++){workerOps[j].DoWork();}workerOps = null;}

獲取觸摸信息

在調用 WorkerOperationGetTabletsInfo 的 DoWork 方法時,將會在觸摸線程獲取觸摸設備信息

private class WorkerOperationGetTabletsInfo : WorkerOperation{internal TabletDeviceInfo[] TabletDevicesInfo{get { return _tabletDevicesInfo;}}//// <summary>/// Returns the list of TabletDeviceInfo structs that contain information/// about all of the TabletDevices on the system./// </summary>protected override void OnDoWork(){try{// create new collection of tabletsMS.Win32.Penimc.IPimcManager3 pimcManager = MS.Win32.Penimc.UnsafeNativeMethods.PimcManager;uint cTablets;pimcManager.GetTabletCount(out cTablets);TabletDeviceInfo[] tablets = new TabletDeviceInfo[cTablets];for ( uint iTablet = 0; iTablet < cTablets; iTablet++ ){MS.Win32.Penimc.IPimcTablet3 pimcTablet;pimcManager.GetTablet(iTablet, out pimcTablet);tablets[iTablet] = PenThreadWorker.GetTabletInfoHelper(pimcTablet);}// Set result data and signal we are done._tabletDevicesInfo = tablets;}catch (Exception e) when (PenThreadWorker.IsKnownException(e)){Debug.WriteLine("WorkerOperationGetTabletsInfo.OnDoWork failed due to: {0}{1}", Environment.NewLine, e.ToString());}}TabletDeviceInfo[] _tabletDevicesInfo = Array.Empty<TabletDeviceInfo>();}

上面代碼的 IPimcManager3 接口是一個 COM 接口,實際邏輯是在 PenImc 層進行定義,在 PenImcRcw.cs 引用,代碼如下

[ComImport,Guid(PimcConstants.IPimcManager3IID),InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]interface IPimcManager3{void GetTabletCount(out UInt32 count);void GetTablet(UInt32 tablet, out IPimcTablet3 IPimcTablet);}

在 PenImc 層的 PenImc.idl 文件里面,定義了公開的接口

[object,uuid(BD2C38C2-E064-41D0-A999-940F526219C2),nonextensible,helpstring("IPimcManager3 Interface"),pointer_default(unique) ] interface IPimcManager3 : IUnknown {[helpstring("method GetTabletCount")] HRESULT GetTabletCount([out] ULONG* pcTablets);[helpstring("method GetTablet") ] HRESULT GetTablet([in] ULONG iTablet, [out] IPimcTablet3** ppTablet); };

在 WPF 中,在 C# 代碼使用的不是最底層的方法,也就是 BD2C38C2-E064-41D0-A999-940F526219C2 組件只是 WPF 用的,而不是系統等給的接口

實際調用底層的代碼是在 PenImc 層的 C++ 代碼,但 PenImc 層的 C++ 代碼只是一層轉發調用而已,換句話說,如果使用 C# 調用底層的系統的組件也是完全可以的

如上面代碼通過 GetTabletCount 方法獲取當前的觸摸設備,此方法是通過 COM 調用到在 PenImc.idl 文件定義的 GetTabletCount 獲取的,實際定義的代碼是 PimcManager.cpp 文件的 GetTabletCount 方法

STDMETHODIMP CPimcManager::GetTabletCount(__out ULONG* pcTablets) {DHR;ULONG cTablets = 0;LoadWisptis(); // Try to load wisptis via the surrogate object.// we will return 0 in the case that there is no stylus since mouse is not considered a stylus anymoreif (m_fLoadedWisptis){CHR(m_pMgrS->GetTabletCount(&cTablets));}*pcTablets = cTablets;CLEANUP:RHR; }

以上代碼里面用到了一些宏,如 DHR 的含義是定義 HRESULT 變量,代碼如下

#define DHR \HRESULT hr = S_OK;

而 CHR 表示的是判斷 HRESULT 的值,如果失敗了,將會調用 CLEANUP 標簽的內容。在 CHR 里面用到 goto 的方法

#define CHR(hr_op) \{ \hr = hr_op; \if (FAILED(hr)) \goto CLEANUP; \}

上面代碼的 RHR 表示的是返回 HRESULT 變量

#define RHR \return hr;

因此以上代碼實際就是如下代碼

STDMETHODIMP CPimcManager::GetTabletCount(__out ULONG* pcTablets) {HRESULT hr = S_OK;ULONG cTablets = 0;LoadWisptis(); // Try to load wisptis via the surrogate object.// we will return 0 in the case that there is no stylus since mouse is not considered a stylus anymoreif (m_fLoadedWisptis){hr = m_pMgrS->GetTabletCount(&cTablets);if (FAILED(hr)){goto CLEANUP;}}*pcTablets = cTablets;CLEANUP:return hr; }

通過上面代碼可以看到,實際調用的是 m_pMgrS 的 GetTabletCount 方法,也就是如下代碼定義的方法

MIDL_INTERFACE("764DE8AA-1867-47C1-8F6A-122445ABD89A")ITabletManager : public IUnknown{public:virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetDefaultTablet( /* [out] */ __RPC__deref_out_opt ITablet **ppTablet) = 0;virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetTabletCount( /* [out] */ __RPC__out ULONG *pcTablets) = 0;virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetTablet( /* [in] */ ULONG iTablet,/* [out] */ __RPC__deref_out_opt ITablet **ppTablet) = 0;virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetTabletContextById( /* [in] */ TABLET_CONTEXT_ID tcid,/* [out] */ __RPC__deref_out_opt ITabletContext **ppContext) = 0;virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE GetCursorById( /* [in] */ CURSOR_ID cid,/* [out] */ __RPC__deref_out_opt ITabletCursor **ppCursor) = 0;};

可以看到這是一個 COM 接口調用,實際使用的就是系統提供的 ITabletManager 組件

在底層系統組件,先調用 ITabletManager 的 GetTabletCount 方法 獲取觸摸設備數量,然后遍歷觸摸設備序號拿到 ITablet 對象

在 C# 代碼里面的邏輯如下

pimcManager.GetTabletCount(out cTablets);TabletDeviceInfo[] tablets = new TabletDeviceInfo[cTablets];for ( uint iTablet = 0; iTablet < cTablets; iTablet++ ){MS.Win32.Penimc.IPimcTablet3 pimcTablet;pimcManager.GetTablet(iTablet, out pimcTablet);tablets[iTablet] = PenThreadWorker.GetTabletInfoHelper(pimcTablet);}

這里的 pimcManager.GetTablet 方法將會調用到 PimcManager.cpp 的 GetTablet 方法

STDMETHODIMP CPimcManager::GetTablet(ULONG iTablet, __deref_out IPimcTablet3** ppTablet) {DHR;switch (iTablet){case RELEASE_MANAGER_EXT:{CHR(m_managerLock.Unlock());}break;default:{CHR(GetTabletImpl(iTablet, ppTablet));}}CLEANUP:RHR; }STDMETHODIMP CPimcManager::GetTabletImpl(ULONG iTablet, __deref_out IPimcTablet3** ppTablet) {DHR;LoadWisptis(); // Make sure wisptis has been loaded! (Can happen when handling OnTabletAdded message)CComPtr<ITablet> pTabS;CComObject<CPimcTablet> * pTabC;// Can only call if we have real tablet hardware which means wisptis must be loaded!CHR(m_fLoadedWisptis ? S_OK : E_UNEXPECTED);CHR(CComObject<CPimcTablet>::CreateInstance(&pTabC));CHR(pTabC->QueryInterface(IID_IPimcTablet3, (void**)ppTablet));CHR(m_pMgrS->GetTablet(iTablet, &pTabS));CHR(pTabC->Init(m_fLoadedWisptis?pTabS:NULL, this));CLEANUP:RHR; }

本質調用的是 m_pMgrS 的 GetTablet 方法,也就是系統提供的 ITabletManager 的 GetTablet 方法 獲取 ITablet 接口。只是在 C++ 代碼里面,將 ITablet 接口再做一層封裝,返回給 C# 的是 IPimcTablet3 接口

接下來就是通過 PenThreadWorker 的 GetTabletInfoHelper 方法獲取觸摸信息

private static TabletDeviceInfo GetTabletInfoHelper(IPimcTablet3 pimcTablet){TabletDeviceInfo tabletInfo = new TabletDeviceInfo();tabletInfo.PimcTablet = new SecurityCriticalDataClass<IPimcTablet3>(pimcTablet);pimcTablet.GetKey(out tabletInfo.Id);pimcTablet.GetName(out tabletInfo.Name);pimcTablet.GetPlugAndPlayId(out tabletInfo.PlugAndPlayId);int iTabletWidth, iTabletHeight, iDisplayWidth, iDisplayHeight;pimcTablet.GetTabletAndDisplaySize(out iTabletWidth, out iTabletHeight, out iDisplayWidth, out iDisplayHeight);tabletInfo.SizeInfo = new TabletDeviceSizeInfo(new Size(iTabletWidth, iTabletHeight),new Size(iDisplayWidth, iDisplayHeight));int caps;pimcTablet.GetHardwareCaps(out caps);tabletInfo.HardwareCapabilities = (TabletHardwareCapabilities)caps;int deviceType;pimcTablet.GetDeviceType(out deviceType);tabletInfo.DeviceType = (TabletDeviceType)(deviceType -1);// // REENTRANCY NOTE: Let a PenThread do this work to avoid reentrancy!// The IPimcTablet3 object is created in the pen thread. If we access it from the UI thread,// COM will set up message pumping which will cause reentrancy here.InitializeSupportedStylusPointProperties(pimcTablet, tabletInfo);tabletInfo.StylusDevicesInfo = GetStylusDevicesInfo(pimcTablet);// Obtain the WispTabletKey for future use in locking the WISP tablet.tabletInfo.WispTabletKey = MS.Win32.Penimc.UnsafeNativeMethods.QueryWispTabletKey(pimcTablet);// If the manager has not already been created and locked, we will lock it here. This is the first opportunity// we will have to lock the manager as it will have been created on the thread to instantiate the first tablet.MS.Win32.Penimc.UnsafeNativeMethods.SetWispManagerKey(pimcTablet);MS.Win32.Penimc.UnsafeNativeMethods.LockWispManager();return tabletInfo;}

實際調用的就是 ITablet 接口 的方法

以上代碼的 pimcTablet.GetKey 方法是在 C++ 層封裝的,而不是系統提供的

STDMETHODIMP CPimcTablet::GetKey(__out INT * pKey) {DHR;CHR(pKey ? S_OK : E_INVALIDARG);*pKey = (INT)PtrToInt(m_pTabS.p); CLEANUP:RHR; }CComPtr<ITablet> m_pTabS;

在 WPF 框架,獲取的方法本質就是通過 Tablet PC 系統組件獲取

更多觸摸請看 WPF 觸摸相關

我搭建了自己的博客 https://blog.lindexi.com/ 歡迎大家訪問,里面有很多新的博客。只有在我看到博客寫成熟之后才會放在csdn或博客園,但是一旦發布了就不再更新

如果在博客看到有任何不懂的,歡迎交流,我搭建了 dotnet 職業技術學院 歡迎大家加入

如有不方便在博客評論的問題,可以加我 QQ 2844808902 交流


本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名林德熙(包含鏈接:http://blog.csdn.net/lindexi_gd ),不得用于商業目的,基于本文修改后的作品務必以相同的許可發布。如有任何疑問,請與我聯系。

總結

以上是生活随笔為你收集整理的dotnet 读 WPF 源代码笔记 插入触摸设备的初始化获取设备信息的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。