Getting the "path" of connected/disconnected USB HID devices in C# - c#

As a flightsim enthusiast, I'm making my own cockpit hardware. I interface this hardware with my computer(s) using the USB HID interface (HW based on PIC microcontrollers). All hardware has the same VID/PID, and the Product Descriptor string is "FMGS HW Device" (fyi: FMGS is the name of the Airbus A320 simulator software I'm using).
On startup, my application scans all the USB devices and only puts the ones with my "VID/PID/Product Descriptor" combination in a Dictionary. As key, I'm using the "DevicePath" that I retrieve via the PSP_DEVICE_INTERFACE_DETAIL_DATA structure with a call to SetupDiGetDeviceInterfaceDetail (setupapi.dll).
Below is a small code extract that shows the core of this operation - just so you get the idea:
// Retrieving the detailDataBuffer
SetupApi.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, IntPtr.Zero);
// Skip over cbsize (4 bytes) to get the address of the devicePathName
var pDevicePath = new IntPtr(detailDataBuffer.ToInt32() + 4);
// Get the String containing the devicePath
AddDevice(Marshal.PtrToStringAuto(pDevicePath));
This DevicePath has the following format:
\?\hid#vid_04d8&pid_003f#9&599cfdc&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}
Question 1:
I can clearly see the VID/PID part (vid_04d8&pid_003f) and the HidGuid (4d1e55b2-f16f-11cf-88cb-001111000030), but what is the part with "9&599cfdc&0&0000"? Is that unique for every device? With other words, isn't there a risk that 2 of my devices have exactly the same DevicePath?
Now I also want to detect if devices are connected/disconnected while the application is running. If device are disconnected, I need to remove them from my Dictionary. If devices are connected, I need to put them in my Dictionary and start communicating with them.
I'm using the "WMI method" (I know there is also a WM_DEVICECHANGE method in WndProc, not sure what is better?). Below is the code (not finished, but works so far).
private void DeviceInsertedEvent(object sender, EventArrivedEventArgs e)
{
Debug.WriteLine($"DeviceInsertEvent");
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
foreach(PropertyData p in instance.Properties )
{
Debug.WriteLine($"{p.Name} = {p.Value }");
}
Debug.WriteLine($"{instance.Properties["Dependent"].Value }");
}
private void DeviceRemovedEvent(object sender, EventArrivedEventArgs e)
{
Debug.WriteLine($"DeviceRemovedEvent");
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
foreach (PropertyData p in instance.Properties)
{
Debug.WriteLine($"{p.Name} = {p.Value }");
}
Debug.WriteLine($"{instance.Properties["Dependent"].Value}");
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var scope = new ManagementScope("root\\CIMV2");
scope.Options.EnablePrivileges = true;
try
{
WqlEventQuery insertQuery = new WqlEventQuery();
insertQuery.EventClassName = "__InstanceCreationEvent";
insertQuery.WithinInterval = new TimeSpan(0, 0, 1);
insertQuery.Condition = #"TargetInstance ISA 'Win32_USBControllerdevice'";
ManagementEventWatcher insertWatcher = new ManagementEventWatcher(insertQuery);
insertWatcher.EventArrived += new EventArrivedEventHandler(DeviceInsertedEvent);
insertWatcher.Start();
WqlEventQuery removeQuery = new WqlEventQuery();
removeQuery.EventClassName = "__InstanceDeletionEvent";
removeQuery.WithinInterval = new TimeSpan(0, 0, 1);
removeQuery.Condition = #"TargetInstance ISA 'Win32_USBControllerdevice'";
ManagementEventWatcher removeWatcher = new ManagementEventWatcher(removeQuery);
removeWatcher.EventArrived += new EventArrivedEventHandler(DeviceRemovedEvent);
removeWatcher.Start();
}
catch (Exception ex)
{
Debug.WriteLine($"backgroundWorker_DoWork Exception: {ex}");
}
while (true) ;
}
I am not a WMI expert, so I have honestly no clue what I'm doing (still learning) - just got the code from several google searches. I discovered that "instance.Properties["Dependent"].Value" gives me also some kind of DevicePath, with the below format.
\DESKTOP-HANS\root\cimv2:Win32_PnPEntity.DeviceID="HID\VID_04D8&PID_003F\9&2E7AE93E&0&0000"
I see the same VID/PID combination, and the unknown "9&2E7AE93E&0&0000" part that I asked for in Question 1. So basically, with some string-manipulations, I could reconstruct the same DevicePath that I'm using as the key in my Dictionary.
Question 2:
Is there another way to discover device connect/disconnect events that give me the same DevicePath as returned by SetupDiGetDeviceInterfaceDetail?

