I'm having to back port some software from Windows Mobile 6.5 to Windows CE 5.0, the software currently detects when the unit is in the base unit (ActiveSync running).
I need to know when ActiveSync is running on the unit so that I can prepare the unit to send and receive files.
I've found an article on using PINVOKE methods such as CeRunAppAtEvent but I am clueless on how that would work.
bool terminateDeviceEventThreads = false;
IntPtr handleActiveSyncEndEvent;
while (!terminateDeviceEventThreads)
{
handleActiveSyncEndEvent = NativeMethods.CreateEvent (IntPtr.Zero,
true, false, "EventActiveSync");
if (IntPtr.Zero != handleActiveSyncEndEvent)
{
if (NativeMethods.CeRunAppAtEvent ("\\\\.\\Notifications\\NamedEvents\\EventActiveSync",
(int) NOTIFICATION_EVENT.NOTIFICATION_EVENT_RS232_DETECTED))
{
NativeMethods.WaitForSingleObject (handleActiveSyncEndEvent, 0);
//
NativeMethods.ResetEvent (handleActiveSyncEndEvent);
if (!NativeMethods.CeRunAppAtEvent ("\\\\.\\Notifications\\NamedEvents\\EventActiveSync",
(int) NOTIFICATION_EVENT.NOTIFICATION_EVENT_NONE))
{
break;
}
handleActiveSyncEndEvent = IntPtr.Zero;
}
}
}
The code you have here is waiting on the system notification NOTIFICATION_EVENT_RS232_DETECTED. By using CeRunAppAtEvent (a bit of a misnomer, as it's not going to run an app but instead set an event) they've registered a named system event with the name "EventActiveSync" to be set when the notification occurs.
In essence, when the device is docked, the named system event will get set.
Your code has got some of the wait code in there, but not fully - it's calls WaitForSingleObject, but never looks at the result and then unhooks the event. I'd think it would look more like this
event EventHandler OnConnect = delegate{};
void ListenerThreadProc()
{
var eventName = "OnConnect";
// create an event to wait on
IntPtr #event = NativeMethods.CreateEvent (IntPtr.Zero, true, false, eventName);
// register for the notification
NativeMethods.CeRunAppAtEvent (
string.Format("\\\\.\\Notifications\\NamedEvents\\{0}", eventName),
(int) NOTIFICATION_EVENT.NOTIFICATION_EVENT_RS232_DETECTED);
while(!m_shutdown)
{
// wait for the event to be set
// use a 1s timeout so we don't prevent thread shutdown
if(NativeMethods.WaitForSingleObject(#event, 1000) == 0)
{
// raise an event
OnConnect(this, EventArgs.Empty);
}
}
// unregister the notification
NativeMethods.CeRunAppAtEvent (
string.Format("\\\\.\\Notifications\\NamedEvents\\{0}", eventName),
(int) NOTIFICATION_EVENT.NOTIFICATION_EVENT_NONE);
// clean up the event handle
NativeMethods.CloseHandle(#event);
}
Your app would create a thread that uses this proc at startup and wire up an event handler for the OnConnect event.
FWIW, the SDF has this already done, so it would be something like this in your code:
DeviceManagement.SerialDeviceDetected += DeviceConnected;
...
void DeviceConnected()
{
// handle connection
}
Here's an ActiveSync document on MSDN. A little old but should still be relevant. Also take a look at this
As for the CeRunAppAtEvent you need to create a wrapper to the Native method as below
[DllImport("coredll.dll", EntryPoint="CeRunAppAtEvent", SetLastError=true)]
private static extern bool CeRunAppAtEvent(string pwszAppName, int lWhichEvent);
You can find PInvode resources here and on MSDN
Related
I've got a UWP (C#) app that's running in production on a remote machine (under windows 10) but it periodically crashes.
My client says, somewhat arbitrarily, every 9 hours or so.
I have several .wer files from the previous crashes but did not have a minidump, the paths referenced in the event viewer entry for the crash are blank other than the WER files.
See edits below for how a minidump was obtained and findings.
The exception is an access violation (0xc0000005) at exception offset 0x0004df23 in ntdll.dll
I have the full source for the application and can run it in debug for long periods without the crash.
If I use DLL Export Viewer and load the exact version of ntdll.dll (copied from the remote machine) then I can see that at relative address 0x0004dc60 is EtwNotificationRegister and at 0x0004e260 is LdrGetDllPath.
Does this mean that my crash is occurring within a line of code in EtwNotificationRegister (which in turn is invoked by something within our code; however very difficult to trace without stack/minidump)
I am not sure if the layout of a dll is such that the address I have can be placed like that?
Edit 2 as per #Raymond: No. There are almost certainly other non-exported functions between EtwNotificationRegister and LdrGetDllPath. On build 17763.475, offset 4df23 is RtlpWaitOnCriticalSection, so you are probably using an uninitialized critical section or an already-deleted critical section.
Is there any way I can extract more detail about this crash? I have remote access to the computer running the app but the crash does not appear to be triggered by a particular event (e.g. we can't hit a button and cause the crash)
Using a minidump now
I am running the program in both local debug as well.
I have a remote debugger to the remote process but can't seem to break or inspect threads, not sure why. Just redeployed with symbols and the debugger attaches no problem but it just skips all breakpoints :(
Our own (rather naive) local log file, originally intended for only local debugging is written with a StreamWriter.WriteLine and immediately followed with a StreamWriter.Flush (wrapped in a try catch since that's not thread safe) just ends at a normal event on the remote machine - there is nothing following this normal event.
We catch App_UnhandledException and write to this log so I'd have expected a stack here.
In Unexplained crashes related to ntdll.dll it is suggested that a crash from ntdll.dll is a canary in a coalmine Unexplained crashes related to ntdll.dll
Edit 1: I have configured an auto crash dump as per https://www.meziantou.net/2018/06/04/tip-automatically-create-a-crash-dump-file-on-error so if I can get it to crash again maybe I'll get a dump file next time?
Here is the detail from the WER
Version=1
EventType=MoAppCrash
EventTime=132017523132123596
ReportType=2
Consent=1
UploadTime=132017523137590717
ReportStatus=268435456
ReportIdentifier=8d467f04-4bdd-4f9e-bf26-b42d143ece1a
IntegratorReportIdentifier=b60f9ca0-4126-4262-a886-98d3844892d3
Wow64Host=34404
NsAppName=praid:App
OriginalFilename=XXXXXX.YYYYYY.exe
AppSessionGuid=00001514-0001-0004-9fe2-6df11905d501
TargetAppId=U:XXXXXX.YYYYYY_1.0.201.0_x64__b0abmt6f49vqj!App
TargetAppVer=1.0.201.0_x64_!2018//01//24:08:17:16!1194d!XXXXXX.YYYYYY.exe
BootId=4294967295
TargetAsId=1298
UserImpactVector=271582000
IsFatal=1
EtwNonCollectReason=4
Response.BucketId=2ee79f27e2e81a541d6200d746866340
Response.BucketTable=5
Response.LegacyBucketId=2117255699418735424
Response.type=4
Sig[0].Name=Package Full Name
Sig[0].Value=XXXXXX.YYYYYY_1.0.201.0_x64__b0abmt6f49vqj
Sig[1].Name=Application Name
Sig[1].Value=praid:App
Sig[2].Name=Application Version
Sig[2].Value=1.0.0.0
Sig[3].Name=Application Timestamp
Sig[3].Value=5a68410c
Sig[4].Name=Fault Module Name
Sig[4].Value=ntdll.dll
Sig[5].Name=Fault Module Version
Sig[5].Value=10.0.17763.475
Sig[6].Name=Fault Module Timestamp
Sig[6].Value=3230aa04
Sig[7].Name=Exception Code
Sig[7].Value=c0000005
Sig[8].Name=Exception Offset
Sig[8].Value=000000000004df23
DynamicSig[1].Name=OS Version
DynamicSig[1].Value=10.0.17763.2.0.0.256.48
DynamicSig[2].Name=Locale ID
DynamicSig[2].Value=5129
DynamicSig[22].Name=Additional Information 1
DynamicSig[22].Value=95b1
DynamicSig[23].Name=Additional Information 2
DynamicSig[23].Value=95b15a88b673e33a5f48839974790b1c
DynamicSig[24].Name=Additional Information 3
DynamicSig[24].Value=283d
DynamicSig[25].Name=Additional Information 4
DynamicSig[25].Value=283dea7b6b6112710c1e3f76ed84d993
Edit 3: screenshot of minidump from a crash last night. In the event log, the WER crash looks the same so this appears to be the same issue. I will see if I can load symbols etc.
Edit 4: Attempting to debug managed. Threads view shows a thread as the exception point but no call stack info.
Edit 5: Debugging native from the minidump. Looks like we have a winner.
#Raymond was correct, it was RtlpWaitOnCriticalSection invoked from BluetoothLEAdvertismentWatcher::AdvertismentReceivedCallbackWorker
Native call stack as text:
Not Flagged > 8748 0 Worker Thread Win64
Thread Windows.Devices.Bluetooth.dll!(void)
ntdll.dll!RtlpWaitOnCriticalSection()
ntdll.dll!RtlpEnterCriticalSectionContended()
ntdll.dll!RtlEnterCriticalSection()
Windows.Devices.Bluetooth.dll!(void)()
Windows.Devices.Bluetooth.dll!wil::ResultFromException<(void)
()
Windows.Devices.Bluetooth.dll!Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher::AdvertisementReceivedCallbackWorker(void)
Windows.Devices.Bluetooth.dll!Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher::AdvertisementReceivedThreadpoolWorkCallbackStatic(struct
_TP_CALLBACK_INSTANCE *,void *,struct _TP_WORK *)
ntdll.dll!TppWorkpExecuteCallback()
ntdll.dll!TppWorkerThread()
kernel32.dll!BaseThreadInitThunk()
ntdll.dll!RtlUserThreadStart()
Edit 6: okay so now, what do I do? How can I resolve this problem? My understanding of the stack is it looks like an exception was thrown inside the callback? Is that correct?
So I could put a managed try/catch in the BLE advertisment callback handler and that should (catch - for further debugging) fix it?
Edit 7: code...
Here is the code we use to instantiate the wrapper and subscribe to events.
The BluetoothLEAdvertisementWatcherWrapper is a delgating class (e.g. it just wraps the underlying BluetoothLEAdvertisementWatcher so it can implement an interface; it simply passes all events through and exposes properties. We do this so that we can have a different version that creates virtual events for testing)
bluetoothAdvertisementWatcher = new BluetoothLEAdvertisementWatcherWrapper();
bluetoothAdvertisementWatcher.SignalStrengthFilter.SamplingInterval = TimeSpan.Zero;
bluetoothAdvertisementWatcher.ScanningMode = BluetoothLEScanningMode.Active;
bluetoothAdvertisementWatcher.Received += Watcher_Received;
bluetoothAdvertisementWatcher.Stopped += Watcher_Stopped;
bluetoothAdvertisementWatcher.Start();
Here is the code for the wrapper; just to show it's not doing anything complex:
public class BluetoothLEAdvertisementWatcherWrapper : IBluetoothAdvertismentWatcher, IDisposable
{
private BluetoothLEAdvertisementWatcher bluetoothWatcher;
public BluetoothLEAdvertisementWatcherWrapper()
{
bluetoothWatcher = new BluetoothLEAdvertisementWatcher();
}
public BluetoothSignalStrengthFilter SignalStrengthFilter => bluetoothWatcher.SignalStrengthFilter;
public BluetoothLEScanningMode ScanningMode
{
get
{
return bluetoothWatcher.ScanningMode;
}
set
{
bluetoothWatcher.ScanningMode = value;
}
}
public event TypedEventHandler<BluetoothLEAdvertisementWatcher, BluetoothLEAdvertisementReceivedEventArgs> Received
{
add
{
bluetoothWatcher.Received += value;
}
remove
{
bluetoothWatcher.Received -= value;
}
}
public event TypedEventHandler<BluetoothLEAdvertisementWatcher, BluetoothLEAdvertisementWatcherStoppedEventArgs> Stopped
{
add
{
bluetoothWatcher.Stopped += value;
}
remove
{
bluetoothWatcher.Stopped -= value;
}
}
public BluetoothLEAdvertisementWatcherStatus Status => bluetoothWatcher.Status;
public Action<IPacketFrame, short> YieldAdvertisingPacket { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public void Start()
{
bluetoothWatcher.Start();
}
public void Stop()
{
bluetoothWatcher.Stop();
}
public void Dispose()
{
if (bluetoothWatcher != null)
{
if (bluetoothWatcher.Status == BluetoothLEAdvertisementWatcherStatus.Started)
{
bluetoothWatcher.Stop();
}
bluetoothWatcher = null;
}
}
}
And here is the code for the Watcher_Received event handler:
private void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
try
{
//we won't queue packets until registered
if (!ApplicationContext.Current.Details.ReceiverId.HasValue)
return;
IPacketFrame frame;
PacketFrameParseResult result = ParseFrame(args, out frame);
if (result == PacketFrameParseResult.Success)
{
ApplicationContext.Current.Details.BluetoothPacketCount++;
}
short rssi = args.RawSignalStrengthInDBm;
string message = FormatPacketForDisplay(args, args.AdvertisementType, rssi, frame, result);
if (BluetoothPacketReceived != null)
{
BluetoothPacketReceived.Invoke(this, new BluetoothPacketReceivedEventArgs(message, result, frame, rssi));
}
}
catch (Exception ex)
{
if (ex.InnerException is Exceptions.PacketFrameParseException && (ex.InnerException as Exceptions.PacketFrameParseException).Result == PacketFrameParseResult.InvalidData)
{
// noop
}
else
{
Logger.Log(LogLevel.Warning, "BLE listener caught bluetooth packet error: {0}", ex);
if (BluetoothPacketError != null)
{
BluetoothPacketError.Invoke(this, new BluetoothPacketErrorEventArgs(ex));
}
}
}
}
You can see here that the entire managed callback is wrapped in a try catch and doesn't rethrow, so I'm not sure if there's anything further I can do to prevent the native exception from bringing the application down.
Current thinking, based on this: RtlpEnterCriticalSectionContended is it a parallel event handler, the native side is raising the handler, and it raises for a new event in the same thread while the previous handler is still executing from a previous event?
Then this is a race condition on the critical section that causes the crash?
Edit 8: To test this theory, I replaced the contents of received with a read + push to a concurrent queue, allowing the managed code to exit the event handler as quickly as possible.
Then added a seperate thread reading from the concurrent queue to perform my application side processing.
Initially, I thought this had resolved the issue as the application actively (listening) ran for approximately 15 hours, however it crashed again this morning with the same symptoms.
Edit 8: Following suggestions in the comments, we tried to ensure that we didn't dispose/GC the watcher after a stop prior to the receive completing.
We did this by using a TaskCompletionSource to function as a promise, subscribing to the Stopped event so we could await on the completion source task which would only have a result set when the Stopped event had fired.
We also used a lock (Monitor.Enter) in both StopAsync and Received to ensure that both could not be running in parallel.
This appeared to reduce the speed at which the system could process events which would make sense if the BLE packets were arriving in parallel.
Updated code as follows:
if ((DateTime.Now - this.LastStartedTimestamp).TotalSeconds > 60)
{
if (this.LastStopReason != BluetoothWatcherStopReason.DeviceCharacteristicWorker)
{
Logger.Log(LogLevel.Debug, "Stopping bluetooth watcher...");
// restart watcher every 10 mins
await this.StopAsync(BluetoothWatcherStopReason.AutomaticRestart);
//start again if automatic restart
Logger.Log(LogLevel.Debug, "Starting bluetooth watcher...");
this.Start(this.testMode);
Logger.Log(LogLevel.Debug, "Started bluetooth watcher");
this.LastStartedTimestamp = DateTime.Now;
}
}
private void Watcher_Stopped(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args)
{
string error = args.Error.ToString();
Logger.Log(LogLevel.Warning, string.Format("BLE listening stopped because {0}...", error));
LastError = args.Error;
if (BluetoothWatcherStopped != null)
{
BluetoothWatcherStopped.Invoke(sender, args);
}
}
public class ReceivedBluetoothAdvertismentPacketItem
{
public DateTime Timestamp { get; set; }
public BluetoothLEAdvertisementType Type { get; set; }
public byte[] Buffer { get; set; }
public short Rssi { get; set; }
}
ConcurrentQueue<ReceivedBluetoothAdvertismentPacketItem> BluetoothPacketsReceivedQueue = new ConcurrentQueue<ReceivedBluetoothAdvertismentPacketItem>();
private void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
bool lockWasTaken = false;
try
{
//this prevents stop until we're exiting Received
Monitor.Enter(BluetoothWatcherEventSynchronisation, ref lockWasTaken);
if (!lockWasTaken)
{
return;
}
//we won't queue packets until registered
if (!ApplicationContext.Current.ReceiverDetails.ReceiverId.HasValue)
return;
BluetoothLEAdvertisementType type = args.AdvertisementType;
byte[] buffer = GetManufacturerData(args.Advertisement);
short rssi = args.RawSignalStrengthInDBm;
BluetoothPacketsReceivedQueue.Enqueue(new ReceivedBluetoothAdvertismentPacketItem
{
Timestamp = DateTime.UtcNow,
Type = type,
Rssi = rssi,
Buffer = buffer
});
ApplicationContext.Current.ReceiverDetails.UnprocessedQueueLength = BluetoothPacketsReceivedQueue.Count;
}
catch (Exception ex)
{
Logger.Log(LogLevel.Warning, "BLE listener caught bluetooth packet error: {0}", ex);
if (BluetoothPacketError != null)
{
BluetoothPacketError.Invoke(this, new BluetoothPacketErrorEventArgs(ex));
}
}
finally
{
if (lockWasTaken)
{
Monitor.Exit(BluetoothWatcherEventSynchronisation);
}
}
}
public BluetoothWatcherStopReason LastStopReason { get; private set; } = BluetoothWatcherStopReason.Unknown;
private object BluetoothWatcherEventSynchronisation = new object();
public Task<BluetoothWatcherStopReason> StopAsync(BluetoothWatcherStopReason reason)
{
var promise = new TaskCompletionSource<BluetoothWatcherStopReason>();
if (bluetoothAdvertisementWatcher != null)
{
LastStopReason = reason;
UpdateBluetoothStatusInReceiverModel(BluetoothLEAdvertisementWatcherStatus.Stopped); //actually stopping but we lie
bool lockWasTaken = false;
try
{
Monitor.Enter(BluetoothWatcherEventSynchronisation, ref lockWasTaken);
{
bluetoothAdvertisementWatcher.Received -= Watcher_Received;
bluetoothAdvertisementWatcher.Stopped += (sender, args) =>
{
// clean up
if (bluetoothAdvertisementWatcher != null)
{
bluetoothAdvertisementWatcher.Stopped -= Watcher_Stopped;
bluetoothAdvertisementWatcher = null;
}
//notify continuation
promise.SetResult(reason);
};
bluetoothAdvertisementWatcher.Stop();
}
}
finally
{
if (lockWasTaken)
{
Monitor.Exit(BluetoothWatcherEventSynchronisation);
}
}
}
base.Stop();
return promise.Task;
}
Following these changes, the same crash is still occuring in the Windows.Devices.Bluetooth native assembly (as per above)
Edit 9: I've removed the automatic periodic start/stop and now the app has been stable for > 36 hours without a crash. So something inside this flow is causing the crashes. We originally added that to work around an issue with the advertisment watcher just stopping after a while, so we'd like to keep it if we can fix it.
The if statement if ((DateTime.Now - this.LastStartedTimestamp).TotalSeconds > 60) (and block) is currently commented.
I have opened a bug for windows universal here: https://wpdev.uservoice.com/forums/110705-universal-windows-platform/suggestions/37623343-bluetoothleadvertismentwatcher-advertismentreceiv
I have a requirement for an UWP app to claim any paired Bluetooth scanner device upon app start, but also after the Bluetooth scanner power cycles, or comes back in range after being out of range. The users shouldn't even have to think about connecting/claiming. Turning on the scanner device and having the UWP app running should do it.
I haven't found any reliable method or event to automatically detect when a Bluetooth scanner is in range and 'ready to be claimed'. (For instance after going out/in of reach or after turning on or power cycling the Bluetooth scanner device.)
Basically this resulted in the following code, in which my code continually tries to claim any Bluetooth scanner detected by DeviceWatcher. It will do so until it succeeds. For any unclaimed scanners in the DeviceWatcher, claim attempts are made on a timer. Also I have to use a rather ugly try/catch construction to prevent unsuccessful ClaimScannerAsync() calls from crashing the app, as this method doesn't seem to throw it's own inherited Exception class.
This does seem to work, but I do wonder if there's a better method for this.
Is there a better way to check when ready-to-claim Bluetooth scanners are in range?
Please take the claiming process for Bluetooth barcode scanners into consideration.
First pair the Bluetooth in Windows settings, the device will then be available and show up as paired. Even if it is turned off.
Only after actually claiming the scanner in an app, the device will show up as connected.
(I'm pasting the code I described for reference and for other people who might have the same requirement)
private readonly List<BarcodeScanner> btScannerList = new List<BarcodeScanner>();
private readonly List<ClaimedBarcodeScanner> ClaimedScannerList = new List<ClaimedBarcodeScanner>();
/// <summary>
/// Function to claim scanner with retry timer in case of claim failure (used to reclaim scanner when scanner can go out/in of range or turned on/off)
/// In this case a device removed event is not fired by DeviceWatcher
/// This function is called for each bluetooth scanner detected by DeviceWatcher
/// </summary>
/// <param name="deviceId"></param>
private async void TryClaimBtScannerByDeviceId(string deviceId)
{
try
{
if (!await this.ClaimBtScannerByDeviceId(deviceId))
{
Log.Debug($"BarcodeService.TryClaimBtScannerByDeviceId Failed to reconnect, setting timer to reconnect.");
this.SetReclaimTimerForBtScanner(deviceId);
}
}
catch
{
Log.Debug($"BarcodeService.TryClaimBtScannerByDeviceId Exception while trying to reconnect (probably a timeout), setting timer to reconnect.");
this.SetReclaimTimerForBtScanner(deviceId);
}
}
private void SetReclaimTimerForBtScanner(string deviceId)
{
var timer = new System.Timers.Timer
{
Interval = 3000,
AutoReset = false
};
timer.Elapsed += delegate { this.TryClaimBtScannerByDeviceId(deviceId); };
timer.Start();
}
private async Task<bool> ClaimBtScannerByDeviceId(string deviceId)
{
var scanner = await BarcodeScanner.FromIdAsync(deviceId);
scanner.StatusUpdated += this.Scanner_StatusUpdated;
this.btScannerList.Add(scanner);
return await this.ClaimScannerAsync(scanner);
}
private void Scanner_StatusUpdated(BarcodeScanner sender, BarcodeScannerStatusUpdatedEventArgs args)
{
if (args.Status == BarcodeScannerStatus.OffOrOffline || args.Status == BarcodeScannerStatus.Offline || args.Status == BarcodeScannerStatus.Off)
{
Log.Information($"BarcodeService.DeviceWatcher StatusUpdated to off or offline, setting reclaim timer for deviceId: {sender.DeviceId} -> {args.Status}");
var deviceId = sender.DeviceId;
this.UnsetScannerByDeviceId(deviceId);
this.SetReclaimTimerForBtScanner(deviceId);
}
}
private async Task<bool> ClaimScannerAsync(BarcodeScanner scanner)
{
Log.Information($"BarcodeService.ClaimBarcodeScannerAsync() Trying to (re)claim scanner with DeviceId: {scanner.DeviceId} for exclusive use...");
// after successful creation, claim the scanner for exclusive use and enable it so that data reveived events are received.
var claimedScanner = await scanner.ClaimScannerAsync();
if (claimedScanner == null)
{
Log.Warning($"BarcodeService.ClaimBarcodeScannerAsync() Couldn't claim barcode scanner for exclusive use (deviceid {scanner.DeviceId})");
return false;
}
else
{
Log.Information($"BarcodeService.ClaimBarcodeScannerAsync() Claimed scanner for exclusive use. Setting up event handlers...");
// It is always a good idea to have a release device requested event handler. If this event is not handled, there are chances of another app can
// claim ownsership of the barcode scanner.
claimedScanner.ReleaseDeviceRequested += this.ClaimedScanner_ReleaseDeviceRequested;
// after successfully claiming, attach the datareceived event handler.
claimedScanner.DataReceived += this.ClaimedScanner_DataReceived;
// Ask the API to decode the data by default. By setting this, API will decode the raw data from the barcode scanner and
// send the ScanDataLabel and ScanDataType in the DataReceived event
claimedScanner.IsDecodeDataEnabled = true;
// enable the scanner.
// Note: If the scanner is not enabled (i.e. EnableAsync not called), attaching the event handler will not be any useful because the API will not fire the event
// if the claimedScanner has not beed Enabled
await claimedScanner.EnableAsync();
this.ClaimedScannerList.Add(claimedScanner);
Log.Information("BarcodeService.ClaimBarcodeScannerAsync() Ready to scan. Device ID: " + claimedScanner.DeviceId);
return true;
}
}
public void UnsetScannerByDeviceId(string deviceId)
{
try
{
foreach (var claimedScanner in this.ClaimedScannerList.Where(x => x.DeviceId.Equals(deviceId)).ToList())
{
this.DisposeClaimedScanner(claimedScanner);
}
foreach (var scanner in this.btScannerList.Where(x => x.DeviceId.Equals(deviceId)).ToList())
{
this.DisposeScanner(scanner);
}
}
catch
{
Log.Warning($"BarcodeService.UnsetScannerByDeviceId() Error while disposing scanner with scannerId: {deviceId}");
}
}
private void DisposeScanner(BarcodeScanner scanner)
{
scanner.StatusUpdated -= this.Scanner_StatusUpdated;
scanner.Dispose();
this.btScannerList.Remove(scanner);
scanner = null;
}
private void DisposeClaimedScanner(ClaimedBarcodeScanner claimedScanner)
{
// Detach the event handlers
claimedScanner.DataReceived -= this.ClaimedScanner_DataReceived;
claimedScanner.ReleaseDeviceRequested -= this.ClaimedScanner_ReleaseDeviceRequested;
// Release the Barcode Scanner and set to null
claimedScanner.Dispose();
this.ClaimedScannerList.Remove(claimedScanner);
claimedScanner = null;
}
Is there a way to gracefully shut-down a DOTNET CORE application which is running in DOCKER? If yes, which event I should listen?
All I want is upon cancellation request I would like to pass my cancellation token/s to current methods and postpone the shut-down while they are working.
Looking for a sample code, reference link etc. which are relevant to dotnet core and not generic info
UPDATE
This question is not a duplicate of docker container exits immediately even with Console.ReadLine() in a .net core console application because I'm not having an immediate exit issue. I need to tap into event something like Windows.SystemsEvents.SessionEnding and relying on Console.CancelKeyPress and/or implementing WebHostBuilder() doesn't fit the bill.
In .NET Core 2.0, you can use the AppDomain.CurrentDomain.ProcessExit event, which works fine on Linux in Docker. AssemblyLoadContext.Default.Unloading probably works as well, even before .NET Core 2.0.
System.Console has an event called CancelKeyPress. I believe this is fired when a sigint event is passed into dotnet.
System.Console.CancelKeyPress += (s,e) => { /* do something here */};
Using 2.0.0-preview2-006497 I did some testing, and now the AssemblyLoadContext.Default.Unloading is fired when Docker sends a SIGTERM/SIGINT to the container.
Example code looks like:
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx =>
{
// code here...
};
See also this issue for some details: https://github.com/aspnet/Hosting/issues/870
If your container is running in Linux then Loader.AssemblyLoadContext.Default.Unloading works since it can trap the SIGTERM signal, however Windows do not have the equivalent mechanism for this(dotnet issue). Here's an answer to handle shutdown notification in Windows by using SetConsoleCtrlHandler originally from gist
namespace Routeguide
{
using System;
using System.Threading;
...
class Program
{
[DllImport("Kernel32")]
internal static extern bool SetConsoleCtrlHandler(HandlerRoutine handler, bool Add);
internal delegate bool HandlerRoutine(CtrlTypes ctrlType);
internal enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
static void Main(string[] args)
{
// do the server starting code
Start();
....
var shutdown = new ManualResetEvent(false);
var complete = new ManualResetEventSlim();
var hr = new HandlerRoutine(type =>
{
Log.Logger.Information($"ConsoleCtrlHandler got signal: {type}");
shutdown.Set();
complete.Wait();
return false;
});
SetConsoleCtrlHandler(hr, true);
Console.WriteLine("Waiting on handler to trigger...");
shutdown.WaitOne();
Console.WriteLine("Stopping server...");
// do the server stopping code
Stop();
complete.Set();
GC.KeepAlive(hr);
}
}
}
I have two WinForm client applications that reference the same managed DLL (written in C++/CLI) with the purpose of connecting to a native socket server.
Both Winform applications run fine when launched separately, but not when one launches the other.
Let's say that client Winform 1 is launched. It creates its own socket and context as intended and then proceeds to launch Winform 2 as a separate thread.
Winform 2 will also open its own socket as a client of the native server, but when Winform 2 closes its socket and exits, Winform 1 stops working because it seems to think it's Winform 2. So any server requests by WinForm 1 fail because its socket becomes the one previously closed by socket 2.
This behavior is new to me, but it must obviously extend beyond variable "SOCKET socket_id".
Is Winform 2 supposed to be launched as a separate process instead of the typical thread that executes Application.Run(Winform2)?
Thanks.
private void LaunchWinForm2Button_Click(object sender, EventArgs e)
{
System.Threading.Thread myThread =
new System.Threading.Thread(new System.Threading.ThreadStart(StartWinForm2));
myThread.Start();
}
private void StartWinForm2()
{
CSharpFormApp.WinForm2 theWinForm2 = new CSharpFormApp.WinForm2();
Application.Run(theWinForm2);
}
The problem was with a global pointer to native data in the C++/CLI DLL.
Since the WinForms get their data through several C++/CLI interfaces, it was initially easier to just have them access native data through a global pointer.
NativeLayer::NativeLayerData* native_data;
public ref class Test1Implementer : ITest1
{
virtual bool TestConnect()
{
bool success = native_data->Connect();
return success;
}
};
public ref class Test2Implementer : ITest2
{
virtual bool TestDisconnect()
{
bool success = native_data->Disconnect();
return success;
}
};
Eventually, such an implementation would come back to haunt me, but those are the perils of attempting to use globals in industrial applications.
Once I got rid of the managed global pointer to native data, everything works as intended. Here's a possible solution that allows for multithreading using nested interfaces:
public ref class TestsImplementer : ITests
{
private:
NativeLayer::NativeLayerData* native_data;
public:
TestsImplementer()
{
native_data = new NativeLayer::NativeLayerData();
}
ref class Test1Implementer : ITest1
{
virtual bool TestConnect(TestsImplementer^ tests)
{
bool success = tests->native_data->Connect();
return success;
}
};
ref class Test2Implementer : ITest2
{
virtual bool TestDisconnect(TestsImplementer^ tests)
{
bool success = tests->native_data->Disconnect();
return success;
}
};
};
Background: In Windows Vista and above, using an expanded Core Audio API (by Ray Molenkamp and Xavier Flix) to enforce volume levels by subscribing to the DefaultAudioEndpoint's OnVolumeNotification and setting the volume when it changes.
Problem: Functionally successful, but as soon as a subscription to OnVolumeNotification is registered, the CPU tends to get pegged at 30-50% depending on the power of your CPU. After much digging with Process Explorer & Process Monitor it was revealed that explorer.exe and sometimes svchost.exe would become consumed by registry read calls. I am uncertain of which registry key. I don't believe I am subscribing to this event in a harmful way as I manage subscription carefully -- it's only being fired once.
Logical process of enforcing volume
Unsubscribe from endpoint OnVolumeNotification
Set endpoint volume scalar property (effective immediately)
Subscribe to endpoint OnVolumeNotification
The underlying win32 methods involved in the Core Audio API are RegisterControlChangeNotify and UnregisterControlChangeNotify. Is it possible the issue is caused by these or the implementation of the event subscription?
Rather than:
Unsubscribing
Change volume / set mute
Re-Subscribe
I modified my logic to essentially use logic in properties with backing fields to manage when to update. It isn't perfect, but it's pretty damn close and it doesn't eat up any CPU and it allows for external input from a slider with full support for INPC.
public EndpointVolumeEnforcer() {
try {
mmDeviceEnumerator = new MMDeviceEnumerator();
mmDevice = mmDeviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
audioEndpointVolume = mmDevice.AudioEndpointVolume;
audioEndpointVolume.OnVolumeNotification += data => {
VolumePercent = Convert.ToInt16(data.MasterVolume*100);
DeviceIsMuted = data.Muted;
};
DesiredVolume = 65;
}
catch (Exception ex) {
// Logging logic here
}
}
public int DesiredVolume {
get { return _desiredVolume; }
private set {
if (_desiredVolume == value) return;
_desiredVolume = value;
NotifyOfPropertyChange();
Enforce(_desiredVolume);
}
}
public int VolumePercent {
get { return volumePercent; }
private set {
if (volumePercent == value) return;
volumePercent = value;
if (volumePercent != _desiredVolume) {
volumePercent = _desiredVolume;
Enforce(volumePercent);
}
}
}
public void Enforce(int pct, bool mute = false) {
var adjusted = Convert.ToInt16(audioEndpointVolume.MasterVolumeLevelScalar*100);
if (adjusted != DesiredVolume) {
audioEndpointVolume.MasterVolumeLevelScalar = pct/100f;
}
}