Some context:
I have an application that opens the first webcam device on Windows 10 64bit (whatever index 0 is during enumeration of devices) and does some processing on the frames. The application's source code is not accessible.
Question:
I need to make this application to work with two webcams at the same time. I thought maybe there is a way to do the following:
hide webcam 2
run application (picks up webcam 1)
hide webcam 1, unhide webcam 2
run application (picks up webcam 2)
Is there a way to do this without interrupting camera's operation? Note that both applications are running at the same time so hard-disabling a camera is not an option. Calling either a Win32 api or doing this in PowerShell is acceptable.
Thanks!
Thanks to comments on my original question, I managed to solve my problem by hooking into CM_Get_Device_Interface_List_ExW Win32 API call.
I had to verify what API is being called, so I used and API tracer tool (API monitor v2 64bit). Debugger should work too but for some reason my VS debugger did not show me any symbols (possibly missing pdbs).
The original process I tried to hook into is written in C# so I hooked into the call via an injected C# DLL containing EasyHook. Here is my code snippet (actual injection code left out):
using System;
using System.Runtime.InteropServices;
using EasyHook;
public class HookDevices : IEntryPoint
{
LocalHook FunctionLocalHook;
// construct this to hook into calls
HookDevices()
{
try
{
FunctionLocalHook = LocalHook.Create(
LocalHook.GetProcAddress("CfgMgr32.dll", "CM_Get_Device_Interface_List_ExW"),
new FunctionHookDelegate(CM_Get_Device_Interface_List_Ex_Hooked),
this);
FunctionLocalHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
}
catch (Exception ExtInfo)
{
Debug.LogException(ExtInfo);
return;
}
}
[UnmanagedFunctionPointer(CallingConvention.StdCall,
CharSet = CharSet.Unicode,
SetLastError = true)]
delegate uint FunctionHookDelegate(
ref Guid interfaceClassGuid,
string deviceID,
IntPtr buffer,
uint bufferLength,
uint flags,
IntPtr hMachine);
[DllImport("CfgMgr32.dll",
CharSet = CharSet.Unicode,
SetLastError = true,
CallingConvention = CallingConvention.StdCall)]
static extern uint CM_Get_Device_Interface_List_ExW(
ref Guid interfaceClassGuid,
string deviceID,
IntPtr buffer,
uint bufferLength,
uint flags,
IntPtr hMachine);
// this is where we are intercepting all API accesses!
static uint CM_Get_Device_Interface_List_Ex_Hooked(
ref Guid interfaceClassGuid,
string deviceID,
IntPtr buffer,
uint bufferLength,
uint flags,
IntPtr hMachine)
{
// pass-through original API
uint ret = CM_Get_Device_Interface_List_ExW(
ref interfaceClassGuid,
deviceID,
buffer,
bufferLength,
flags,
hMachine);
// do custom logic here and re-arrange "buffer"
return ret;
}
}
Related
I have a project that I developed about seven years ago in Win95, and works in Win7. It is developed in Visual Studio 2005. This application looks for the "You have new email" tray icon that appears in the tray (in various forms) by most email applications. I use it to blink an LED on a serial port, so I can glance in the room to see if I have email, rather than going to the computer, moving the mouse to wake the screen, and looking at the tray or the email program itself. It's a time-saver and aggravation-reducer.
It works by getting the system tray handle, and then using this handle, iterates all the buttons in the tray, and comparing the button text for a specific string. Here is the part that is having a problem in Windows 10:
IntPtr hWndTray = GetSystemTrayHandle();
listBoxIcons.Items.Add(string.Format("Tray handle=0x{0:X}", (int)hWndTray));
UInt32 count = User32.SendMessage(hWndTray, TB.BUTTONCOUNT, 0, 0);
listBoxIcons.Items.Add(string.Format("Tray button count={0:D}", count));
The call to GetSystemTrayHandle() works fine, I get a non-null value. The call to SendMessage(hWndTray, TB.BUTTONCOUNT, ...) returns zero, even though in the test case I'm using, there are nine buttons in the tray.
Did the concept of "tray icons", or the API calls to get them, change in Windows 10?
Here are the API calls I am using:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
From User32.cs:
internal class TB
{
public const uint GETBUTTON = WM.USER + 23 ;
public const uint BUTTONCOUNT = WM.USER + 24 ;
public const uint CUSTOMIZE = WM.USER + 27 ;
public const uint GETBUTTONTEXTA = WM.USER + 45 ;
public const uint GETBUTTONTEXTW = WM.USER + 75 ;
}
Here is the GetSystemTrayHandle() method:
private IntPtr GetSystemTrayHandle()
{
IntPtr hWndTray = FindWindow("Shell_TrayWnd", null);
if (hWndTray != IntPtr.Zero)
{
hWndTray = FindWindowEx(hWndTray, IntPtr.Zero, "TrayNotifyWnd", null);
if (hWndTray != IntPtr.Zero)
{
hWndTray = FindWindowEx(hWndTray, IntPtr.Zero, "SysPager", null);
if (hWndTray != IntPtr.Zero)
{
hWndTray = FindWindowEx(hWndTray, IntPtr.Zero, "ToolbarWindow32", null);
return hWndTray;
}
}
}
return IntPtr.Zero;
}
The "SendMessage" call has existed since about the inception of Windows, so if it no longer works, TB.BUTTONCOUNT may have been redefined or superceded in Windows 10. I cannot find any information on this.
Edit: Developed on Win98, not Win95.
Remy's message spurred further research into notification icons. The answer was found using information at http://www.ghacks.net/2015/03/11/manage-and-display-system-tray-icons-in-windows-10/. Once notifications were enabled, for example "email" and "volume," this program, as written, can now see them.
Edit: this question was originally about behavioural difference between code in C++ and C# but that proved to be a red herring. Question has been rewritten to better reflect the problem I'm having.
I'm trying to list the alternate streams of a file hosted on an SMB server using the NtQueryInformationFile function. The code I've written so far works correctly with most servers but fails sometimes when run against non-samba Mac OS X SMB server.
I've reduced the code form my application to the following bit of code that reproduces the issue reliably:
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace Streams
{
class Program
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class NETRESOURCE
{
public uint dwScope;
public uint dwType;
public uint dwDisplayType;
public uint dwUsage;
public string lpLocalName;
public string lpRemoteName;
public string lpComment;
public string lpProvider;
}
[DllImport("ntdll.dll", SetLastError = false, CharSet = CharSet.Unicode)]
public static extern uint NtQueryInformationFile(
SafeFileHandle handle,
IntPtr IoStatusBlock,
IntPtr pInfoBlock,
uint length,
uint fileInformationClass);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFile(
string fileName,
uint desiredAccessMask,
uint shareMode,
IntPtr lpSecurityAttributes,
uint creationDisposition,
uint flagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("mpr.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint WNetAddConnection2(NETRESOURCE netResource, [MarshalAs(UnmanagedType.LPTStr)] string password, [MarshalAs(UnmanagedType.LPTStr)] string username, uint flags);
static void Main(string[] args)
{
var server = args[0];
var username = args[1];
var password = args[2];
var uncPath = args[3];
NETRESOURCE netResource = new NETRESOURCE();
netResource.dwType = 0x1 /* DISK */;
netResource.lpRemoteName = server;
WNetAddConnection2(netResource, password, username, 0x00000004 /* CONNECT_TEMPORARY */);
var fh = CreateFile(
uncPath,
0x80000000 /* GENERIC_READ */,
(uint)(FileShare.Read | FileShare.Write | FileShare.Delete),
IntPtr.Zero,
3 /* OPEN_EXISTING */,
0x08000000 /* SequentialScan */ | 0x02000000 /* BackupSemantics */,
IntPtr.Zero
);
IntPtr status = Marshal.AllocHGlobal(1024);
uint bufferSize = 64*1024;
IntPtr buffer = Marshal.AllocHGlobal((int)bufferSize);
uint ntStatus = NtQueryInformationFile(fh, status, buffer, bufferSize, 22 /* FileStreamInformation */);
Console.WriteLine($"NtQueryFileInformation returned {ntStatus}");
Console.ReadKey();
}
}
}
In most cases the value of ntStatus is STATUS_OK as is expected. When I try to use this on a file that has alternate data streams with a Mac OS X SMB server I get STATUS_INVALID_NETWORK_RESPONSE instead. That error code seems to indicate that the response from the server is incorrect, but when I inspect this with Wireshark there are no differences compared to when STATUS_OK is returned.
To make things even stranger, this code works when I run it in 32-bit mode (set the 'Prefer 32-bit' in the Visual Studio project settings). When run in 64-bit mode I always get the error code.
The obvious error then would be that the code in question is not 64-bit clean, but AFAICT the code above is.
I've also tried replacing the NtQueryInformationFile call with the call to GetFileInformationByHandleEx, but that gives me the same result (with a Win32 error code instead of an NT status code of course).
What could explain this difference in behaviour between 32-bit and 64-bit mode?
My specific problem:
I need to develop a watchdog app that needs to keep various applications running.
I am using visual studio and a windows environment that supports .net 4.0
Since I am not creating those applications and I do not have access to modify them in any way I can only rely on the information provided by windows.
For the past week I've been trying to find exactly how to get the "not responding" property as shown in task manager for an application.
I have tried:
1 Using system diagnostics, getting the process information and interpreting the information within. The problem is, when an app stops working (crashes) the process is still running, the JIT Debugger message pops and reports the app crashed. At this particular moment Task Manager reports the app "Not responding" but the process information (although it does have a main window handle ) has the property Responding set to "true".
I found a lot of open source task managers (top 5) and all use the "Responding" property to determine if a application is running.
So the problem is : task manager shows not responding, Process property responding = True, this method to determine if an app is not responding is INVALID
2 Sending timeout messages to the main window handler.
I used the SendMessageTimeout functions
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644952(v=vs.85).aspx
*I used the SMTO_ABORTIFHUNG, SMTO_BLOCK, SMTO_NORMAL, SMTO_NOTIMEOUTIFNOTHUNG and SMTO_ERRORONEXIT
And encountered the same problem:
-before the application crashes: reports running ( all messages return 1)
-after the application crashes (JIT debugger popup reports app crashed and displayed "Not Responding" in task manager) all the above messages sent to the process window handler return 1 .
So this method to determine if app is not responding is also INVALID
I am amazed I haven't been able to find relevant resources related to my issue.
Here is some code from what I've tried:
bool IsResponding(Process process)
{
HandleRef handleRef = new HandleRef(process, process.MainWindowHandle);
int timeout = 2000;
IntPtr lpdwResult;
IntPtr lResult = SendMessageTimeout(
handleRef,
0,
IntPtr.Zero,
IntPtr.Zero,
1,
1000,
out lpdwResult);
lResult = SendMessageTimeout(
handleRef,
0,
IntPtr.Zero,
IntPtr.Zero,
2,
1000,
out lpdwResult);
lResult = SendMessageTimeout(
handleRef,
0,
IntPtr.Zero,
IntPtr.Zero,
0,
1000,
out lpdwResult);
lResult = SendMessageTimeout(
handleRef,
0,
IntPtr.Zero,
IntPtr.Zero,
8,
1000,
out lpdwResult);
lResult = SendMessageTimeout(
handleRef,
0,
IntPtr.Zero,
IntPtr.Zero,
20,
1000,
out lpdwResult);
return lResult != IntPtr.Zero;
}
and the testing part:
processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension("...testAppLocation.exe"));
bool test = processes[0].Responding;
test = asd.IsResponding(processes[0]);
I've run the sample in debug mode so I am sure all the messages return 1 as a value whether the actual application is running or it is crashed.
And "processes[0]" was tested against a real process and does have the process information
at runtime.
I am out of ideas and I still haven't figured out what resources Task manager uses to determine an application is "Not Responding".
The first solution, provided with the help of Hans Passant would look like this:
Functions to get active windows (including hung ):
internal static class NativeMethods{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsHungAppWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern Int32 EnumWindows(EnumWindowsCallback lpEnumFunc, Int32 lParam);
[DllImport("user32.dll")]
public static extern void GetWindowText(IntPtr hWnd, StringBuilder lpString, Int32 nMaxCount);
[DllImport("user32.dll")]
static extern UInt64 GetWindowLongA(IntPtr hWnd, Int32 nIndex);
public static readonly Int32 GWL_STYLE = -16;
public static readonly UInt64 WS_VISIBLE = 0x10000000L;
public static readonly UInt64 WS_BORDER = 0x00800000L;
public static readonly UInt64 DESIRED_WS = WS_BORDER | WS_VISIBLE;
public delegate Boolean EnumWindowsCallback(IntPtr hwnd, Int32 lParam);
public static List<WindowWrapper> GetAllWindows()
{
List<WindowWrapper> windows = new List<WindowWrapper>();
StringBuilder buffer = new StringBuilder(100);
EnumWindows(delegate(IntPtr hwnd, Int32 lParam)
{
if ((GetWindowLongA(hwnd, GWL_STYLE) & DESIRED_WS) == DESIRED_WS)
{
GetWindowText(hwnd, buffer, buffer.Capacity);
WindowWrapper wnd = new WindowWrapper();
wnd.handle = hwnd;
wnd.title = buffer.ToString();
windows.Add(wnd);
}
return true;
}, 0);
return windows;
}
.
.
.
What I put in my is hung check-er function:
Note that for each app that is hung (JIT debugger pop-up saying it's not working)
You will get 2 entries with the same window handler title:
The app original window handler - which is taken over by the jit debugger will return
false (that means it's not hung) while for the other entry -which is assigned a new
IntPtr the IsHungAppWindow will return true
foreach (var wnd in NativeMethods.GetAllWindows())
{
Console.WriteLine(wnd.title + " and status is " + IsHungAppWindow(wnd.Handle));
}
If anyone has other specific, tested solutions to this issue I would greatly appreciate them.
You'll need to pinvoke IsHungAppWindow(). It returns true when the window is replaced by the ghost window that displays "Not responding".
And keep in mind that this never means that the app crashed and not that often means that the app is actually hung. Certainly not on my pokey laptop when it tries to run VS2010+. Or most any Winforms or WPF app written by a programmer that hasn't yet mastered the art of threading. So don't just arbitrarily kill processes.
I'm developing a USB device as a standard HID keyboard with an 8-byte feature report added to the report descriptor; I'm writing an host app to configure the device. I'm trying to adapt the wonderful HidLibrary to utilize the HidD_GetFeature() function in hid.dll.
Before I start posting c# code, I will say that I have successfully tested my firmware using the SimpleHidWrite utility with both Get and Set Feature commands, so I'm fairly confident that's not the issue.
The HidD_SetFeature() function, as wrapped in the HidLibrary API, works great. I can write 8 bytes to the device and I've verified using the SimpleHidWrite tool that they are stored correctly. However, I can't pull those bytes back using HidD_GetFeature(), and I'm baffled as to why.
Here are what I believe to be the pertinent details.
First, the CreateFile call built into the library with values:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
int dwShareMode,
ref HidLibrary.NativeMethods.SECURITY_ATTRIBUTES lpSecurityAttributes,
int dwCreationDisposition,
int dwFlagsAndAttributes,
int hTemplateFile);
where (using test vid/pid):
lpFileName = "\\?\hid#vid_03eb&pid_2042#7&1fef463f&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
dwDesiredAccess = 0
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE (0x01 | 0x02)
lpSecurityAttributes = { bInheritHandle = true, lpSecurityDescriptor = 0, nLength = 12 }
dwCreationDisposition = OPEN_EXISTING (3)
dwFlagsAndAttributes = 0
hTemplateFile = 0
The import definition:
[DllImport("hid.dll", SetLastError = true)]
static internal extern bool HidD_GetFeature(IntPtr hidDeviceObject, ref byte[] lpReportBuffer, int reportBufferLength);
And finally the API method I'm creating that is currently failing on the HidD_GetFeature() call with error 23 (ERROR_CRC):
public bool ReadFeatureData(byte reportId, out byte[] data)
{
if (_deviceCapabilities.FeatureReportByteLength <= 0)
{
data = new byte[0];
return false;
}
// FeatureReportByteLength returns 9 (byte 0 is the report id and 8 bytes for the actual report length)
data = new byte[_deviceCapabilities.FeatureReportByteLength];
//yields a 9-byte array
var buffer = this.CreateFeatureInputBuffer();
buffer[0] = reportId;
IntPtr hidHandle = IntPtr.Zero;
bool success = false;
try
{
// Performs the CreateFile call above resulting in an IntPtr handle
hidHandle = OpenDeviceIO(_devicePath, NativeMethods.ACCESS_NONE);
success = NativeMethods.HidD_GetFeature(hidHandle, ref buffer, buffer.Length);
// at this point, success is false, and buffer has gone from 9 bytes to 1
if(success)
{
Array.Copy(buffer, 0, data, 0, Math.Min(data.Length, _deviceCapabilities.FeatureReportByteLength));
}
else
{
//Yes, i know casting to a byte isn't good here; it's dirty but helping me debug for now
data[0] = (byte)Marshal.GetLastWin32Error(); //returns 23 (verified, actual) - ERROR_CRC
}
}
catch (Exception exception)
{
throw new Exception(string.Format("Error accessing HID device '{0}'.", _devicePath), exception);
}
finally
{
if (hidHandle != IntPtr.Zero)
CloseDeviceIO(hidHandle);
}
return success;
}
I've read there are some issues with working with system devices like keyboards and mice, and that may or may not play in here, however I know that it's possible to interact with the device in the way I'm trying because I was able to do so with SimpleHidWrite. That said, I haven't ruled anything out and any ideas are welcome.
Let me know if I need to provide any more info.
I think this declaration may be the problem.
[DllImport("hid.dll", SetLastError = true)]
static internal extern bool HidD_GetFeature(IntPtr hidDeviceObject, ref byte[] lpReportBuffer, int reportBufferLength);
Try changing the lpReportBuffer ref parameter to out, or omitting it altogether.
Yes your declaration is the problem.
use:
[DllImport("hid.dll", SetLastError = true)]
protected static extern bool HidD_GetFeature( IntPtr hDevInfo,
Byte[] lpReportBuffer,
Int32 ReportBufferLength);
NO: ref or out
is it possible to simulate Click on a process without actually clicking on it?
e.g. I wanna Click on a running Calculator with mouse stand still. is this possible?
If you are just trying to click a button within a fairly typical labels, fields, and buttons application, you can use a little bit of P/Invoke to use FindWindow and SendMessage to the control.
If you are not already familiar with Spy++, now is the time to start!
It is packaged with Visual Studio 2012 RC in: C:\Program Files\Microsoft Visual Studio 11.0\Common7\Tools. It should be similarly found for other versions.
Try this as Console C# application:
class Program
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
private const uint BM_CLICK = 0x00F5;
static void Main(string[] args)
{
// Get the handle of the window
var windowHandle = FindWindow((string)null, "Form1");
// Get button handle
var buttonHandle = FindWindowEx(windowHandle, IntPtr.Zero, (string)null, "A Huge Button");
// Send click to the button
SendMessage(buttonHandle, BM_CLICK, 0, 0);
}
}
This gets the handle to the window captioned "Form1". Using that handle, it gets a handle to the Button within the Window. Then sends a message of type "BM_CLICK" with no useful params to the button control.
I used a test WinForms app as my target. A single button and some code behind to increment a counter.
You should see the counter increment when you run the P/Invoke console application. However, you will not see the button animate.
You could also use the Spy++ Message Logger function. I recommend a filter to BM_CLICK and maybe WM_LBUTTONDOWN/WM_LBUTTONUP (what a manual click will give you).
Hope that helps!