I have been continuing with the above concept, and it works as a charm. I changed the code in the below, and now my USB devices are nicely added or removed dynamically when my application is running. The "puzzling part" is in constructing the correct DevicePath format in the DeviceCreatedEvent and DeviceRemovedEvent.
_stringHidGuid is obtained in the constructer of my UsbServer. I need it a few times, so I created a private member for it. It is a string with the format (already added the '#' and curly brackets):
#{4d1e55b2-f16f-11cf-88cb-001111000030}
I use this to "construct" the DevicePath based on the "Dependent" value I'm getting from the WMI __InstanceCreationEvent and __InstanceDeletionEvent.
_DeviceArrivedWatcher and _DeviceRemovedWatcher are another 2 private members of type ManagementEventWatcher to keep the notifiers when initializing the code. _vid and _pid contain my VID and PID.
Below is the full code extract.
private void DeviceCreatedEvent(object sender, EventArrivedEventArgs e)
{
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
// Describes the logical device connected to the Universal Serial Bus (USB) controller.
string path = (string)instance.Properties["Dependent"].Value;
// Check if it is our device
int start = path.IndexOf($"HID\\\\VID_{_vid:X4}&PID_{_pid:X4}");
if (start != -1)
{
// Create the DevicePath to be used in UsbServer
path = path.Substring(start, path.Length - start - 1).Replace("\\\\", "#");
path = string.Concat("\\\\?\\", path, _stringHidGuid);
Debug.WriteLine($"DeviceCreatedEvent for {path}");
AddDevice(path);
}
}
private void DeviceRemovedEvent(object sender, EventArrivedEventArgs e)
{
ManagementBaseObject instance = (ManagementBaseObject)e.NewEvent["TargetInstance"];
// Describes the logical device connected to the Universal Serial Bus (USB) controller.
string path = (string)instance.Properties["Dependent"].Value;
// Check if it is our device
int start = path.IndexOf($"HID\\\\VID_{_vid:X4}&PID_{_pid:X4}");
if (start != -1)
{
// Create the DevicePath to be used in UsbServer
path = path.Substring(start, path.Length - start - 1).Replace("\\\\", "#");
path = string.Concat("\\\\?\\", path, _stringHidGuid);
Debug.WriteLine($"DeviceRemovedEvent for {path}");
RemoveDevice(path);
}
}
private void AddDeviceArrivedHandler()
{
try
{
var scope = new ManagementScope("root\\CIMV2");
scope.Options.EnablePrivileges = true;
var q = new WqlEventQuery();
q.EventClassName = "__InstanceCreationEvent";
q.WithinInterval = new TimeSpan(0, 0, 1);
q.Condition = #"TargetInstance ISA 'Win32_USBControllerdevice'";
_deviceArrivedWatcher = new ManagementEventWatcher(scope, q);
_deviceArrivedWatcher.EventArrived += DeviceCreatedEvent;
_deviceArrivedWatcher.Start();
}
catch (Exception ex)
{
Debug.WriteLine($"AddDeviceArrivedHandler() Exception: {ex}");
if (_deviceArrivedWatcher != null)
_deviceArrivedWatcher.Stop();
}
}
private void AddDeviceRemovedHandler()
{
try
{
var scope = new ManagementScope("root\\CIMV2");
scope.Options.EnablePrivileges = true;
var q = new WqlEventQuery();
q.EventClassName = "__InstanceDeletionEvent";
q.WithinInterval = new TimeSpan(0, 0, 1);
q.Condition = #"TargetInstance ISA 'Win32_USBControllerdevice'";
_deviceRemovedWatcher = new ManagementEventWatcher(scope, q);
_deviceRemovedWatcher.EventArrived += DeviceRemovedEvent;
_deviceRemovedWatcher.Start();
}
catch (Exception ex)
{
Debug.WriteLine($"AddDeviceRemovedHandler() Exception: {ex}");
if (_deviceRemovedWatcher != null)
_deviceRemovedWatcher.Stop();
}
}
private void DeviceNotificationsStop()
{
try
{
if (_deviceArrivedWatcher != null)
_deviceArrivedWatcher.Stop();
if (_deviceRemovedWatcher != null)
_deviceRemovedWatcher.Stop();
}
catch (Exception ex)
{
Debug.WriteLine($"DeviceNotificationStop() Exception: {ex}");
throw;
}
}

