win32Api提供一种方法,模拟用户触摸点击
- InitializeTouchInjection
- InjectTouchInput
在模拟添加触摸输入(InjectTouchInput)前,需要提前初始化(InitializeTouchInjection)
1 /// <summary> 2 /// Use this Classes static methods to initialize and inject touch input. 3 /// </summary> 4 public class NativeMethods 5 { 6 /// <summary> 7 /// Call this first to initialize the TouchInjection! 8 /// </summary> 9 /// <param name="maxCount">The maximum number of touch points to simulate. Must be less than 256!</param> 10 /// <param name="feedbackMode">Specifies the visual feedback mode of the generated touch points</param> 11 /// <returns>true if success</returns> 12 [DllImport("User32.dll")] 13 public static extern bool InitializeTouchInjection(uint maxCount = 256, TouchFeedback feedbackMode = TouchFeedback.DEFAULT); 14 15 /// <summary> 16 /// Inject an array of POINTER_TUCH_INFO 17 /// </summary> 18 /// <param name="count">The exact number of entries in the array</param> 19 /// <param name="contacts">The POINTER_TOUCH_INFO to inject</param> 20 /// <returns>true if success</returns> 21 [DllImport("User32.dll")] 22 public static extern bool InjectTouchInput(int count, [MarshalAs(UnmanagedType.LPArray), In] PointerTouchInfo[] contacts); 23 } 24 25 static class IdGenerator 26 { 27 static private int _int; 28 private static uint _uint; 29 private static readonly object _mutex = new object(); 30 31 public static int GetUniqueInt() 32 { 33 Interlocked.Increment(ref _int); 34 return _int; 35 } 36 37 public static uint GetUinqueUInt() 38 { 39 lock (_mutex) 40 { 41 if (_uint > 256) 42 { 43 ResetUint(); 44 } 45 if (_uint == uint.MaxValue) 46 throw new IndexOutOfRangeException(); 47 else 48 { 49 _uint++; 50 return _uint; 51 } 52 } 53 } 54 55 public static void ResetUint() 56 { 57 lock (_mutex) 58 { 59 _uint = uint.MinValue; 60 } 61 } 62 } 63 64 #region Types 65 /// <summary> 66 /// Enum of touch visualization options 67 /// </summary> 68 public enum TouchFeedback 69 { 70 /// <summary> 71 /// Specifies default touch visualizations. 72 /// </summary> 73 DEFAULT = 0x1, 74 /// <summary> 75 /// Specifies indirect touch visualizations. 76 /// </summary> 77 INDIRECT = 0x2, 78 /// <summary> 79 /// Specifies no touch visualizations. 80 /// </summary> 81 NONE = 0x3 82 } 83 84 /// <summary> 85 /// The contact area. 86 /// </summary> 87 [StructLayout(LayoutKind.Explicit)] 88 public struct ContactArea 89 { 90 [FieldOffset(0)] 91 public int left; 92 [FieldOffset(4)] 93 public int top; 94 [FieldOffset(8)] 95 public int right; 96 [FieldOffset(12)] 97 public int bottom; 98 } 99 100 /// <summary> 101 /// Values that can appear in the TouchMask field of the PointerTouchInfo structure 102 /// </summary> 103 public enum TouchFlags 104 { 105 /// <summary> 106 /// Indicates that no flags are set. 107 /// </summary> 108 NONE = 0x00000000 109 } 110 111 /// <summary> 112 /// Values that can appear in the TouchMask field of the PointerTouchInfo structure. 113 /// </summary> 114 public enum TouchMask 115 { 116 /// <summary> 117 /// Default. None of the optional fields are valid. 118 /// </summary> 119 NONE = 0x00000000, 120 /// <summary> 121 /// The ContactArea field is valid 122 /// </summary> 123 CONTACTAREA = 0x00000001, 124 /// <summary> 125 /// The orientation field is valid 126 /// </summary> 127 ORIENTATION = 0x00000002, 128 /// <summary> 129 /// The pressure field is valid 130 /// </summary> 131 PRESSURE = 0x00000004 132 } 133 134 /// <summary> 135 /// Values that can appear in the PointerFlags field of the PointerInfo structure. 136 /// </summary> 137 public enum PointerFlags 138 { 139 /// <summary> 140 /// Default 141 /// </summary> 142 NONE = 0x00000000, 143 /// <summary> 144 /// Indicates the arrival of a new pointer 145 /// </summary> 146 NEW = 0x00000001, 147 /// <summary> 148 /// Indicates that this pointer continues to exist. When this flag is not set, it indicates the pointer has left detection range. 149 /// This flag is typically not set only when a hovering pointer leaves detection range (PointerFlag.UPDATE is set) or when a pointer in contact with a window surface leaves detection range (PointerFlag.UP is set). 150 /// </summary> 151 INRANGE = 0x00000002, 152 /// <summary> 153 /// Indicates that this pointer is in contact with the digitizer surface. When this flag is not set, it indicates a hovering pointer. 154 /// </summary> 155 INCONTACT = 0x00000004, 156 /// <summary> 157 /// Indicates a primary action, analogous to a mouse left button down. 158 ///A touch pointer has this flag set when it is in contact with the digitizer surface. 159 ///A pen pointer has this flag set when it is in contact with the digitizer surface with no buttons pressed. 160 ///A mouse pointer has this flag set when the mouse left button is down. 161 /// </summary> 162 FIRSTBUTTON = 0x00000010, 163 /// <summary> 164 /// Indicates a secondary action, analogous to a mouse right button down. 165 /// A touch pointer does not use this flag. 166 /// A pen pointer has this flag set when it is in contact with the digitizer surface with the pen barrel button pressed. 167 /// A mouse pointer has this flag set when the mouse right button is down. 168 /// </summary> 169 SECONDBUTTON = 0x00000020, 170 /// <summary> 171 /// Indicates a secondary action, analogous to a mouse right button down. 172 /// A touch pointer does not use this flag. 173 /// A pen pointer does not use this flag. 174 /// A mouse pointer has this flag set when the mouse middle button is down. 175 /// </summary> 176 THIRDBUTTON = 0x00000040, 177 /// <summary> 178 /// Indicates actions of one or more buttons beyond those listed above, dependent on the pointer type. Applications that wish to respond to these actions must retrieve information specific to the pointer type to determine which buttons are pressed. For example, an application can determine the buttons states of a pen by calling GetPointerPenInfo and examining the flags that specify button states. 179 /// </summary> 180 OTHERBUTTON = 0x00000080, 181 /// <summary> 182 /// Indicates that this pointer has been designated as primary. A primary pointer may perform actions beyond those available to non-primary pointers. For example, when a primary pointer makes contact with a window’s surface, it may provide the window an opportunity to activate by sending it a WM_POINTERACTIVATE message. 183 /// </summary> 184 PRIMARY = 0x00000100, 185 /// <summary> 186 /// Confidence is a suggestion from the source device about whether the pointer represents an intended or accidental interaction, which is especially relevant for PT_TOUCH pointers where an accidental interaction (such as with the palm of the hand) can trigger input. The presence of this flag indicates that the source device has high confidence that this input is part of an intended interaction. 187 /// </summary> 188 CONFIDENCE = 0x00000200, 189 /// <summary> 190 /// Indicates that the pointer is departing in an abnormal manner, such as when the system receives invalid input for the pointer or when a device with active pointers departs abruptly. If the application receiving the input is in a position to do so, it should treat the interaction as not completed and reverse any effects of the concerned pointer. 191 /// </summary> 192 CANCELLED = 0x00000400, 193 /// <summary> 194 /// Indicates that this pointer just transitioned to a “down” state; that is, it made contact with the window surface. 195 /// </summary> 196 DOWN = 0x00010000, 197 /// <summary> 198 /// Indicates that this information provides a simple update that does not include pointer state changes. 199 /// </summary> 200 UPDATE = 0x00020000, 201 /// <summary> 202 /// Indicates that this pointer just transitioned to an “up” state; that is, it broke contact with the window surface. 203 /// </summary> 204 UP = 0x00040000, 205 /// <summary> 206 /// Indicates input associated with a pointer wheel. For mouse pointers, this is equivalent to the action of the mouse scroll wheel (WM_MOUSEWHEEL). 207 /// </summary> 208 WHEEL = 0x00080000, 209 /// <summary> 210 /// Indicates input associated with a pointer h-wheel. For mouse pointers, this is equivalent to the action of the mouse horizontal scroll wheel (WM_MOUSEHWHEEL). 211 /// </summary> 212 HWHEEL = 0x00100000 213 } 214 215 /// <summary> 216 /// The TouchPoint structure defines the x- and y- coordinates of a point. 217 /// </summary> 218 [StructLayout(LayoutKind.Sequential)] 219 public struct TouchPoint 220 { 221 /// <summary> 222 /// The x-coordinate of the point. 223 /// </summary> 224 public int X; 225 /// <summary> 226 /// The y-coordinate of the point. 227 /// </summary> 228 public int Y; 229 } 230 231 /// <summary> 232 /// Identifies the pointer input types. 233 /// </summary> 234 public enum PointerInputType 235 { 236 /// <summary> 237 /// Generic pointer type. This type never appears in pointer messages or pointer data. Some data query functions allow the caller to restrict the query to specific pointer type. The PT_POINTER type can be used in these functions to specify that the query is to include pointers of all types 238 /// </summary> 239 POINTER = 0x00000001, 240 /// <summary> 241 /// Touch pointer type. 242 /// </summary> 243 TOUCH = 0x00000002, 244 /// <summary> 245 /// Pen pointer type. 246 /// </summary> 247 PEN = 0x00000003, 248 /// <summary> 249 /// Mouse pointer type 250 /// </summary> 251 MOUSE = 0x00000004, 252 /// <summary> 253 /// touchpad pointer type 254 /// </summary> 255 TOUCHPAD = 0x00000005 256 }; 257 258 /// <summary> 259 /// Contains basic pointer information common to all pointer types. Applications can retrieve this information using the GetPointerInfo, GetPointerFrameInfo, GetPointerInfoHistory and GetPointerFrameInfoHistory functions. 260 /// </summary> 261 [StructLayout(LayoutKind.Sequential)] 262 public struct PointerInfo 263 { 264 /// <summary> 265 /// A value from the PointerInputType enumeration that specifies the pointer type. 266 /// </summary> 267 public PointerInputType pointerType; 268 269 /// <summary> 270 /// An identifier that uniquely identifies a pointer during its lifetime. A pointer comes into existence when it is first detected and ends its existence when it goes out of detection range. Note that if a physical entity (finger or pen) goes out of detection range and then returns to be detected again, it is treated as a new pointer and may be assigned a new pointer identifier. 271 /// </summary> 272 public uint PointerId; 273 274 /// <summary> 275 /// An identifier common to multiple pointers for which the source device reported an update in a single input frame. For example, a parallel-mode multi-touch digitizer may report the positions of multiple touch contacts in a single update to the system. 276 /// Note that frame identifier is assigned as input is reported to the system for all pointers across all devices. Therefore, this field may not contain strictly sequential values in a single series of messages that a window receives. However, this field will contain the same numerical value for all input updates that were reported in the same input frame by a single device. 277 /// </summary> 278 public uint FrameId; 279 280 /// <summary> 281 /// May be any reasonable combination of flags from the Pointer Flags constants. 282 /// </summary> 283 public PointerFlags PointerFlags; 284 285 /// <summary> 286 /// Handle to the source device that can be used in calls to the raw input device API and the digitizer device API. 287 /// </summary> 288 public IntPtr SourceDevice; 289 290 /// <summary> 291 /// Window to which this message was targeted. If the pointer is captured, either implicitly by virtue of having made contact over this window or explicitly using the pointer capture API, this is the capture window. If the pointer is uncaptured, this is the window over which the pointer was when this message was generated. 292 /// </summary> 293 public IntPtr WindowTarget; 294 295 /// <summary> 296 /// Location in screen coordinates. 297 /// </summary> 298 public TouchPoint PtPixelLocation; 299 300 /// <summary> 301 /// Location in device coordinates. 302 /// </summary> 303 public TouchPoint PtPixelLocationRaw; 304 305 /// <summary> 306 /// Location in HIMETRIC units. 307 /// </summary> 308 public TouchPoint PtHimetricLocation; 309 310 /// <summary> 311 /// Location in device coordinates in HIMETRIC units. 312 /// </summary> 313 public TouchPoint PtHimetricLocationRaw; 314 315 /// <summary> 316 /// A message time stamp assigned by the system when this input was received. 317 /// </summary> 318 public uint Time; 319 320 /// <summary> 321 /// Count of inputs that were coalesced into this message. This count matches the total count of entries that can be returned by a call to GetPointerInfoHistory. If no coalescing occurred, this count is 1 for the single input represented by the message. 322 /// </summary> 323 public uint HistoryCount; 324 325 /// <summary> 326 /// A value whose meaning depends on the nature of input. 327 /// When flags indicate PointerFlag.WHEEL, this value indicates the distance the wheel is rotated, expressed in multiples or factors of WHEEL_DELTA. A positive value indicates that the wheel was rotated forward and a negative value indicates that the wheel was rotated backward. 328 /// When flags indicate PointerFlag.HWHEEL, this value indicates the distance the wheel is rotated, expressed in multiples or factors of WHEEL_DELTA. A positive value indicates that the wheel was rotated to the right and a negative value indicates that the wheel was rotated to the left. 329 /// </summary> 330 public uint InputData; 331 332 /// <summary> 333 /// Indicates which keyboard modifier keys were pressed at the time the input was generated. May be zero or a combination of the following values. 334 /// POINTER_MOD_SHIFT – A SHIFT key was pressed. 335 /// POINTER_MOD_CTRL – A CTRL key was pressed. 336 /// </summary> 337 public uint KeyStates; 338 339 /// <summary> 340 /// TBD 341 /// </summary> 342 public ulong PerformanceCount; 343 344 /// <summary> 345 /// ??? 346 /// </summary> 347 public PointerButtonChangeType ButtonChangeType; 348 } 349 350 /// <summary> 351 /// Enumeration of PointerButtonChangeTypes 352 /// </summary> 353 public enum PointerButtonChangeType 354 { 355 NONE, 356 FIRSTBUTTON_DOWN, 357 FIRSTBUTTON_UP, 358 SECONDBUTTON_DOWN, 359 SECONDBUTTON_UP, 360 THIRDBUTTON_DOWN, 361 THIRDBUTTON_UP, 362 FOURTHBUTTON_DOWN, 363 FOURTHBUTTON_UP, 364 FIFTHBUTTON_DOWN, 365 FIFTHBUTTON_UP 366 } 367 368 /// <summary> 369 /// Contains information about a 'contact' (coordinates, size, pressure...) 370 /// </summary> 371 [StructLayout(LayoutKind.Sequential)] 372 public struct PointerTouchInfo 373 { 374 ///<summary> 375 /// Contains basic pointer information common to all pointer types. 376 ///</summary> 377 public PointerInfo PointerInfo; 378 379 ///<summary> 380 /// Lists the touch flags. 381 ///</summary> 382 public TouchFlags TouchFlags; 383 384 /// <summary> 385 /// Indicates which of the optional fields contain valid values. The member can be zero or any combination of the values from the Touch Mask constants. 386 /// </summary> 387 public TouchMask TouchMasks; 388 389 ///<summary> 390 /// Pointer contact area in pixel screen coordinates. 391 /// By default, if the device does not report a contact area, 392 /// this field defaults to a 0-by-0 rectangle centered around the pointer location. 393 ///</summary> 394 public ContactArea ContactArea; 395 396 /// <summary> 397 /// A raw pointer contact area. 398 /// </summary> 399 public ContactArea ContactAreaRaw; 400 401 ///<summary> 402 /// A pointer orientation, with a value between 0 and 359, where 0 indicates a touch pointer 403 /// aligned with the x-axis and pointing from left to right; increasing values indicate degrees 404 /// of rotation in the clockwise direction. 405 /// This field defaults to 0 if the device does not report orientation. 406 ///</summary> 407 public uint Orientation; 408 409 ///<summary> 410 /// Pointer pressure normalized in a range of 0 to 256. 411 ///</summary> 412 public uint Pressure; 413 414 /// <summary> 415 /// Move the touch point, together with its ContactArea 416 /// </summary> 417 /// <param name="deltaX">the change in the x-value</param> 418 /// <param name="deltaY">the change in the y-value</param> 419 public void Move(int deltaX, int deltaY) 420 { 421 PointerInfo.PtPixelLocation.X += deltaX; 422 PointerInfo.PtPixelLocation.Y += deltaY; 423 ContactArea.left += deltaX; 424 ContactArea.right += deltaX; 425 ContactArea.top += deltaY; 426 ContactArea.bottom += deltaY; 427 } 428 } 429 #endregion
我们模拟触摸A(100,100)并移动到(500,500):
提前初始化“触摸注入”
1 private void MainWindow_Loaded(object sender, RoutedEventArgs e) 2 { 3 NativeMethods.InitializeTouchInjection(); 4 }
模拟触摸移动:
1 private void FakeTouchMove(int fromX, int fromY, int toX, int toY) 2 { 3 // Touch Down 4 PointerTouchInfo contact = MakePointerTouchInfo(fromX, fromY, 5, 1); 5 PointerFlags oFlags = PointerFlags.DOWN | PointerFlags.INRANGE | PointerFlags.INCONTACT; 6 contact.PointerInfo.PointerFlags = oFlags; 7 NativeMethods.InjectTouchInput(1, new[] { contact }); 8 9 // Touch Move 10 int movedX = toX - fromX; 11 int movedY = toY - fromY; 12 contact.Move(movedX, movedY); 13 oFlags = PointerFlags.INRANGE | PointerFlags.INCONTACT | PointerFlags.UPDATE; 14 contact.PointerInfo.PointerFlags = oFlags; 15 NativeMethods.InjectTouchInput(1, new[] { contact }); 16 17 // Touch Up 18 contact.PointerInfo.PointerFlags = PointerFlags.UP; 19 NativeMethods.InjectTouchInput(1, new[] { contact }); 20 } 21 private PointerTouchInfo MakePointerTouchInfo(int x, int y, int radius, 22 uint orientation = 90, uint pressure = 32000) 23 { 24 PointerTouchInfo contact = new PointerTouchInfo(); 25 contact.PointerInfo.pointerType = PointerInputType.TOUCH; 26 contact.TouchFlags = TouchFlags.NONE; 27 contact.Orientation = orientation; 28 contact.Pressure = pressure; 29 contact.TouchMasks = TouchMask.CONTACTAREA | TouchMask.ORIENTATION | TouchMask.PRESSURE; 30 contact.PointerInfo.PtPixelLocation.X = x; 31 contact.PointerInfo.PtPixelLocation.Y = y; 32 uint unPointerId = IdGenerator.GetUinqueUInt(); 33 Console.WriteLine("PointerId " + unPointerId); 34 contact.PointerInfo.PointerId = unPointerId; 35 contact.ContactArea.left = x - radius; 36 contact.ContactArea.right = x + radius; 37 contact.ContactArea.top = y - radius; 38 contact.ContactArea.bottom = y + radius; 39 return contact; 40 }
界面接收触摸消息:
使用InjectTouchInput函数模拟触摸,Touch、Stylus消息都可以接收到。
1 private Line _proxyLine; 2 private void MainWindow_TouchDown(object sender, TouchEventArgs e) 3 { 4 System.Windows.Input.TouchPoint oPos = e.GetTouchPoint(this); 5 Line oLine = new Line(); 6 oLine.Stroke = new SolidColorBrush(Colors.Red); 7 oLine.StrokeThickness = 2; 8 oLine.X1 = oPos.Position.X; 9 oLine.Y1 = oPos.Position.Y; 10 _proxyLine = oLine; 11 Console.WriteLine("TouchDown;TouchID " + e.TouchDevice.Id + " TouchDown " + oPos.Position.X + " " + oPos.Position.Y); 12 } 13 14 private void MainWindow_TouchMove(object sender, TouchEventArgs e) 15 { 16 System.Windows.Input.TouchPoint movedPoint = e.GetTouchPoint(this); 17 Console.WriteLine("TouchMove:TouchID " + e.TouchDevice.Id + " TouchMove " + movedPoint.Position.X + " " + movedPoint.Position.Y); 18 } 19 20 private void MainWindow_TouchUp(object sender, TouchEventArgs e) 21 { 22 System.Windows.Input.TouchPoint oPos = e.GetTouchPoint(this); 23 this._proxyLine.X2 = oPos.Position.X; 24 this._proxyLine.Y2 = oPos.Position.Y; 25 RootGrid.Children.Add(this._proxyLine); 26 Console.WriteLine("TouchUp:TouchID " + e.TouchDevice.Id + " TouchUp " + oPos.Position.X + " " + oPos.Position.Y); 27 }
触摸模拟结果: