Detect USB Eject event with WMI - c#

I use code like here Detecting USB drive insertion and removal using windows service and c# to detect when a USB device is plugged in / removed.
But when the user "ejects" the device leaving it plugged in I get no notification at all.
Of course when I try to access such a drive it will fail.
Is there a different WMI Event I can use to get a notification about this situation?
The link #Jimi posted brings me a (BIG) step further.
public void AddWMIWatcher() {
WqlEventQuery query = new WqlEventQuery();
ManagementScope scope = new ManagementScope("root\\CIMV2");
query.EventClassName = "__InstanceOperationEvent";
query.WithinInterval = new TimeSpan(0, 0, 3);
query.Condition = #"TargetInstance ISA 'Win32_DiskDrive' ";
ManagementEventWatcher watcher = new ManagementEventWatcher(scope, query);
watcher.EventArrived += Watcher_EventArrived;
watcher.Query = query;
watcher.Start();
}
private void Watcher_EventArrived(object sender, EventArrivedEventArgs e) {
ManagementBaseObject baseObject = e.NewEvent;
if(baseObject.ClassPath.ClassName.Equals("__InstanceCreationEvent")) {
Console.WriteLine("A drive was connected");
}
else if(baseObject.ClassPath.ClassName.Equals("__InstanceDeletionEvent")) {
Console.WriteLine("A drive was removed");
}
else if(baseObject.ClassPath.ClassName.Equals("__InstanceModificationEvent")) {
Console.WriteLine("A drive was changed");
//that is what I'm looking for
}
I get an __InstanceModificationEvent - the only thing I have to find out is how to get the affected drive (letter) out of this event.

Related

Retrieving value from bluetooth BLE device

In my windows forms app I am starting to enumerate list of devices as follows:
DeviceWatcher deviceWatcher =
DeviceInformation.CreateWatcher(
BluetoothLEDevice.GetDeviceSelectorFromPairingState(false),
requestedProperties,
DeviceInformationKind.AssociationEndpoint);
// Register event handlers before starting the watcher.
// Added, Updated and Removed are required to get all nearby devices
deviceWatcher.Added += DeviceWatcher_Added;
// EnumerationCompleted and Stopped are optional to implement.
deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
deviceWatcher.Stopped += DeviceWatcher_Stopped;
// Start the watcher.
deviceWatcher.Start();
when a device is found I am retrieving the service and characteristic as follows:
BluetoothLEDevice btdev;
List<DeviceInformation> lst = new List<DeviceInformation>();
GattCharacteristic ch;
private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
if (args.Name == "Bluno")
{
btdev = await BluetoothLEDevice.FromIdAsync(args.Id);
GattDeviceServicesResult result = await btdev.GetGattServicesAsync();
Guid customGuid = new Guid("0000dfb0-0000-1000-8000-00805f9b34fb");
foreach (GattDeviceService service in result.Services)
{
if (customGuid == service.Uuid)
{
GattCharacteristicsResult cresult = await service.GetCharacteristicsAsync();
ch = cresult.Characteristics.Where(x => x.Uuid == new Guid("0000dfb1-0000-1000-8000-00805f9b34fb")).FirstOrDefault();
ch.ValueChanged += Ch_ValueChanged;
}
}
}
}
the issue is that it is not firing the ValueChanged event even if the BLE device continuously sending multiple values per second.
help appreciated

How to know if a specific device removed C#

