I am trying to throw together a proof of concept project, just to see how good Microsoft's Cognitive Services Speech Transcription is.
I have followed all the examples on their site, but have so far been unsuccessful. Initially I was unable to get it to run at all under one of my existing code bases as x86, it was throwing the error:
An attempt was made to load a program with an incorrect format
Then I created a brand new .net framework x64 console app. And it would start, then crash internally using version 1.4.0 as well as a few other versions I tried and put this error into my event log:
Faulting application name: dotnet.exe, version: 2.1.27415.1, time
stamp: 0x5c672873 Faulting module name:
Microsoft.CognitiveServices.Speech.core.dll, version: 1.3.1.28, time
stamp: 0x5c764ab1 Exception code: 0xc0000094 Fault offset:
0x000000000007567c Faulting process id: 0x6200 Faulting application
start time: 0x01d4f1518c240c4b Faulting application path: C:\Program
Files\dotnet\dotnet.exe Faulting module path:
C:\Users\username.nuget\packages\microsoft.cognitiveservices.speech\1.3.1\runtimes\win-x64\native\Microsoft.CognitiveServices.Speech.core.dll
Finally I found version 1.1.0 which would actually start, (version 1.0.0 would not even allow the app to compile). Now I am running into the issue that the SessionStarted and SessionStopped events are called instantly, but no transcription ever takes place, and using Fiddler it looks like no calls are being made outside of my machine.
Unless Cognitive Services is really buggy, then there must be something simple I am missing. Can anyone point it out?
My goal is to transcribe a 5 minute or less audio file on my local network. Here is the code I am attempting.
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var file = #"U:\path\file.wav";
ContinuousRecognitionAsync(file).Wait();
Console.WriteLine("End!");
}
public static async Task ContinuousRecognitionAsync(string audiopath)
{
// subscription key and service region. Replace with your own subscription key
// and service region (e.g., "westus").
var config = SpeechConfig.FromSubscription("<my free test key>", "westus");
var audio = Microsoft.CognitiveServices.Speech.Audio.AudioConfig.FromWavFileInput(audiopath);
// Creates a continuos speech recognizer using WAV input.
using (var recognizer = new SpeechRecognizer(config, audio))
{
//Subscribes to events.
recognizer.Recognizing += (s, e) =>
{
Console.WriteLine($"\n Recognizing: {e.Result.Text}.");
};
recognizer.Recognized += (s, e) =>
{
Console.WriteLine($"\n Recognized: {e.Result.Text}.");
};
recognizer.SessionStarted += (s, e) =>
{
Console.WriteLine($"\n SessionStarted: {e.SessionId}.");
};
recognizer.SessionStopped += (s, e) =>
{
Console.WriteLine($"\n SessionStopped: {e.SessionId}.");
};
recognizer.SpeechEndDetected += (s, e) =>
{
Console.WriteLine($"\n SpeechEndDetected: {e.SessionId}.");
};
recognizer.SpeechStartDetected += (s, e) =>
{
Console.WriteLine($"\n SpeechStartDetected: {e.SessionId}.");
};
recognizer.Canceled += (s, e) =>
{
Console.WriteLine($"\n Canceled: {e.SessionId}.");
};
// Starts continuous recognition. Uses StopContinuousRecognitionAsync() to stop recognition.
Console.WriteLine("Say something...");
//await recognizer.StartContinuousRecognitionAsync().ConfigureAwait(false);
await recognizer.StartContinuousRecognitionAsync().ConfigureAwait(false);
Console.WriteLine("Press any key to stop");
Console.ReadKey();
await recognizer.StopContinuousRecognitionAsync().ConfigureAwait(false);
}
}
EDIT: After some changes, and moving the wav file locally (it was on a mapped drive), it did briefly try to run a transcription on the file, but no valid text was ever returned, only blank strings.
Transcription via microphone is working just fine. But as soon as I throw one of my .wav files at it Cognitive Services is once again crashing with the Exception code: 0xc0000094. I even tried the code that half worked, and that is also throwing the same error now.
I figured out the issue, it turned out to be the .wav files themselves. As near as I could tell, they were valid wave files. With WAV listed at the top of the binary file if you looked at it in Notepad++. However, they consistently caused Cognitive Services to crash. And the one time I got it to take one, it was unable to read it and just started running in an infinite loop returning blank strings.
I solved the issue by running the files through a double conversion. I converted them to .m4a files, then back to .wav files. Once I did that they all started working perfectly.
I originally thought it was because I was storing the files remotely on a mapped drive. However, access via mapped drive worked just fine once the files were fixed.
Hopefully Microsoft will add better error handling to the Cognitive Services wrapper. And allow the API to handle more than just wav file types.
Related
I am trying to create a simple proof of concept speech transcribing program using Azure. I have set up all the stuff in Azure and tested with a simple program based on the docs:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.CognitiveServices.Speech;
using Microsoft.CognitiveServices.Speech.Audio;
class Program
{
static SpeechRecognizer recognizer;
async static Task FromMic(SpeechConfig speechConfig)
{
using var audioConfig = AudioConfig.FromDefaultMicrophoneInput();
recognizer = new SpeechRecognizer(speechConfig, audioConfig);
var stopRecognition = new TaskCompletionSource<int>();
recognizer.SessionStarted += (s, e) =>
{
Console.WriteLine("\n Session started event: " + e);
};
recognizer.Recognizing += (s, e) =>
{
Console.WriteLine($"RECOGNIZING: Text={e.Result.Text}");
};
recognizer.Recognized += (s, e) =>
{
if (e.Result.Reason == ResultReason.RecognizedSpeech)
{
Console.WriteLine($"RECOGNIZED: Text={e.Result.Text}");
}
else if (e.Result.Reason == ResultReason.NoMatch)
{
Console.WriteLine($"NOMATCH: Speech could not be recognized.");
}
};
recognizer.Canceled += (s, e) =>
{
Console.WriteLine($"CANCELED: Reason={e.Reason}");
if (e.Reason == CancellationReason.Error)
{
Console.WriteLine($"CANCELED: ErrorCode={e.ErrorCode}");
Console.WriteLine($"CANCELED: ErrorDetails={e.ErrorDetails}");
Console.WriteLine($"CANCELED: Did you set the speech resource key and region values?");
}
stopRecognition.TrySetResult(0);
};
recognizer.SessionStopped += (s, e) =>
{
Console.WriteLine("\n Session stopped event.");
stopRecognition.TrySetResult(0);
};
await recognizer.StartContinuousRecognitionAsync();
// Waits for completion. Use Task.WaitAny to keep the task rooted.
Task.WaitAny(new[] { stopRecognition.Task });
}
async static Task Main(string[] args)
{
var speechConfig = SpeechConfig.FromSubscription("xxxxxxxxxxxxxxxxxxxx", "xxxx");
// Clearly I don't really know how to stop the recognition properly before exiting, but that's an issue for another day...
AppDomain.CurrentDomain.ProcessExit += delegate
{
EndRecognition();
};
Console.CancelKeyPress += delegate
{
EndRecognition();
};
await FromMic(speechConfig);
Console.WriteLine("Exiting");
}
static void EndRecognition()
{
Console.WriteLine("Ending recognition...");
recognizer.StopContinuousRecognitionAsync();
recognizer.Dispose();
Console.WriteLine("Done.");
}
}
The program works fine on my personal machine at home. When I try the same thing on a work computer, I get the session started message, but nothing else (no speech recognition).
My organization routes all traffic through a proxy and of course has less permissive firewall rules than my home machine/network, so I have tried:
making sure the mic is working/connected
setting HTTP_PROXY and HTTPS_PROXY environment variables to my organization's proxy
viewing the AV firewall logs (doesn't seem to show anything, but perhaps that's because I'm not an admin or something)
viewing the "total calls" metric chart on Azure to see if anything is actually happening on the Azure side (nothing is)
I would have expected an exception to be thrown if the program can't connect to the Azure VM hosting the speech resource (not sure if using correct terminology, still new to this), but apparently not; something seems to be silently failing.
What would be the next thing to try/check as the next troubleshooting step here?
Note: as stated above, this is proof of concept/experiment kind of thing for a demo or two; long term I don't plan to connect to a personal cloud service on a corporate network.
I have to tried to reproduce the issue by using the visual studio 2019 and created a sample speech resource in azure and copied the key and region of that resource with your code sample and able to get the output from the speech recognizer as shown in the below screenshot:
I have installed cognitive services speech package through the nuget package manager as shown below:
Initially got the error message as shown in below image:
After passing the key and region of the azure speech resource in the code and after rebuilding in VS got the result below:
Output:
Refer this Microsoft document to Configure virtual networks and the Speech resource networking settings and also use Speech service through a private endpoint.
In addition, SpeechConfig has a SetProxy method that can be used to establish proxy details before creating a recognizer -- if you have a known proxy within an enterprise network, this method may allow traffic to do the right thing.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.cognitiveservices.speech.speechconfig.setproxy?view=azure-dotnet#microsoft-cognitiveservices-speech-speechconfig-setproxy(system-string-system-int32)
Some customers have corporate networks with more restrictive "allow-based" networking lists, and in those situations proxy configuration won't make a difference (coordination with the network administrators to add the appropriate hosts to that allow list are needed). But if it's just a matter of providing a host/port/username/password, using the SpeechConfig per the above should help.
We are getting error on server and our service is automatically stopped in the server.
Randomly application is crash in approx 1 hour with below Error as -
Faulting application name: Chubb.Studio.Event.Processor.exe, version:
0.0.0.0, time stamp: 0x5c0ab1b7 Faulting module name: KERNELBASE.dll, version: 6.3.9600.19425, time stamp: 0x5d26b6e9 Exception code:
0xc0000005 Fault offset: 0x0000000000001556 Faulting process id:
0x115c Faulting application start time: 0x01d5a35fd202f96d Faulting
application path:
E:\WindowsService\DevInt\Chubb.Studio.EventProcessor\Chubb.Studio.Event.Processor.exe
Faulting module path: C:\Windows\system32\KERNELBASE.dll Report Id:
762c15d4-0f5b-11ea-8120-005056a27597 Faulting package full name:
Faulting package-relative application ID:
Our Code is look like as -
protected override void OnStarted()
{
//IntializeEventsExecution();
Task task = Task.Factory.StartNew(() => IntializeEventsExecution());
base.OnStarted();
}
public void IntializeEventsExecution()
{
StartEvents();
}
public void StartEvents()
{
var eventList = GetEventTopics();
Parallel.ForEach(eventList,
new ParallelOptions { MaxDegreeOfParallelism = eventList.Count },
(item, state, index) =>
{
StartProcessingEvent(eventList[(int)index]);
});
}
/// <summary>
///
/// </summary>
/// <param name="index"></param>
public void StartProcessingEvent(EventTopic topic)
{
try
{
Task task = Task.Factory.StartNew(() => ExecuteProcessingEvent(topic));
task.Wait();
}
catch (Exception)
{
}
finally
{
new _processingDelegate(StartProcessingEvent).Invoke(topic);
}
}
As Klaus says in his comment, a STATUS_ACCESS-VIOLATION exception is caused by a process reading or writing memory that it doesn't own. Given this is C#, the most likely reason is either an incorrect use of P/Invoke or using unsafe code.
The best approach to debugging something vague like this is to isolate the issue by removing P/Invoke calls one by one until the exception doesn't happen. It's hard to be more precise because the exception may be triggered a long way from the cause (memory or stack corruption).
This SO answer gives a good list of the likely causes of an access violation in managed code.
Access violations in managed apps typically happen for one of these
reasons:
You P/Invoke a native API passing in a handle to a managed object and the native API uses that handle. If you get a collection and
compaction while the native API is running, the managed object may
move and the pointer becomes invalid.
You P/Invoke something with a buffer that is too small or smaller than the size you pass in and the API overruns a read or write
A pointer (IntPtr, etc) you pass to a P/Invoke call is invalid (-1 or 0) and the native isn't checking it before use
You P/Invoke a native call and the native code runs out of memory (usually virtual) and isn't checking for failed allocations and
reads/writes to an invalid address
You use a GCHandle that is not initialized or that somehow is pointing to an already finalized and collected object (so it's not
pointing to an object, it's pointing to an address where an object
used to be)
Your app uses a handle to something that got invalidated by a sleep/wake. This is more esoteric but certainly happens. For example,
if you're running an application off of a storage card, the entire app
isn't loaded into RAM. Pieces in use are demand-paged in for
execution. This is all well and good. Now if you power the device off,
the drivers all shut down. When you power back up, many devices simply
re-mount the storage devices. When your app needs to demand-page in
more program, it's no longer where it was and it dies. Similar
behavior can happen with databases on mounted stores. If you have an
open handle to the database, after a sleep/wake cycle the connection
handle may no longer be valid.
I have a console application written in C# .Net 4.5 and running on Mono 3.2.8 on Linux (Ubuntu 14.04 LTS).
The console app is started as a service with upstart and I am logging output using log4net v2.0.5 and a console appender.
Upstart redirects all the output in /var/log/upstart/{appname}.log
Randomly, after few hours up to a couple of days, the application hangs and I see nothing in the logs.
What I know:
Disk is not full
Memory is not full and there is no swap
Command ps shows the process still running
The application is not sending anymore data to external server. When working properly the external server receives data every few seconds.
Log file is not populated anymore
I used the command strace to see the last syscall and this is what I get:
$ strace -p 5602
Process 5602 attached
poll([{fd=6, events=POLLIN}, {fd=8, events=POLLIN}], 2, -1) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGPWR {si_signo=SIGPWR, si_code=SI_TKILL, si_pid=5602, si_uid=1001} ---
rt_sigprocmask(SIG_BLOCK, [XCPU], NULL, 8) = 0
futex(0x26b18c, FUTEX_WAKE_PRIVATE, 1) = 1
rt_sigsuspend(~[XCPU RTMIN RT_1]) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGXCPU {si_signo=SIGXCPU, si_code=SI_TKILL, si_pid=5602, si_uid=1001} ---
rt_sigreturn() = -1 EINTR (Interrupted system call)
rt_sigprocmask(SIG_UNBLOCK, [XCPU], NULL, 8) = 0
futex(0x26b18c, FUTEX_WAKE_PRIVATE, 1) = 1
rt_sigreturn() = -1 EINTR (Interrupted system call)
The 2 file descriptors 6 and 8 are pipes:
$ file /proc/5602/fd/6
/proc/5602/fd/6: broken symbolic link to pipe:[6562495]'
$ file /proc/5602/fd/8
/proc/5602/fd/8: broken symbolic link to pipe:[6562496]'
I do not understand the root cause of the issue and I do not know what to try to fix it.
EDIT:
As suggested by #sushihangover I ran the app in a terminal with --debug option. Eventually the app hanged again. No exception, no error.
The code is acquiring frames from MJPEG streams and doing some work on a background worker to detect object in the frame.
The work only happens when the background worker is not busy. The last log trace I get is:
[Background worker] nothing to do
I suspect the app is hanging while trying to get the next frame from the camera. I am using AForge.NET to read the MJPEG stream. AForge raise an events every time a new frame arrives.
Here is some code:
private static void Camera_NewFrame(object sender, NewImageEventArgs e)
{
var bmp = (Bitmap)e.Frame;
log.DebugFormat("got image " + DateTime.Now.Ticks + " {0} x {1}", bmp.Width, bmp.Height);
if (!bWorker.IsBusy)
{
// Run the background operation to check image and update cloud
log.Debug("Starting background work");
bWorker.RunWorkerAsync();
}
else { } // skip frame
}
private static void BWorker_DoWork(object sender, DoWorkEventArgs e)
{
log.Debug("[Background worker] enter");
if (registeredCar)
{
log.Debug("[Background worker] opening the gate");
OpenGate();
}
else
{
log.Debug("[Background worker] nothing to do");
}
}
And the backtrace given by gdb attached to the hanging process:
(gdb) bt
0 0xb6e19fc0 in poll () at ../sysdeps/unix/syscall-template.S:81
1 0xb4b264be in Mono_Unix_UnixSignal_WaitAny () from /usr/lib/libMonoPosixHelper.so
2 0xb4b6f740 in ?? ()
I'm using BluetoothLEAdvertisementWatcher to find nearby BLE devices and it's working well. After finding them I want to connect and read/write data via GATT. But I can't figure out how to use the API after getting the BluetoothLEAdvertisement (https://msdn.microsoft.com/de-de/library/windows/apps/windows.devices.bluetooth.genericattributeprofile).
public class Adapter
{
private readonly BluetoothLEAdvertisementWatcher _bleWatcher = new BluetoothLEAdvertisementWatcher();
public Adapter()
{
_bleWatcher.Received += BleWatcherOnReceived;
}
private void BleWatcherOnReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
// how to connect?
// I know, it's the wrong place to to this, but this is just an example
}
public void StartScanningForDevices(Guid[] serviceUuids)
{
_blewatcher.advertisementfilter.advertisement.serviceuuids.clear();
foreach (var uuid in serviceuuids)
{
_blewatcher.advertisementfilter.advertisement.serviceuuids.add(uuid);
}
_blewatcher.start();
}
}
I've found Samples that are using DeviceInformation.FindAllAsync instead of BluetoothLEAdvertisementWatcher but these are not working / finding any device.
UPDATE
After digging around some time, I found the following way. But unfortunately, the pairing fails. The device is just an Arduino with a BLE shield. I can definitely connect with Android and iOS. So it must be possible with UWP somehow. :/
private void BleWatcherOnReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
var dev = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
// dev.DeviceInformation.Pairing.CanPair is true
// dpr.Status is Failed
DevicePairingResult dpr = await dev.DeviceInformation.Pairing.PairAsync(DevicePairingProtectionLevel.None);
var service = await GattDeviceService.FromIdAsync(dev.DeviceInformation.Id);
}
UPDATE #2
I'm now able to discover and pair (unstable, but ok for now), but
var service = await GattDeviceService.FromIdAsync(args.Id);
throws the following Exception
System.IO.FileNotFoundException:
The system cannot find the file specified. (Exception from HRESULT: 0x80070002)
I have no clue why.
UPDATE 04/17 - CREATORS UPDATE
Microsoft have just updated their Bluetooth APIs. We now have unpaired BLE device communication!
They have very little documentation up at the moment but here is the much simplified new structure:
BleWatcher = new BluetoothLEAdvertisementWatcher
{
ScanningMode = BluetoothLEScanningMode.Active
};
BleWatcher.Start();
BleWatcher.Received += async (w, btAdv) => {
var device = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress);
Debug.WriteLine($"BLEWATCHER Found: {device.name}");
// SERVICES!!
var gatt = await device.GetGattServicesAsync();
Debug.WriteLine($"{device.Name} Services: {gatt.Services.Count}, {gatt.Status}, {gatt.ProtocolError}");
// CHARACTERISTICS!!
var characs = await gatt.Services.Single(s => s.Uuid == SAMPLESERVICEUUID).GetCharacteristicsAsync();
var charac = characs.Single(c => c.Uuid == SAMPLECHARACUUID);
await charac.WriteValueAsync(SOMEDATA);
};
Much better now. As I said there is next to no documentation at the moment, I have a weird issue where my ValueChanged callback stops being called after 30 seconds or so, though that seems to be a separate scoping issue.
UPDATE 2 - SOME WEIRDNESS
After some more playing around with the new creators update there are a few more things to consider when building BLE apps.
You no longer need to run the Bluetooth stuff on the UI thread. There doesn't seem to be any permissions windows for BLE without pairing so no longer necessary to run on UI thread.
You may find that your application stops receiving updates from the device after a period of time. This is a scoping issue where objects are being disposed of that shouldn't. In the code above if you were listening to ValueChanged on the charac you may hit this issue. This is because the GattCharacteristic is disposed of before it should be, set the characteristic as a global rather than relying on it being copied in.
Disconnecting seems to be a bit broken. Quitting an app does not terminate connections. As such make sure you use the App.xml.cs OnSuspended callback to terminate your connections. Otherwise you get in a bit of a weird state where Windows seems to maintain (and keep reading!!) the BLE connection.
Well it has its quirks but it works!
OLD ANSWER
Following on from Jason's correct answer about devices needing to be paired to have their services be discovered, here is some sample code to address this:
private void SetupBluetooth()
{
Watcher = new BluetoothLEAdvertisementWatcher { ScanningMode = BluetoothLEScanningMode.Active };
Watcher.Received += DeviceFound;
DeviceWatcher = DeviceInformation.CreateWatcher();
DeviceWatcher.Added += DeviceAdded;
DeviceWatcher.Updated += DeviceUpdated;
StartScanning();
}
private void StartScanning()
{
Watcher.Start();
DeviceWatcher.Start();
}
private void StopScanning()
{
Watcher.Stop();
DeviceWatcher.Stop();
}
private async void DeviceFound(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs btAdv)
{
if (_devices.Contains(btAdv.Advertisement.LocalName))
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () =>
{
Debug.WriteLine($"---------------------- {btAdv.Advertisement.LocalName} ----------------------");
Debug.WriteLine($"Advertisement Data: {btAdv.Advertisement.ServiceUuids.Count}");
var device = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress);
var result = await device.DeviceInformation.Pairing.PairAsync(DevicePairingProtectionLevel.None);
Debug.WriteLine($"Pairing Result: {result.Status}");
Debug.WriteLine($"Connected Data: {device.GattServices.Count}");
});
}
}
private async void DeviceAdded(DeviceWatcher watcher, DeviceInformation device)
{
if (_devices.Contains(device.Name))
{
try
{
var service = await GattDeviceService.FromIdAsync(device.Id);
Debug.WriteLine("Opened Service!!");
}
catch
{
Debug.WriteLine("Failed to open service.");
}
}
}
private void DeviceUpdated(DeviceWatcher watcher, DeviceInformationUpdate update)
{
Debug.WriteLine($"Device updated: {update.Id}");
}
The key things to note here are:
DeviceWatcher needs both Added and Updated properties set to work.
You need to catch the exception FileNotFound which occurs when attempting to interrogate a service which is not paired, or not yet ready.
UPDATE (5/5/16): The "Element Not Found" error issue seems to only happen when the bluetooth settings screen isn't open/scanning. I don't remember that being the case before 10586.218 but I haven't checked. Obviously, not every issue is fixed in the update.
UPDATE (4/29/16): The 10586.218 windows update appears to have fixed the problem of pairing with a device that has never been paired to the machine (or phone) before. The process I've outlined here and Gerard Wilkinson's sample code in his answer should work more consistently now.
If you are lucky enough to get this to work, it requires waiting a considerable amount of time for the driver to install. I've done it by having both BluetoothLEAdvertisementWatcher and a DeviceWatcher running simultaneously.
Save the DeviceInformation from the BluetoothLEDevice that you get from FromBluetoothAddressAsync() then Dispose() the BluetoothLEDevice before initiating pairing. This is important. If you don't, it won't see the Gatt Services after pairing.
Then wait for the DeviceWatcher to see the paired device. It can take minutes but you'll usually get it before the progress bar for device installation (in the Bluetooth control panel) gets to 100%. If FromIdAsync still fails, it usually means there was a driver installation error. You can unpair and then do the pairing process over again. That usually works for me.
It's very unstable, though, and it seems to be dependent on which Bluetooth chipset and driver the machine has. I often get an Element Not Found error with FromBluetoothAddress but if it gets past there, pairing usually works on the first or second try.
PairAsync and UnpairAsync also need to be posted to the UI thread. If it isn't able to pop up a blue dialog asking for authorization, you'll get exceptions. You can use Post() from a saved UI SynchronizationContext or Windows.ApplicationModel.Core.CoreApplication.MainView.Dispatcher.RunAsync() with an async delegate to do this.
I've seen multiple posts from MS employees on the forums saying FromBluetoothAddressAsync() only works for paired devices. This isn't the case but it is buggy and seems to work best if the device has been paired manually at least once in the past.
Gerard Wilkinson's answer is correct. To make life easier, I turned it into an awaitable method using Reactive Extensions (). Any comments are welcome.
So once you found the device using the BluetoothLEAdvertisementWatcher and paired with it, you can use this to enable GATTServices.
private async Task<GattDeviceService> GetGATTServiceAsync(string deviceName)
{
//devicewatcher is abused to trigger connection
var deviceWatcher = DeviceInformation.CreateWatcher(); //trick to enable GATT
var addedSource = Observable.FromEventPattern(deviceWatcher, nameof(deviceWatcher.Added))
.Select(pattern => ((DeviceInformation)pattern.EventArgs));
var updatedSource = Observable.FromEventPattern(deviceWatcher, nameof(deviceWatcher.Updated))
.Select(pattern =>
{
var update = ((DeviceInformationUpdate)pattern.EventArgs);
return Observable.FromAsync(() => DeviceInformation.CreateFromIdAsync(update.Id).AsTask());
}).Concat();
var source = addedSource.Merge(updatedSource);
source.Publish().Connect(); //make sure the event handlers are attached before starting the device watcher
deviceWatcher.Start();
var result = await source.Where(di => di.Name == deviceName) //find the relevant device
.Select(di => Observable.FromAsync(() => GattDeviceService.FromIdAsync(di.Id).AsTask())) //get all services from the device
.Concat() //necessary because of the async method in the previous statement
.Where(service => service.Uuid == SERVICE_UUID) //get the service with the right UUID
.Retry() //GattDeviceService.FromIdAsync can throw exceptions
.FirstAsync();
deviceWatcher.Stop();
return result;
}
Basically you have the answer partly included in the questions. In essence you use the BluetoothLEAdvertisementWatcher for finding the devices only, basically they work just as beacons.
And you are not suppose to connect these devices by only using this API. To connect the devices you must use DeviceInformation.FindAllAsync(), and to get it to show you any devices, you do need to pair them first.
Anyway, if you are interested on getting data from some specific BLE characteristics, you could try using GattCharacteristicNotificationTrigger, for full example & a bit of additional explanations see my blog.
I am trying to access application history from C#. I would like to present same information as in task manager, but I cannot find api/example. Of course I implement a desktop application.
To specify problem: I am not interested on CPU/RAM. The only thing I would like to get is Time.
UPDATE
To exactly show what I am talking about look at this screen:
There is no supported way to get this information. Sorry.
The description of the questions lacks details that you are talking about Metro Apps. The solution would be an equivalent of Process.TotalProcessorTime but for Metro Apps.
Unfortunately, you cannot get that information because as you stated the applications are running within another process (WWAHost.exe). The design of Metro is a bit more like the phone - you can't directly manipulate the system in the same way. There isn't a directly "Process" in the Windows Runtime (see here), which in turn means the Process class wouldn't make sense to port...
I assume by The only thing I would like to get is Time. you mean the start and end time of each process
The best way will be that you will have to build that history your self. If you need the history only when your application is running then you just implement the code bellow otherwise if you wish to build the history for a certain period even when your application is down then try to create a Windows service that does the job for you. Following are the steps you need to do assuming you know how to create a windows service project, compile and install it:
Getting running processes info :
using System;
using System.Diagnostics;
public static class ProcessStart
{
Process[] runningProcesses;
var processesStartTimes = new Dictionary<int, Datetime>();
var processesExitTimes = new Dictionary<int, Datetime>();
static ProcessStart()
{
// This will get current running processes
runningProcesses = Process.GetProcesses();
foreach (var p in processes)
{
p.Exited += new EventHandler(ProcessExited);
processesStartTimes.Add(p.Id, p.StartTime);
}
}
private void ProcessExited(object sender, System.EventArgs e)
{
var p = (Process)sender;
processesExitTimes.Add(p.Id, p.ExitTime);
}
}
Getting new started processes
You need to create a new Timer object and run it every second checking for new created processes. I copied the code above and extended it :
public static class ProcessStart
{
Process[] runningProcesses;
var processesStartTimes = new Dictionary<int, Datetime>();
var processesExitTimes = new Dictionary<int, Datetime>();
var updateTimer = new Timer(1000);
static ProcessStart()
{
// This will get current running processes
runningProcesses = Process.GetProcesses();
foreach (var p in processes)
{
p.Exited += new EventHandler(ProcessExited);
processesStartTimes.Add(p.Id, p.StartTime);
}
updateTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
}
private void ProcessExited(object sender, System.EventArgs e)
{
var p = (Process)sender;
processesExitTimes.Add(p.Id, p.ExitTime);
}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
runningProcesses = Process.GetProcesses();
foreach (var p in processes)
{
// This will only add the processes that are not added yet
if (!processesStartTimes.Keys.Contains(p.Id))
{
p.Exited += new EventHandler(ProcessExited);
processesStartTimes.Add(p.Id, p.StartTime);
}
}
}
}
Finally :
You just then need to play with the System.Diagnostics.Process class. That should be your best way for getting all information you need about windows processes. It will grant you access to a wide range of properties and methods. Here is the official MSDN lik for it:
Process Class
EDIT :
As I can see from your comment, you are interested in the time the CPU have spent running a specific process. The same System.Diagnostics.Process class contains that info as well. That can be found using the Process.PrivilegedProcessorTime property. The MSDN description for this property is :
A TimeSpan that indicates the amount of time that the process has spent running code inside the operating system core.
More information Here
EDIT 2:
The following 2 Properties could also give you probably what you need:
Process.UserProcessorTime : Here
A TimeSpan that indicates the amount of time that the associated process has spent running code inside the application portion of the process (not inside the operating system core).
Process.TotalProcessorTime : Here
A TimeSpan that indicates the amount of time that the associated process has spent utilizing the CPU. This value is the sum of the UserProcessorTime and the PrivilegedProcessorTime.
I've done some research on this, because I found it to be interesting how the metro apps integrate into the existing process structure.
As other people have pointed out here, it is not possible in a beautiful supported API way to get the info you want.
However, every app is launched as a different process and for processes there are known ways to get run time, cpu time etc.
If you find the correspondig process to your windows 8 app, you will be able to get the info you need.
Some apps are really easy to spot. They are just a executable like any other desktop application with a proper processname and description.
For example in this picture "Map", "Windows Reader" and "Microsoft Camera Application":
Some other apps are a bit harder to spot. They are hosted in a process that is launched by the wwahost.exe executabe. I could only spot the app name by looking at the module list.
This is the Bing app:
To enumerate the modules and mapped files from a process you can use the WinAPIs VirtualQueryEx and GetMappedFileName.
Then you want to look for files that are located at %programfiles%\WindowsApps. This is the folder where windows saves all the application files for it's apps.
Every app has a .pri file. This file contains binary metadata of the application. You could use it to find the name of the app from the process that is hosting it. In the same folder with the .pri file there is a AppxManifest.xml in which you could find all kinds of additional information about the app.
I will try to summarize my writing a bit:
enumerate processes with WinAPI and get all infos you need (run time, cpu time, etc).
enumerate mapped files of each process and look for ressources.pri. The directory where this file is located has the app name in it.
as bonus read the AppxManifest.xml file for additional info on the app. You might need Administrator privilege or additional privileges to read this file.