Detect Zoom-Control in .NET 8 application

49 Views Asked by At

During remote control in a Zoom meeting, colleagues are causing accidental drag+drop actions in my application (WPF, .NET 8) due to the latency. I now want to deactivate drag+drop in my software in such cases. I have found the following properties for this:

IsRemoteSession

IsRemotelyControlled

However, the properties IsRemoteSession and IsRemotelyControlled do not work in a Zoom meeting, but apparently only in "real" RDP sessions.

Does anyone know of a way to recognise the control through a Zoom share in a .NET application?

1

There are 1 best solutions below

0
Dai On BEST ANSWER

During remote control in a Zoom meeting, colleagues are causing accidental drag+drop actions in my application (WPF, .NET 8) due to the latency.

I might suggest those kinds of situations would be perfect for a practical demonstration of my favourite kind of ethernet cable, which should certainly help with the latency issue you've got there.

Moving on...

Does anyone know of a way to recognise the control through a Zoom share in a .NET application?

Well, a trivial workaround would be to check if( System.Diagnostics.Process.GetProcessesByName( "zoom.exe" ).Any() ) { _ = Process.Start( "shutdown", "/s /t 0" ); } but that might be a tad too effective.

Instead of trying to detect Zoom - which is just one specific modernday annoyance competing with dozens other fungible upstarts today; what you need is a general-solution that can discern simulated input apart from real input: I find that most (non-Microsoft/non-Citrix) screensharing and remote-control software will just reach for Win32's SendInput blunt-instrument, which, fortunately, makes it easy to detect and ignore.


Now, such as it is, WPF's MouseEventArgs object extends InputEventArgs which exposes an InputDevice property, which I initially assumed would indicate if an event came from a physical mouse or another device - or SendInput - or whathave you.

...but apparently it isn't as useful as I thought: while it does work to differentiate Touch-input from Stylus-input from Mouse-input, it doesn't seem to differentiate between fake mouse-input from SendInput/SendMessage/etc et al compared to physical mice, as you discovered.

Fortunately, an alternative solution exists in Win32's GetCurrentInputMessageSource(), as some folks discovered previously on StackOverflow.


The GetCurrentInputMessageSource( [out] INPUT_MESSAGE_SOURCE* inputMessageSource ) function indicates exactly which device or source the last Win32 hWnd window-message received by the current UI thread was from.

...so just call GetCurrentInputMessageSource directly and immediately from within your mouse-move or mouse-click or mouse-ate-the-cheese event-handler - and before you do anything with async/await as you need to be calling it from the UI thread.


As it's 2024 now, the ol' classic forever-trapped-in-the-year-2005 website PInvoke.net appears to have gone-off and joined its contemporary, MySpace, in the cloud above - so now we have to use a new tool from Microsoft called CsWin32 to generate extern functions and struct defintitions directly from Windows' header files - so the days of enterprise-grade mission-critical software being heavily dependent on atrocious, VB.NET-only, user-submitted code to pinvoke.net are over.

Get it from NuGet.

Anyway, in .NET 4.x, your import should look like this:

[return: MarshalAs( UnmanagedType.Bool )]
[DllImport( "User32.dll", EntryPoint = "GetCurrentInputMessageSource", SetLastError = true )]
static extern Boolean GetCurrentInputMessageSource( ref INPUT_MESSAGE_SOURCE inputMessageSource );

[StructLayout( LayoutKind.Sequential )]
struct INPUT_MESSAGE_SOURCE
{
    public INPUT_MESSAGE_DEVICE_TYPE deviceType;
    public INPUT_MESSAGE_ORIGIN_ID   originId;
}

[Flags]
enum INPUT_MESSAGE_DEVICE_TYPE : UInt32 /* DWORD */
{
  IMDT_UNAVAILABLE = 0x00000000,
  IMDT_KEYBOARDv   = 0x00000001,
  IMDT_MOUSE       = 0x00000002,
  IMDT_TOUCH       = 0x00000004,
  IMDT_PEN         = 0x00000008,
  IMDT_TOUCHPAD    = 0x00000010
}

[Flags]
enum INPUT_MESSAGE_ORIGIN_ID : UInt32 /* DWORD */
{
  IMO_UNAVAILABLE  = 0x00000000,
  IMO_HARDWARE     = 0x00000001,
  IMO_INJECTED     = 0x00000002,
  IMO_SYSTEM       = 0x00000004
}

In .NET 8, it should be something like this (I can't currently test it right now):

[return: MarshalAs( UnmanagedType.Bool )]
[LibraryImport( "User32.dll", EntryPoint = "GetCurrentInputMessageSource", SetLastError = true )]
static extern Boolean GetCurrentInputMessageSource( ref INPUT_MESSAGE_SOURCE inputMessageSource );