I am developing a program that uses a hardware (I am using Arduino) to collect data and then store the data in the database. As there is no way to get assured that the serialport is receiving the data, I am trying to implement some code snippet to declare hardware removal and then check if there is my desired device still connected or otherwise inform the user.
What I have come to up to now is:
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WM_DEVICECHANGE:
switch ((int)m.WParam)
{
case DBT_DEVICEARRIVAL:
listBox1.Items.Add("New Device Arrived");
int devType = Marshal.ReadInt32(m.LParam, 4);
if (devType == DBT_DEVTYP_PORT)
{
string portName = Marshal.PtrToStringAuto((IntPtr)((long)m.LParam + 12));
listBox1.Items.Add("the name of the port is " + portName);
}
break;
case DBT_DEVICEREMOVECOMPLETE:
{
listBox1.Items.Add("Device Removed");
}
break;
}
break;
}
}
which informs me if any changes in device connection to the PC has happened. Also the following code tells me about existence of the arduino in the list of connected devices:
ManagementScope connectionScope = new ManagementScope();
SelectQuery serialQuery = new SelectQuery("SELECT * FROM Win32_SerialPort");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(connectionScope, serialQuery);
try
{
foreach (ManagementObject item in searcher.Get())
{
string desc = item["Description"].ToString();
string deviceId = item["DeviceID"].ToString();
if (!desc.Contains("Arduino"))
{
// Inform the user about the disconnection of the arduino
}
}
}
catch //(ManagementException e)
{
/* Do Nothing */
}
How ever, when I mix the codes to inform me when arduino is unplugged, they do not comply with each other and throw the error of:
Transition into COM context 0x8ff990 for this RuntimeCallableWrapper failed with the following error: An outgoing call cannot be made since the application is dispatching an input-synchronous call. (Exception from HRESULT: 0x8001010D (RPC_E_CANTCALLOUT_ININPUTSYNCCALL)). This is typically because the COM context 0x8ff990 where this RuntimeCallableWrapper was created has been disconnected or it is busy doing something else and cannot process the context transition. No proxy will be used to service the request on the COM component and calls will be made to the COM component directly. This may cause corruption or data loss. To avoid this problem, please ensure that all COM contexts/apartments/threads stay alive and are available for context transition, until the application is completely done with the RuntimeCallableWrappers that represents COM components that live inside them.
Any Help is appreciated, Thanks in advance.
PS: For your further information, the whole coded is:
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WM_DEVICECHANGE:
switch ((int)m.WParam)
{
case DBT_DEVICEARRIVAL:
listBox1.Items.Add("New Device Arrived");
int devType = Marshal.ReadInt32(m.LParam, 4);
if (devType == DBT_DEVTYP_PORT)
{
string portName = Marshal.PtrToStringAuto((IntPtr)((long)m.LParam + 12));
listBox1.Items.Add("the name of the port is " + portName);
}
break;
case DBT_DEVICEREMOVECOMPLETE:
{
listBox1.Items.Add("Device Removed");
ManagementScope connectionScope = new ManagementScope();
SelectQuery serialQuery = new SelectQuery("SELECT * FROM Win32_SerialPort");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(connectionScope, serialQuery);
try
{
foreach (ManagementObject item in searcher.Get())
{
string desc = item["Description"].ToString();
string deviceId = item["DeviceID"].ToString();
if (desc.Contains("Arduino"))
{
MessageBox.Show(deviceId);
}
}
}
catch //(ManagementException e)
{
/* Do Nothing */
}
}
break;
}
break;
}
}
public partial class Form1 : Form
{
ManagementEventWatcher watcher;
// Not all arduinos have 'Arduino' in their serialport driver 'Description', So check for a Com port Number.
private static string MustHavePort = "COM3"; //Port to check for.
public Form1()
{
InitializeComponent();
watcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2 or EventType = 3");
watcher.EventArrived += new EventArrivedEventHandler(USBChangedEvent);
watcher.Start();
}
public void USBChangedEvent(object sender, EventArrivedEventArgs e)
{
(sender as ManagementEventWatcher).Stop();
this.Invoke((MethodInvoker)delegate
{
ManagementObjectSearcher deviceList = new ManagementObjectSearcher("Select Name, Description, DeviceID from Win32_SerialPort");
// List to store available USB serial devices plugged in. (Arduino(s)/Cell phone(s)).
List<String> ComPortList = new List <String>();
listBox1.Items.Clear();
textBox1.Text = "";
// Any results? There should be!
if (deviceList != null)
{
// Enumerate the devices
foreach (ManagementObject device in deviceList.Get())
{
string SerialPortNumber = device["DeviceID"].ToString();
string serialName = device["Name"].ToString();
string SerialDescription = device["Description"].ToString();
ComPortList.Add(SerialPortNumber);
listBox1.Items.Add(SerialPortNumber);
listBox1.Items.Add(serialName);
listBox1.Items.Add(SerialDescription);
}
}
else
{
listBox1.Items.Add("NO SerialPorts AVAILABLE!");
// Inform the user about the disconnection of the arduino ON PORT 3 etc...
SendAlarm();
}
if (!ComPortList.Contains(MustHavePort))
{
// Inform the user about the disconnection of the arduino ON PORT 3 etc...
SendAlarm();
}
});
(sender as ManagementEventWatcher).Start();
}
private void SendAlarm()
{
textBox1.Text = "Alarm..." + MustHavePort + "Port Missing...";
MessageBox.Show("Error " + MustHavePort + " Port Missing!!!", "Serial Port Error.");
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
watcher.Stop();
}
}