For a best source of knowledge about this topic I can recommend USB Complete
The Developer's Guide book, USBView and HClient official MS samples on GitHub.
\?\hid#vid_04d8&pid_003f#9&599cfdc&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} is a so called device interface path. Its format is not documented by MS (so could be changes at any time) and its not safe to parse it. Every device could provide several interfaces to interact with it. For example every USB HID device also have interface with GUID_DEVINTERFACE_USB_DEVICE - {A5DCBF10-6530-11D2-901F-00C04FB951ED}.
There are two main Win32 APIs exist to query for connected devices: SetupDi* (defined in SetupAPI.h) and CM_* (defined in cfgmgr32.h). You can also use WMI but I not familiar with it.
Regarding how to convert device interface path to VID/PID:
You can use CM_Get_Device_Interface_PropertyW (or SetupDiGetDevicePropertyW) with DEVPKEY_Device_InstanceId param to request "device instance identifier". After that you can open that device via CM_Locate_DevNodeW (or SetupDiGetClassDevsW) with device instance id that you got and query DEVPKEY_Device_HardwareIds (string list) property on it. And returned stuff you can parse because its documented:
Each interface has a device ID of the following form:
USB\VID_v(4)&PID_d(4)&MI_z(2)
Where:
v(4) is the 4-digit vendor code that the USB committee assigns to the vendor.
d(4) is the 4-digit product code that the vendor assigns to the device.
z(2) is the interface number that is extracted from the bInterfaceNumber field of the interface descriptor.
PS: Found WMI example code with VID/PID filtering for in a book I mentioned:
void FindDevice()
{
const String deviceIdString = #”USB\VID_0925&PID_150C”;
_deviceReady = false;
var searcher = new ManagementObjectSearcher(“root\\CIMV2”, “SELECT PNPDeviceID FROM Win32_PnPEntity”);
foreach (ManagementObject queryObj in searcher.Get()) {
if (queryObj[“PNPDeviceID”].ToString().Contains(deviceIdString)) {
_deviceReady = true;
}
}
}

Related

Connecting to the MQ Server using CCDT