Getting the Drive letter of USB drive inserted

I have detected the insertion of USB drive in my WPF application by following suggestions in given link How do I detect when a removable disk is inserted using C#?
but I am unable to figure out the Drive letter of the detected USB drive; my Code is given below
static ManagementEventWatcher w = null;
static void AddInsertUSBHandler()
{
WqlEventQuery q;
ManagementScope scope = new ManagementScope("root\\CIMV2");
scope.Options.EnablePrivileges = true;
try {
q = new WqlEventQuery();
q.EventClassName = "__InstanceCreationEvent";
q.WithinInterval = new TimeSpan(0, 0, 3);
q.Condition = "TargetInstance ISA 'Win32_USBControllerdevice'";
w = new ManagementEventWatcher(scope, q);
w.EventArrived += USBInserted;
w.Start();
}
catch (Exception e) {
Console.WriteLine(e.Message);
if (w != null)
{
w.Stop();
}
}
}
static void USBInserted(object sender, EventArgs e)
{
Console.WriteLine("A USB device inserted");
}
Kindly guide me if possible.
Hope this helps, its code based on Neeraj Dubbey’s answer.
We run code in a continuous multithreaded loop that keeps checking for a change in connected USB devices. Because we keep track of previously connected devices we can compare it against a new list of devices. We can also determine if a device is removed using the same method.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Usb_Test
{
class Program
{
private static List<USBDeviceInfo> previousDecices = new List<USBDeviceInfo>();
static void Main(string[] args)
{
Thread thread = new Thread(DeviceDetection);
thread.Start();
Console.Read();
}
static void DeviceDetection()
{
while (true)
{
List<USBDeviceInfo> devices = new List<USBDeviceInfo>();
ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(#"Select * From Win32_USBHub"))
collection = searcher.Get();
foreach (var device in collection)
{
devices.Add(new USBDeviceInfo(
(string)device.GetPropertyValue("DeviceID")
));
}
if (previousDecices == null || !previousDecices.Any()) // So we don't detect already plugged in devices the first time.
previousDecices = devices;
var insertionDevices = devices.Where(d => !previousDecices.Any(d2 => d2.DeviceID == d.DeviceID));
if (insertionDevices != null && insertionDevices.Any())
{
foreach(var value in insertionDevices)
{
Console.WriteLine("Inserted: " + value.DeviceID); // Add your own event for the insertion of devices.
}
}
var removedDevices = previousDecices.Where(d => !devices.Any(d2 => d2.DeviceID == d.DeviceID));
if (removedDevices != null && removedDevices.Any())
{
foreach (var value in removedDevices)
{
Console.WriteLine("Removed: " + value.DeviceID); // Add your own event for the removal of devices.
}
}
previousDecices = devices;
collection.Dispose();
}
}
}
class USBDeviceInfo
{
public USBDeviceInfo(string deviceID)
{
this.DeviceID = deviceID;
}
public string DeviceID { get; private set; }
}
}
However this is not a great solution, you're a lot better off listening for WM_DEVICECHANGE.
Windows will send WM_DEVICECHANGE message to all applications whenever
some hardware change occurs, including when a flash drive (or other
removable device) is inserted or removed. The WParam parameter of this
message contains code which specifies exactly what event occurred. For
our purpose only the following events are interesting:
DBT_DEVICEARRIVAL - sent after a device or piece of media has been
inserted. Your program will receive this message when the device is
ready for use, at about the time when Explorer displays the dialog
which lets you choose what to do with the inserted media.
DBT_DEVICEQUERYREMOVE - sent when the system requests permission to
remove a device or piece of media. Any application can deny this
request and cancel the removal. This is the important event if you
need to perform some action on the flash drive before it is removed,
e.g. encrypt some files on it. Your program can deny this request
which will cause Windows to display the well-known message saying that
the device cannot be removed now.
DBT_DEVICEREMOVECOMPLETE - sent after a device has been removed. When
your program receives this event, the device is no longer available —
at the time when Windows display its "device has been removed" bubble
to the user.
Here are some links that each detail an answer on how to do this in C#:
http://www.codeproject.com/Articles/18062/Detecting-USB-Drive-Removal-in-a-C-Program
http://www.codeproject.com/Articles/60579/A-USB-Library-to-Detect-USB-Devices
Both solution should work, and you can always adapt them to fit your needs.
Hey try this console based code afetr adding the reference of System.Management in project
namespace ConsoleApplication1
{
using System;
using System.Collections.Generic;
using System.Management; // need to add System.Management to your project references.
class Program
{
static void Main(string[] args)
{
var usbDevices = GetUSBDevices();
foreach (var usbDevice in usbDevices)
{
Console.WriteLine("Device ID: {0}", usbDevice.DeviceID);
}
Console.Read();
}
static List<USBDeviceInfo> GetUSBDevices()
{
List<USBDeviceInfo> devices = new List<USBDeviceInfo>();
ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(#"Select * From Win32_USBHub"))
collection = searcher.Get();
foreach (var device in collection)
{
devices.Add(new USBDeviceInfo(
(string)device.GetPropertyValue("DeviceID")
));
}
collection.Dispose();
return devices;
}
}
class USBDeviceInfo
{
public USBDeviceInfo(string deviceID)
{
this.DeviceID = deviceID;
}
public string DeviceID { get; private set; }
}

Creating a queue to handle jobs triggered by FileSystemWatcher

I have built a small tray app that will watch a folder and when a new file is added it runs a job. The job is to watch for video files and convert them to .mp4 using handBrakeCli. I have all this logic worked out. The problem I run into is that if there is more than one file I want it to queue the job til the prior one is complete. I am fairly new to c# and I am not sure of the best way to handle this.
one idea is to create a queue somehow, a file to store the commands in order maybe, then execute the next one after the process is complete. We are dealing with large movie files here so it can take a while. I am doing this on a quad core with 8gb of RAM and it seems to generally take about 30mins to complete a full length movie. I just dont know how to do this.
here is the code I have so far. there are some bits in here that are for future functionality so it refers to some classes that you wont see but it doesnt matter as they aren't used here. any suggestions are welcome.
public void Watcher()
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = textBox1.Text + "\\"; //path to watch
watcher.Filter = strfilter; //what types to look for set to * and i will filter later as it cant accept an array
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName; //properties to look at
watcher.IncludeSubdirectories = true; //scan subdirs
watcher.Created += new FileSystemEventHandler(OnChanged);
//TODO: make this only run if the files are of a certain type
watcher.EnableRaisingEvents = true; // start the watcher
}
static bool IsFileLocked(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
// Define the event handlers.
private void OnChanged(object source, FileSystemEventArgs e)
{
string sFile = e.FullPath;
//check that file is available
FileInfo fileInfo = new FileInfo(sFile);
while (IsFileLocked(fileInfo))
{
Thread.Sleep(500);
}
if (System.Diagnostics.Process.GetProcessesByName("HandBrakeCLI").Length != 0)
{
Thread.Sleep(500);
}
else
{
//hbOptions hbCl = new hbOptions();
//hbCli = hbCl.HbCliOptions();
if (textBox3.Text != "")
{
hbCli = textBox3.Text.ToString();
}
else
{
hbCli = "-e x264 -q 20 -B 160";
}
string t = e.Name;
string s = t.Substring(0, t.Length - 4); //TODO: fix this its not reliable
file = e.FullPath;
string opath = textBox1.Text.ToString();
cmd = "-i \"" + file + "\" -o \"" + opath + "\\" + s + ".mp4\" " + hbCli;
try
{
for (int i = 0; i < Ext.Count(); i++)
{
if (e.Name.Contains(Ext[i]))
{
Process hb = new Process();
hb.StartInfo.FileName = "D:\\Apps\\Handbrake\\Install\\Handbrake\\HandBrakeCLI.exe";
hb.StartInfo.Arguments = cmd;
notifyIcon.BalloonTipTitle = "Now Converting";
notifyIcon.BalloonTipText = file;
notifyIcon.ShowBalloonTip(2000);
hb.Start();
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
private void button1_Click(object sender, EventArgs e) //ok button
{
//add each array item to the list
for (int i = 0; i < filter.Count(); i++)
{
Ext.Add(filter[i]);
}
if (textBox1.Text != "" && textBox1.Text.Length > 2)
{
Watcher(); //call watcher to run
}
this.WindowState = FormWindowState.Minimized;
}
}
You way want to utilize WCF and MsmqQueueBinding:
Service uses NetMsmqBinding
Queue implemented for you using built-in into Windows queue called MSMQ (you can use MMC snap-it to control main, dead and poisoned letters queue. Both client and server Windows OS are bundled with it, turn it on in Windows Features)
Client puts process request into the queue and forgets about it
Service receives it automatically and process
Queue is durable, persisted and transactional (if you want)
You can run a queue on the same machine or on another intranet server
See the follow wonderful tutorial:
MSMQ, WCF and IIS: Getting them to play nice (Part 1)
MSMQ, WCF and IIS: Getting them to play nice (Part 2)
MSMQ, WCF and IIS: Getting them to play nice (Part 3)

Counting printed Pages by Clients with c# and WMI

the goal is like i said in the Topic. I know there are a lot of articles on that specific Problem and i tried all most all of them.
But since non of them worked out for me, I'm now trying to find out why this one just works sometimes and sometimes nothing is happening although many things are printed.
So this is my code which right now should wait for a job to be printed and just tell me about it. Nothing more.
private void StartMonitor()
{
try
{
var opt = new ConnectionOptions { EnablePrivileges = true };
var scope = new ManagementScope("root\\CIMV2", opt);
scope.Connect();
var query = new WqlEventQuery("SELECT * FROM __InstanceOperationEvent WITHIN 60 WHERE TargetInstance ISA \"Win32_PrintJob\"");
var watcher = new ManagementEventWatcher(query);
Console.WriteLine("Ready to receive Printer Job events...");
var pjEvent = watcher.WaitForNextEvent();
if (pjEvent != null) Console.WriteLine("Event occured: " + pjEvent.Properties["PagesPrinted"]);
}
catch (ManagementException e)
{
Console.WriteLine(e.StackTrace);
Console.WriteLine(e.ErrorCode);
Console.WriteLine(e.ErrorInformation);
_Error = e.Message;
throw;
}
}
To get the progress of the printed pages for a particular job, try using a smaller polling interval and use the EventArrivedEventHandler delegate attached to the WMI event for handling the incoming data in a asynchronous way.
Try this sample.
using System;
using System.Collections.Generic;
using System.Management;
using System.Text;
namespace GetWMI_Info
{
public class EventWatcherAsync
{
private void WmiEventHandler(object sender, EventArrivedEventArgs e)
{
Console.WriteLine("TargetInstance.Caption : " + ((ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value)["Caption"]);
Console.WriteLine("TargetInstance.JobStatus : " + ((ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value)["JobStatus"]);
Console.WriteLine("TargetInstance.PagesPrinted : " + ((ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value)["PagesPrinted"]);
Console.WriteLine("TargetInstance.Status : " + ((ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value)["Status"]);
}
public EventWatcherAsync()
{
try
{
string ComputerName = "localhost";
string WmiQuery;
ManagementEventWatcher Watcher;
ManagementScope Scope;
if (!ComputerName.Equals("localhost", StringComparison.OrdinalIgnoreCase))
{
ConnectionOptions Conn = new ConnectionOptions();
Conn.Username = "";
Conn.Password = "";
Conn.Authority = "ntlmdomain:DOMAIN";
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), Conn);
}
else
Scope = new ManagementScope(String.Format("\\\\{0}\\root\\CIMV2", ComputerName), null);
Scope.Connect();
WmiQuery ="Select * From __InstanceOperationEvent Within 1 "+
"Where TargetInstance ISA 'Win32_PrintJob' ";
Watcher = new ManagementEventWatcher(Scope, new EventQuery(WmiQuery));
Watcher.EventArrived += new EventArrivedEventHandler(this.WmiEventHandler);
Watcher.Start();
Console.Read();
Watcher.Stop();
}
catch (Exception e)
{
Console.WriteLine("Exception {0} Trace {1}", e.Message, e.StackTrace);
}
}
public static void Main(string[] args)
{
Console.WriteLine("Listening {0}", "__InstanceOperationEvent");
Console.WriteLine("Press Enter to exit");
EventWatcherAsync eventWatcher = new EventWatcherAsync();
Console.Read();
}
}
}
To get the total pages printed by a job I would try monitoring __InstanceDeletionEvents, I believe that at that time Win32_PrintJob.TotalPages should show the printed pages accurately (can't test this right now, sorry). There is also a nice alternative here:
Monitoring a Printer Queue from VB.NET
If you look at the article comments, the author also advises to monitor JOB_WRITTEN events to get the total number of pages printed.
If you are trying to monitor the progress of large print jobs, try monitoring __InstanceModificationEvents once the job is created, something like this:
Select * From __InstanceModificationEvent Within 1
Where TargetInstance Isa "Win32_PrintJob"
And TargetInstance.PagesPrinted > PreviousInstance.PagesPrinted

Categories