I'm trying to connect to the MQ using the information present in the CCDT file. I can currently connect to the MQ using all the details, and get and put messages from and to the queue.
After extensive googling, I've been unable to find any sample code which allows me to connect using the CCDT file.
One of my colleagues forwarded me his JMS connection code, but I've been unable to port it to C#.
The JAVA code is as follows -
public class MQTest {
public static void main(String[] args) {
MQQueueManager queueManager = null;
URL ccdtFileUrl = null;
MQMessage mqMessage = null;
//MQPutMessageOptions myPMO = null
try {
String QM = "IB9QMGR";
String QUEUE1 = "TEST";
System.out.println("Starting MQClient Put Program: ");
ccdtFileUrl = new URL("file:///D:/AMQCLCHL.TAB") ;
ccdtFileUrl.openConnection();
queueManager = new MQQueueManager("SDCQMGR.T1", ccdtFileUrl);
System.out.println("Connected to QMGR ");
int openOptions = MQC.MQOO_OUTPUT;
MQQueue InQueue = queueManager.accessQueue(QUEUE1,openOptions,null,null,null);
MQMessage inMessage = new MQMessage();
inMessage.writeString("###Testing####");
InQueue.put(inMessage);
System.out.println("Message Id is :" + inMessage.messageId);
System.out.println(inMessage.toString());
InQueue.close();
queueManager.disconnect() ;
}
catch(MQException ex){
System.out.println("MQ Error - Reason code :" + ex.reasonCode);
}
catch (Exception e){
System.out.println("Error : " + e);
}
}
}
Instead of URL, I used the URI (in C#) to set file location. (This may be wrongly used. Not sure what else to use though.)
Uri ccdtFileUrl = new Uri("file:///D:/AMQCLCHL.TAB") ;
but I can't use openConnection() on a URI. Also,
queueManager = new MQQueueManager("SDCQMGR.T1",ccdtFileUrl);
gives an argument overload exception. As URI is not supported in C#.
I've tried looking up samples but I've found some JMS samples and thats it. Looking for some sample code to connect in C#.
You will need to set MQCHLLIB and MQCHLTAB environment variables to use CCDT. You can set these two variables either from command prompt,app.config or code in the application itself.
Following example demonstrates usage of CCDT:
MQQueueManager qm = null;
System.Environment.SetEnvironmentVariable("MQCHLLIB", "C:\\ProgramData\\IBM\\MQ\\qmgrs\\QM1\\#ipcc");
System.Environment.SetEnvironmentVariable("MQCHLTAB", "AMQCLCHL.TAB");
try
{
**Hashtable props = new Hashtable();
props.Add(MQC.TRANSPORT_PROPERTY, MQC.TRANSPORT_MQSERIES_CLIENT);
qm = new MQQueueManager("QM1",props);**
MQQueue queue1 = qm.AccessQueue("SYSTEM.DEFAULT.LOCAL.QUEUE", MQC.MQOO_OUTPUT | MQC.MQOO_FAIL_IF_QUIESCING);
MQMessage msg = new MQMessage();
msg.WriteUTF("Hello this message is from .net client");
queue1.Put(msg);
queue1.Close();
qm.Disconnect();
}
catch (Exception ex)
{
Console.Write(ex);
}

How to check the length of a MSMQ [duplicate]

I was wondering if there is a way to programmatically check how many messages are in a private or public MSMQ using C#? I have code that checks if a queue is empty or not using the peek method wrapped in a try/catch, but I've never seen anything about showing the number of messages in the queue. This would be very helpful for monitoring if a queue is getting backed up.
You can read the Performance Counter value for the queue directly from .NET:
using System.Diagnostics;
// ...
var queueCounter = new PerformanceCounter(
"MSMQ Queue",
"Messages in Queue",
#"machinename\private$\testqueue2");
Console.WriteLine( "Queue contains {0} messages",
queueCounter.NextValue().ToString());
There is no API available, but you can use GetMessageEnumerator2 which is fast enough. Sample:
MessageQueue q = new MessageQueue(...);
int count = q.Count();
Implementation
public static class MsmqEx
{
public static int Count(this MessageQueue queue)
{
int count = 0;
var enumerator = queue.GetMessageEnumerator2();
while (enumerator.MoveNext())
count++;
return count;
}
}
I also tried other options, but each has some downsides
Performance counter may throw exception "Instance '...' does not exist in the specified Category."
Reading all messages and then taking count is really slow, it also removes the messages from queue
There seems to be a problem with Peek method which throws an exception
If you need a fast method (25k calls/second on my box), I recommend Ayende's version based on MQMgmtGetInfo() and PROPID_MGMT_QUEUE_MESSAGE_COUNT:
for C#
https://github.com/hibernating-rhinos/rhino-esb/blob/master/Rhino.ServiceBus/Msmq/MsmqExtensions.cs
for VB
https://gist.github.com/Lercher/5e1af6a2ba193b38be29
The origin was probably http://functionalflow.co.uk/blog/2008/08/27/counting-the-number-of-messages-in-a-message-queue-in/ but I'm not convinced that this implementation from 2008 works any more.
We use the MSMQ Interop. Depending on your needs you can probably simplify this:
public int? CountQueue(MessageQueue queue, bool isPrivate)
{
int? Result = null;
try
{
//MSMQ.MSMQManagement mgmt = new MSMQ.MSMQManagement();
var mgmt = new MSMQ.MSMQManagementClass();
try
{
String host = queue.MachineName;
Object hostObject = (Object)host;
String pathName = (isPrivate) ? queue.FormatName : null;
Object pathNameObject = (Object)pathName;
String formatName = (isPrivate) ? null : queue.Path;
Object formatNameObject = (Object)formatName;
mgmt.Init(ref hostObject, ref formatNameObject, ref pathNameObject);
Result = mgmt.MessageCount;
}
finally
{
mgmt = null;
}
}
catch (Exception exc)
{
if (!exc.Message.Equals("Exception from HRESULT: 0xC00E0004", StringComparison.InvariantCultureIgnoreCase))
{
if (log.IsErrorEnabled) { log.Error("Error in CountQueue(). Queue was [" + queue.MachineName + "\\" + queue.QueueName + "]", exc); }
}
Result = null;
}
return Result;
}
//here queue is msmq queue which you have to find count.
int index = 0;
MSMQManagement msmq = new MSMQManagement() ;
object machine = queue.MachineName;
object path = null;
object formate=queue.FormatName;
msmq.Init(ref machine, ref path,ref formate);
long count = msmq.MessageCount();
This is faster than you selected one.
You get MSMQManagement class refferance inside "C:\Program Files (x86)\Microsoft SDKs\Windows" just brows in this address you will get it. for more details you can visit http://msdn.microsoft.com/en-us/library/ms711378%28VS.85%29.aspx.
I had real trouble getting the accepted answer working because of the xxx does not exist in the specified Category error. None of the solutions above worked for me.
However, simply specifying the machine name as below seems to fix it.
private long GetQueueCount()
{
try
{
var queueCounter = new PerformanceCounter("MSMQ Queue", "Messages in Queue", #"machineName\private$\stream")
{
MachineName = "machineName"
};
return (long)queueCounter.NextValue();
}
catch (Exception e)
{
return 0;
}
}
The fastest method I have found to retrieve a message queue count is to use the peek method from the following site:
protected Message PeekWithoutTimeout(MessageQueue q, Cursor cursor, PeekAction action)
{
Message ret = null;
try
{
ret = q.Peek(new TimeSpan(1), cursor, action);
}
catch (MessageQueueException mqe)
{
if (!mqe.Message.ToLower().Contains("timeout"))
{
throw;
}
}
return ret;
}
protected int GetMessageCount(MessageQueue q)
{
int count = 0;
Cursor cursor = q.CreateCursor();
Message m = PeekWithoutTimeout(q, cursor, PeekAction.Current);
{
count = 1;
while ((m = PeekWithoutTimeout(q, cursor, PeekAction.Next)) != null)
{
count++;
}
}
return count;
}
This worked for me. Using a Enumarator to make sure the queue is empty first.
Dim qMsg As Message ' instance of the message to be picked
Dim privateQ As New MessageQueue(svrName & "\Private$\" & svrQName) 'variable svrnme = server name ; svrQName = Server Queue Name
privateQ.Formatter = New XmlMessageFormatter(New Type() {GetType(String)}) 'Formating the message to be readable the body tyep
Dim t As MessageEnumerator 'declared a enumarater to enable to count the queue
t = privateQ.GetMessageEnumerator2() 'counts the queues
If t.MoveNext() = True Then 'check whether the queue is empty before reading message. otherwise it will wait forever
qMsg = privateQ.Receive
Return qMsg.Body.ToString
End If
If you want a Count of a private queue, you can do this using WMI.
This is the code for this:
// You can change this query to a more specific queue name or to get all queues
private const string WmiQuery = #"SELECT Name,MessagesinQueue FROM Win32_PerfRawdata_MSMQ_MSMQQueue WHERE Name LIKE 'private%myqueue'";
public int GetCount()
{
using (ManagementObjectSearcher wmiSearch = new ManagementObjectSearcher(WmiQuery))
{
ManagementObjectCollection wmiCollection = wmiSearch.Get();
foreach (ManagementBaseObject wmiObject in wmiCollection)
{
foreach (PropertyData wmiProperty in wmiObject.Properties)
{
if (wmiProperty.Name.Equals("MessagesinQueue", StringComparison.InvariantCultureIgnoreCase))
{
return int.Parse(wmiProperty.Value.ToString());
}
}
}
}
}
Thanks to the Microsoft.Windows.Compatibility package this also works in netcore/netstandard.
The message count in the queue can be found using the following code.
MessageQueue messageQueue = new MessageQueue(".\\private$\\TestQueue");
var noOFMessages = messageQueue.GetAllMessages().LongCount();

Duplicate entries on server response .net

Scenario
One windows service polls a url every two minutes to retrieve certain data.
If any data has been added since the previous call, the data is retrieved and stored otherwise the loop carries on.
Issue
Sometimes a request takes more than two minutes to return a response.
When this happens, the next request is still made and finds new data, since the previous request hasn't return a response yet
This results in duplicate entries when the data is stored.
What I've tried
I tried to handle that by using a boolean like so:
Boolean InProgress = true;
foreach (var item in Lists)
{
\\Make a request and return new data (if any)
InProgress = false;
if (InProgress = false)
{
\\Store new data
}
}
This doesn't solve the issue. I believe I'm using the boolean in wrong place, but I'm not sure where it should.
This is the loop that makes the request and store the data
void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
Data getCredentials = new Data();
DataTable credentials = getCredentials.loadCredentials();
Boolean InProgress = true;
for (int i = 0; i < credentials.Rows.Count; i++)
{
if (credentials != null)
{
var PBranchID = (int)credentials.Rows[i]["PortalBranchID"];
var negRef = (int)credentials.Rows[i]["NegotiatorRef"];
var Username = credentials.Rows[i]["Username"].ToString();
var Password = credentials.Rows[i]["Password"].ToString();
var Domain = credentials.Rows[i]["Domain"].ToString();
var FooCompanyBaseUrl = "https://" + Domain + ".FooCompany.com/";
Data getCalls = new Data();
DataTable calls = getCalls.loadCalls(PBranchID);
//If it's not the first call
if (calls != null && calls.Rows.Count > 0)
{
//Makes a call
DateTime CreatedSince = DateTime.SpecifyKind((DateTime)calls.Rows[0]["LastSuccessOn"], DateTimeKind.Local);
string IssueListUrl = FooCompany.WebApi.V2.URLs.Issues(BaseUrl, null, CreatedSince.ToUniversalTime(), null);
FooCompany.WebApi.V2.DTO.PrevNextPagedList resultIssueList;
resultIssueList = FooCompany.WebApi.Client.Helper.Utils.Getter<Foocompany.WebApi.V2.DTO.PrevNextPagedList>(IssueListUrl, Username, Password);
InProgress = false;
if (InProgress == false)
{
if (resultIssueList.Items.Count > 0)
{
//If call returns new issues, save call
Data saveCalls = new Data();
saveCalls.saveCalls(PBranchID);
foreach (var item in resultIssueList.Items)
{
var Issue = FooCompany.WebApi.Client.Helper.Utils.Getter<FooCompany.WebApi.V2.DTO.Issue>(item, Username, Password);
string TenantSurname = Issue.Surname;
string TenantEmail = Issue.EmailAddress;
Data tenants = new Data();
int tenantPropRef = Convert.ToInt32(tenants.loadTenantPropRef(PBranchID, TenantSurname, TenantEmail));
Data Properties = new Data();
DataTable propAddress = Properties.loadPropAddress(PBranchID, tenantPropRef);
var Address1 = propAddress.Rows[0]["Address1"];
var Address2 = propAddress.Rows[0]["Address2"];
var AddressFolder = Address1 + "," + Address2;
if (!Directory.Exists("path"))
{
Directory.CreateDirectory("path");
}
string ReportPDFDestination = "path";
if (File.Exists(ReportPDFDestination))
{
File.Delete(ReportPDFDestination);
}
FooCompany.WebApi.Client.Helper.Utils.DownloadFileAuthenticated(FooCompany.WebApi.V2.URLs.IssueReport(BaseUrl, Issue.Id), Username, Password, ReportPDFDestination);
//Store data
}
IssueListUrl = resultIssueList.NextURL;
}
}
}
else
{
continue;
}
}
}
catch (Exception ex)
{
//write to log
}
}
Question
I'm sure there is a better way than a boolean.
Could anyone advice a different method to handle the issue properly?
Thanks.
Solution
I ended up using a combination of both Thomas and Mason suggestions. I wrapped a lock statement around the main function of my windows service and used a boolean inside the function section that makes the call to the remote server.
Tested many times and it's error free.
You seems to have a problem of synchronisation, just surround the code that iterate though the List with a lock, and you will be fine.
public class MyClass{
private readonly object internalLock= new object();
private bool AlreadyRunning { get; set; }
void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if(AlreadyRunning){
return;
}
try{
lock(internalLock){
Thread.MemoryBarrier();
if(AlreadyRunning){
return;
}
AlreadyRunning = true;
...Do all the things...
}
}
catch(Exception e){
..Exception handling
}
finally
{
AlreadyRunning = false;
}
}
bool InProgress=false;
void serviceTimer_Elapsed(object sender, ElapsedEventArgs e)
{
if(!InProgress)
{
InProgress=true;
//retrieve data
InProgress=false;
}
}
Your InProgress variable needs to be declared outside the event handler. When you enter the method, check to see if it's already running. If it is, then we do nothing. If it's not running, then we say it's running, retrieve our data, then reset our flag to say we've finished running.
You'll probably need to add appropriate locks for thread safety, similar to Thomas's answer.

Specific message makes Push Sharp ApplePushService stop working

I am implementing push notifications using push sharp and I have 2 types of messages I am sending RecomendationLiked and NewFollower, I can send RecomendationLiked messages as much as I want and everything works fine but sending a single NewFollower message simpley causes the service to stop responding with no exception, or any of the events called. This happens both in Production and in Development environment
Here is the service creation logic:
private void InitApplePushService()
{
try
{
string appDataPath = HttpContext.Current.Server.MapPath("~/app_data");
//***** Development Server *****//
string file = Path.Combine(appDataPath, "PushSharp.PushCert.Development.p12");
var appleCert = File.ReadAllBytes(file);
_applePushService = new ApplePushService(new ApplePushChannelSettings(false, appleCert, "XXX"));
_applePushService.OnChannelCreated += OnChannelCreated;
_applePushService.OnChannelDestroyed += OnChannelDestroyed;
_applePushService.OnChannelException += OnChannelException;
_applePushService.OnDeviceSubscriptionChanged += OnDeciveSubscriptionChanged;
_applePushService.OnDeviceSubscriptionExpired += OnDeviceSubscriptionExpired;
_applePushService.OnNotificationFailed += OnNorificationFailed;
_applePushService.OnNotificationRequeue += OnNotificationQueued;
_applePushService.OnNotificationSent += OnNOtificationSend;
_applePushService.OnServiceException += OnServiceException;
Trace.TraceInformation("ApplePushService initialized succesfully");
}
catch (Exception e)
{
Trace.TraceError("Error initializing ApplePushService : " + e);
throw;
}
}
RecomendationLiked message creation:
private void SendRecomendationLikedMessageToAppleDevice(User likingUser, Recomendation recomendation)
{
var notification = new AppleNotification();
notification.DeviceToken = recomendation.User.PushNotificationID;
notification.Payload.Alert.LocalizedKey = "NewLikeNotification";
notification.Payload.Alert.LocalizedArgs = new List<object> { likingUser.NickName };
notification.Payload.Sound = "default";
notification.Payload.AddCustom("LikingUser", likingUser.NickName);
notification.Payload.AddCustom("AlertType", "RecomendationLiked");
notification.Payload.AddCustom("ID", likingUser.ID);
notification.Payload.AddCustom("ImageUrl", likingUser.ImageUrl);
_applePushService.QueueNotification(notification);
}
NewFollower message creation:
private void SendNewFollowingUserMessageToAppleDevice(User followingUser, User followedUser)
{
var notification = new AppleNotification();
notification.DeviceToken = followedUser.PushNotificationID;
notification.Payload.Alert.LocalizedKey = "NewFollowingUserNotification";
notification.Payload.Alert.LocalizedArgs = new List<object> { followingUser.NickName };
notification.Payload.Sound = "default";
notification.Payload.AddCustom("followingUser", followingUser.NickName);
notification.Payload.AddCustom("AlertType", "NewFollowingUser");
notification.Payload.AddCustom("ID", followingUser.ID);
notification.Payload.AddCustom("ImageUrl", followingUser.ImageUrl);
Trace.TraceInformation("Trying to send notifications: "+ notification);
_applePushService.QueueNotification(notification);
//_pushService.QueueNotification(notification);
}
The first one works, the second kills the push service silently...
Any ideas?
Solved it finally...
The issue is with the length of the json string that is generated. it seems that the maximum is 255 chars. anything higher and it fails silently...
beware.
Amit

Listing USB Devices with C# and WMI

I'm trying to implement a function in my application that lists all the plugged in USB Mass Storage Devices in a computer.
My code works well when launching the application but my problem is that I want the box in my form to refresh automatically when a USB device is removed or attached.
Implementing DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE conditions should work but I get back a "DisconnectedContext was detected" exception.
I learned that I need to use a delegate and set an asyncronous call for this to work correctly.
Here's my code:
public void listUSB()
{
ManagementScope sc = new ManagementScope(wmiUsbList);
ObjectQuery query = new ObjectQuery("select * from Win32_DiskDrive " + "where InterfaceType='USB'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(sc, query);
ManagementObjectCollection result = searcher.Get();
foreach (ManagementObject obj in result)
{
if (obj["DeviceID"] != null)
{
usbListTextBox.AppendText(obj["Model"].ToString());
}
}
}
I'd really like to know how to apply a delegate to my method.
I also looked at this thread on MSDN which provides an excellent example, but as of that example I am not able to understand how to put the deviceList in a textbox.
I'm still Learning so if someone could be so kind to point me to the right direction on one or both of my questions, that would be greatly appreciated.
Thanks!
Try to use ManagementEventWatcher and assign an event handler to the EventArrived.
I don't know how to accomplish exactly this, but here is a watcher that listens to the print events:
string printQuery = "Select * From __InstanceCreationEvent Within 1 Where TargetInstance ISA 'Win32_PrintJob'";
string nspace = #"\\.\root\CIMV2";
var watcher = new ManagementEventWatcher(nspace, printQuery);
Hope it helps.
private usbListArrayDelegate mDeleg;
protected override void WndProc(ref Message m)
{
int devType;
base.WndProc(ref m);
switch (m.WParam.ToInt32())
{
case DBT_DEVICEARRIVAL:
devType = Marshal.ReadInt32(m.LParam, 4);
if (devType == DBT_DEVTYP_VOLUME)
{
// usb device inserted, call the query
mDeleg = new usbListArrayDelegate(usbListArray);
AsyncCallback callback = new AsyncCallback(usbListArrayCallback);
// invoke the thread that will handle getting the friendly names
mDeleg.BeginInvoke(callback, new object());
}
break;
case DBT_DEVICEREMOVECOMPLETE:
devType = Marshal.ReadInt32(m.LParam, 4);
if (devType == DBT_DEVTYP_VOLUME)
{
mDeleg = new usbListArrayDelegate(usbListArray);
AsyncCallback callback = new AsyncCallback(usbListArrayCallback);
// invoke the thread that will handle getting the friendly names
mDeleg.BeginInvoke(callback, new object());
}
break;
}
}
public ArrayList usbListArray()
{
ArrayList deviceList = new ArrayList();
manager = new UsbManager(); ==> about how to implement this please see http://www.codeproject.com/Articles/63878/Enumerate-and-Auto-Detect-USB-Drives
UsbDiskCollection disks = manager.GetAvailableDisks();
foreach (UsbDisk disk in disks)
{
deviceList.Add(disk);
}
return deviceList;
}
// delegate wrapper function for the getFriendlyNameList function
private delegate ArrayList usbListArrayDelegate();
// callback method when the thread returns
private void usbListArrayCallback(IAsyncResult ar)
{
string ArrayData = string.Empty;
// got the returned arrayList, now we can do whatever with it
ArrayList result = mDeleg.EndInvoke(ar);
int count = 0;
foreach (UsbDisk disk in result)
{
++count;
ArrayData += count + ") " + disk.ToString().
}

Categories

Resources