Getting AccessDenied when enumerating BLE characteristics - c#

I am trying to convert a working UWP app to a WPF one using .NET 6 with Target framework monikers.
I can scan BLE devices with no issues and connect to them, however I get an AccessDenied status at the next GetCharacteristicsAsync's call
I have checked characteristics properties and they all have the flag corresponding to the operation I am doing.
My OS version is 1943 and my target version is net6.0-windows10.0.19041.0
I have tried using BLuetoothCacheMode.Cached and Uncached: same result.
Edit:I have tried to call RequestAccessAsync and it does not seem to do anything. However this same method returns Allowed.
Here is a piece of code I use, it is inpired by the microsoft docs.
public async Task<byte[]> ReadAsync(string serviceUuid, string characteristicUuid)
{
using var bluetoothLEDevice = await BluetoothLEDevice.FromIdAsync(_device.DeviceId);
GattDeviceServicesResult result1 = await bluetoothLEDevice.GetGattServicesAsync(BluetoothCacheMode.Cached);
if (result1.Status == GattCommunicationStatus.Success)
{
foreach (var service in result1.Services)
{
if (service.Uuid.ToString() == serviceUuid)
{
GattCharacteristicsResult result2 = await service.GetCharacteristicsAsync(BluetoothCacheMode.Cached);
if (result2.Status == GattCommunicationStatus.Success)
{
foreach (var characteristic in result2.Characteristics)
{
if (characteristic.Uuid.ToString() == characteristicUuid)
{
var result = await characteristic.ReadValueAsync();
if (result.Status == GattCommunicationStatus.Success)
{
var reader = DataReader.FromBuffer(result.Value);
byte[] contents = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(contents);
return contents;
}
else
{
throw new Exception("Read: Cannot read characteristic: " + characteristic + " " + Enum.GetName(typeof(GattCommunicationStatus), result.Status));
}
}
}
}
else
{
// This exception is thrown
throw new Exception("Read: Cannot get characteristics for service: " + service.Uuid + " " + Enum.GetName(typeof(GattCommunicationStatus), result2.Status));
}
}
}
}
bluetoothLEDevice.Dispose();
return null;
}

Related

Error 0x87DD0005 when implementing Xbox Live services

I just got done adding Xbox support code to my project, and have run into at least two issues.
The first involves save data sync which is working just fine, however when the game reads the user's login data on Windows it behaves as if login has not been completed - no gamertag is displayed in the corner, and the login provider throws error 0x87DD0005 regardless of the number of retry attempts.
Execution of the code is just fine on Xbox - only Windows seems to be affected by this. I'm also targeting the creator's showcase initially (or at least until I can get to where I'm ready for another run at ID#Xbox) so achievements and the like aren't a concern right now.
The following is the code I'm using (and in no particular order):
public void doStartup()
{
getData(-1);
for (int i = 0; i <= 5; i++)
{
getData(i);
}
ContentViewport.Source = new Uri("ms-appx-web:///logo.html");
}
public async void getData(int savefileId)
{
var users = await Windows.System.User.FindAllAsync();
string c_saveBlobName = "Advent";
//string c_saveContainerDisplayName = "GameSave";
string c_saveContainerName = "file" + savefileId;
if (savefileId == -1) c_saveContainerName = "config";
if (savefileId == 0) c_saveContainerName = "global";
GameSaveProvider gameSaveProvider;
GameSaveProviderGetResult gameSaveTask = await GameSaveProvider.GetForUserAsync(users[0], "00000000-0000-0000-0000-00006d0be05f");
//Parameters
//Windows.System.User user
//string SCID
if (gameSaveTask.Status == GameSaveErrorStatus.Ok)
{
gameSaveProvider = gameSaveTask.Value;
}
else
{
return;
//throw new Exception("Game Save Provider Initialization failed");;
}
//Now you have a GameSaveProvider
//Next you need to call CreateContainer to get a GameSaveContainer
GameSaveContainer gameSaveContainer = gameSaveProvider.CreateContainer(c_saveContainerName);
//Parameter
//string name (name of the GameSaveContainer Created)
//form an array of strings containing the blob names you would like to read.
string[] blobsToRead = new string[] { c_saveBlobName };
// GetAsync allocates a new Dictionary to hold the retrieved data. You can also use ReadAsync
// to provide your own preallocated Dictionary.
GameSaveBlobGetResult result = await gameSaveContainer.GetAsync(blobsToRead);
string loadedData = "";
//Check status to make sure data was read from the container
if (result.Status == GameSaveErrorStatus.Ok)
{
//prepare a buffer to receive blob
IBuffer loadedBuffer;
//retrieve the named blob from the GetAsync result, place it in loaded buffer.
result.Value.TryGetValue(c_saveBlobName, out loadedBuffer);
if (loadedBuffer == null)
{
//throw new Exception(String.Format("Didn't find expected blob \"{0}\" in the loaded data.", c_saveBlobName));
}
DataReader reader = DataReader.FromBuffer(loadedBuffer);
loadedData = reader.ReadString(loadedBuffer.Length);
if (savefileId == -1)
{
try
{
System.IO.File.WriteAllText(ApplicationData.Current.TemporaryFolder.Path + "\\config.json", loadedData);
}
catch { }
}
else if (savefileId == 0)
{
try
{
System.IO.File.WriteAllText(ApplicationData.Current.TemporaryFolder.Path + "\\global.json", loadedData);
}
catch { }
}
else
{
try
{
System.IO.File.WriteAllText(ApplicationData.Current.TemporaryFolder.Path + "\\file" + savefileId + ".json", loadedData);
}
catch { }
}
}
}
public async void InitializeXboxGamer()
{
try
{
XboxLiveUser user = new XboxLiveUser();
if (user.IsSignedIn == false)
{
SignInResult result = await user.SignInSilentlyAsync(Window.Current.Dispatcher);
if (result.Status == SignInStatus.UserInteractionRequired)
{
result = await user.SignInAsync(Window.Current.Dispatcher);
}
}
System.IO.File.WriteAllText(ApplicationData.Current.TemporaryFolder.Path + "\\curUser.txt", user.Gamertag);
doStartup();
}
catch (Exception ex)
{
// TODO: log an error here
}
}
I finally managed to figure out why the Xbox was working but Windows was not: it was a platform support issue. In the game's creator's dashboard for Xbox Live there's a settings window that allows the support of the game to be determined. Because I originally had separate builds for Xbox and Windows, only the Xbox support item was checked, so I went ahead and also checked off for Desktop support. After saving the changes, I resubmitted with the new configuration and now it works properly.

Can I program a hold point into an Xbox game that waits for a user's cloud data sync to complete?

Is it possible to program a startup hold into an Xbox game package that waits for the user's cloud saves to sync before continuing? I have an HTML5 game package for which I require save data to be loaded from Xbox Live which is then read from local storage (in JSON format) to populate the load screen at startup, and I also have to account for any error messages for which the user may have to respond. The game itself is started up once sync is done (but obviously not until after the legal stuff has been taken care of, so logo splash, engine branding, seizure advisory and possibly also the FBI and ESRB notices also have to be taken into account). If it also helps to investigate, data is being saved properly while there's an active session and I can successfully copy it to local app storage. The Xbox Live copy, on the other hand, seems to be at issue right now.
I'm not holding that much data in the game files, either - just five save slots not including global config and user settings. Basically I'm trying to keep the per-user storage well under the 64MB cap on creator's projects (not to mention that my initial request for ID#Xbox didn't work out) and I'm hoping to have this implemented before I even dare to resubmit. This is provided that it's even possible of course, considering all of the conflicting information I've been receiving through my Bing and Google requests.
The closest I have come is with the following code, which is based largely on Microsoft Docs samples. This first block is what's supposed to load the Xbox data and copy it to disk, and for which I require the delay in the first place:
public void doStartup()
{
getData(-1);
for (int i = 0; i <= 5; i++)
{
getData(i);
}
}
public async void getData(int savefileId)
{
var users = await Windows.System.User.FindAllAsync();
string c_saveBlobName = "Advent";
//string c_saveContainerDisplayName = "GameSave";
string c_saveContainerName = "file" + savefileId;
if (savefileId <= 0) c_saveContainerName = "config";
if (savefileId == 0) c_saveContainerName = "global";
GameSaveProvider gameSaveProvider;
GameSaveProviderGetResult gameSaveTask = await GameSaveProvider.GetForUserAsync(users[0], "00000000-0000-0000-0000-00006d0be05f");
//Parameters
//Windows.System.User user
//string SCID
if (gameSaveTask.Status == GameSaveErrorStatus.Ok)
{
gameSaveProvider = gameSaveTask.Value;
}
else
{
return;
//throw new Exception("Game Save Provider Initialization failed");;
}
//Now you have a GameSaveProvider
//Next you need to call CreateContainer to get a GameSaveContainer
GameSaveContainer gameSaveContainer = gameSaveProvider.CreateContainer(c_saveContainerName);
//Parameter
//string name (name of the GameSaveContainer Created)
//form an array of strings containing the blob names you would like to read.
string[] blobsToRead = new string[] { c_saveBlobName };
// GetAsync allocates a new Dictionary to hold the retrieved data. You can also use ReadAsync
// to provide your own preallocated Dictionary.
GameSaveBlobGetResult result = await gameSaveContainer.GetAsync(blobsToRead);
string loadedData = "";
//Check status to make sure data was read from the container
if (result.Status == GameSaveErrorStatus.Ok)
{
//prepare a buffer to receive blob
IBuffer loadedBuffer;
//retrieve the named blob from the GetAsync result, place it in loaded buffer.
result.Value.TryGetValue(c_saveBlobName, out loadedBuffer);
if (loadedBuffer == null)
{
//throw new Exception(String.Format("Didn't find expected blob \"{0}\" in the loaded data.", c_saveBlobName));
}
DataReader reader = DataReader.FromBuffer(loadedBuffer);
loadedData = reader.ReadString(loadedBuffer.Length);
if (savefileId <= 0)
{
try
{
System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\config.json", loadedData);
}
catch { }
}
else if (savefileId == 0)
{
try
{
System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\global.json", loadedData);
}
catch { }
}
else
{
try
{
System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\file"+savefileId+".json", loadedData);
}
catch { }
}
}
}
And this is what's supposed to read the data when called by the HTML5 portion:
public string getSaveFile(int savefileId)
{
string data;
if (savefileId == 0)
{
try
{
data = System.IO.File.ReadAllText(ApplicationData.Current.LocalFolder.Path + "\\global.json");
if (data == null) data = "";
Debug.WriteLine(data);
}
catch { data = ""; }
return data;
} else
{
try
{
data = System.IO.File.ReadAllText(ApplicationData.Current.LocalFolder.Path + "\\file" + savefileId + ".json");
if (data == null) data = "";
Debug.WriteLine(data);
}
catch { data = ""; }
return data;
}
}
public string getConfig()
{
string data;
try
{
data = System.IO.File.ReadAllText(ApplicationData.Current.LocalFolder.Path + "\\config.json");
if (data == null) data = "";
Debug.WriteLine(data);
}
catch { data = ""; }
return data;
}
And this is the save code on the WinRT side:
public async void doSave(int key, string data)
{
//Get The User
var users = await Windows.System.User.FindAllAsync();
string c_saveBlobName = "Advent";
string c_saveContainerDisplayName = "GameSave";
string c_saveContainerName = "file"+key;
if (key == -1) c_saveContainerName = "config";
if (key == 0) c_saveContainerName = "global";
GameSaveProvider gameSaveProvider;
GameSaveProviderGetResult gameSaveTask = await GameSaveProvider.GetForUserAsync(users[0], "00000000-0000-0000-0000-00006d0be05f");
//Parameters
//Windows.System.User user
//string SCID
if (gameSaveTask.Status == GameSaveErrorStatus.Ok)
{
gameSaveProvider = gameSaveTask.Value;
}
else
{
return;
//throw new Exception("Game Save Provider Initialization failed");
}
//Now you have a GameSaveProvider (formerly ConnectedStorageSpace)
//Next you need to call CreateContainer to get a GameSaveContainer (formerly ConnectedStorageContainer)
GameSaveContainer gameSaveContainer = gameSaveProvider.CreateContainer(c_saveContainerName); // this will create a new named game save container with the name = to the input name
//Parameter
//string name
// To store a value in the container, it needs to be written into a buffer, then stored with
// a blob name in a Dictionary.
DataWriter writer = new DataWriter();
writer.WriteString(data); //some number you want to save, in this case 23.
IBuffer dataBuffer = writer.DetachBuffer();
var blobsToWrite = new Dictionary<string, IBuffer>();
blobsToWrite.Add(c_saveBlobName, dataBuffer);
GameSaveOperationResult gameSaveOperationResult = await gameSaveContainer.SubmitUpdatesAsync(blobsToWrite, null, c_saveContainerDisplayName);
int i;
for (i = 1; i <= 90000; i++) {}
Debug.WriteLine("SaveProcessed");
//IReadOnlyDictionary<String, IBuffer> blobsToWrite
//IEnumerable<string> blobsToDelete
//string displayName
}
And I have just recently added the user detection code to the main project.
public static async void InitializeXboxGamer(TextBlock gamerTagTextBlock)
{
try
{
XboxLiveUser user = new XboxLiveUser();
SignInResult result = await user.SignInSilentlyAsync(Window.Current.Dispatcher);
if (result.Status == SignInStatus.UserInteractionRequired)
{
result = await user.SignInAsync(Window.Current.Dispatcher);
}
System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\curUser.txt", user.Gamertag);
}
catch (Exception ex)
{
// TODO: log an error here
}
}
Big dumb... I figured out the problem with my code. When loading the global config, getData is passed a negative one; however for some reason it was writing the app data file instead so after I changed the verification to be a little more specific it started to work as expected.
public void doStartup()
{
getData(-1);
for (int i = 0; i <= 5; i++)
{
getData(i);
}
}
public async void getData(int savefileId)
{
var users = await Windows.System.User.FindAllAsync();
string c_saveBlobName = "Advent";
//string c_saveContainerDisplayName = "GameSave";
string c_saveContainerName = "file" + savefileId;
if (savefileId <= 0) c_saveContainerName = "config";
if (savefileId == 0) c_saveContainerName = "global";
GameSaveProvider gameSaveProvider;
GameSaveProviderGetResult gameSaveTask = await GameSaveProvider.GetForUserAsync(users[0], "00000000-0000-0000-0000-00006d0be05f");
//Parameters
//Windows.System.User user
//string SCID
if (gameSaveTask.Status == GameSaveErrorStatus.Ok)
{
gameSaveProvider = gameSaveTask.Value;
}
else
{
return;
//throw new Exception("Game Save Provider Initialization failed");;
}
//Now you have a GameSaveProvider
//Next you need to call CreateContainer to get a GameSaveContainer
GameSaveContainer gameSaveContainer = gameSaveProvider.CreateContainer(c_saveContainerName);
//Parameter
//string name (name of the GameSaveContainer Created)
//form an array of strings containing the blob names you would like to read.
string[] blobsToRead = new string[] { c_saveBlobName };
// GetAsync allocates a new Dictionary to hold the retrieved data. You can also use ReadAsync
// to provide your own preallocated Dictionary.
GameSaveBlobGetResult result = await gameSaveContainer.GetAsync(blobsToRead);
string loadedData = "";
//Check status to make sure data was read from the container
if (result.Status == GameSaveErrorStatus.Ok)
{
//prepare a buffer to receive blob
IBuffer loadedBuffer;
//retrieve the named blob from the GetAsync result, place it in loaded buffer.
result.Value.TryGetValue(c_saveBlobName, out loadedBuffer);
if (loadedBuffer == null)
{
//throw new Exception(String.Format("Didn't find expected blob \"{0}\" in the loaded data.", c_saveBlobName));
}
DataReader reader = DataReader.FromBuffer(loadedBuffer);
loadedData = reader.ReadString(loadedBuffer.Length);
if (savefileId <= 0)
{
try
{
System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\config.json", loadedData);
}
catch { }
}
else if (savefileId == 0)
{
try
{
System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\global.json", loadedData);
}
catch { }
}
else
{
try
{
System.IO.File.WriteAllText(ApplicationData.Current.LocalFolder.Path + "\\file"+savefileId+".json", loadedData);
}
catch { }
}
}
}
I still need the execution delay though, but that's for an entirely different reason that I discuss over here.

UWP app crashes (but not all the time) during function execution

I'm working on a proof of concept at the moment, just for fun (and for YouTube). The thing I am trying to prove is that I can efficiently "hack" WiFi passwords using UWP and C# for Windows. I don't know of any Wi-Fi cracking tools that are designed specifically for Windows 10 devices (PC, Tablet, XboxOne, Mobile etc)...
So I have actually managed to perform a dictionary style attack (on my own WiFi network of course). However my function seems to completely crash occasionally when running the "hack".
Please consider the fact that this is completely white hat hacking I am talking about here, nothing illegal is intended.
Any help with a reason why this crashes is appreciated...
private async void connectWiFi_Tapped(object sender, TappedRoutedEventArgs e)
{
int success = 0;
var picker = new FileOpenPicker();
picker.ViewMode = PickerViewMode.Thumbnail;
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add(".txt");
StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
do
{
string _line;
using (var inputStream = await file.OpenReadAsync())
using (var classicStream = inputStream.AsStreamForRead())
using (var streamReader = new StreamReader(classicStream))
{
while (streamReader.Peek() >= 0)
{
if (success == 0)
{
_line = streamReader.ReadLine();
setConnectionStatus("Status: Checking WiFi network using passphrase " + _line);
if (await checkWifiPassword(_line) == true)
{
success = 1;
setConnectionStatus("SUCCESS: Password successfully identified as " + _line);
firstAdapter.Disconnect();
var msg = new MessageDialog(connectionStatus.Text);
await msg.ShowAsync();
}
else
{
success = 0;
setConnectionStatus("FAIL: Password " + _line + "is incorrect. Checking next password...");
}
}
}
}
} while (success == 0);
}
}
This is the code that actually runs a dictionary-style "hack" on a selected network. The code to actually connect to the network is as follows:
private async Task<bool> checkWifiPassword(string passPhrase)
{
var credential = new PasswordCredential();
WiFiReconnectionKind reconnectionKind = WiFiReconnectionKind.Manual;
credential.Password = passPhrase;
var selectedNetwork = null as WiFiNetworkDisplay;
foreach (var network in ResultCollection)
{
if (WifiNetworks.SelectedItem.ToString() == network.Ssid)
{
selectedNetwork = network as WiFiNetworkDisplay;
}
}
if (selectedNetwork != null)
{
var result = await firstAdapter.ConnectAsync(selectedNetwork.AvailableNetwork, reconnectionKind, credential);
if (result.ConnectionStatus == WiFiConnectionStatus.Success)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
Does anyone have any idea what I am missing here?
Any help appreciated.
Thanks
Consider this loop
while (streamReader.Peek() >= 0)
{
if (success == 0)
{
_line = streamReader.ReadLine();
setConnectionStatus("Status: Checking WiFi network using passphrase " + _line);
if (await checkWifiPassword(_line) == true)
{
success = 1;
setConnectionStatus("SUCCESS: Password successfully identified as " + _line);
firstAdapter.Disconnect();
var msg = new MessageDialog(connectionStatus.Text);
await msg.ShowAsync();
}
else
{
success = 0;
setConnectionStatus("FAIL: Password " + _line + "is incorrect. Checking next password...");
}
}
}
This can lead to and infinite loop:
Imagine the following dictionary file:
abc
bcd
cde
where abc is the correct password.
You peek the stream, you get 97 (decimal ASCII for letter a), fine.
Success is 0, as we just started.
You read the next line.
You check the password, it works, cool.
You set success to 1, show the message, etc.
User closes the message dialog, ShowAsync() returns.
End of first loop iteration, let's start another one.
You peek the stream, you get 98 (ASCII for letter b), non 0, fine.
Success is not zero, so we skip the entire body of that while, end of second loop iteration.
You peek the stream again, the pointer did not move since the last peek, so you're going to get that same 98 again.
And you skip again, infinite loop.
EDIT - there is actually another infinite loop there
I will not detail this that much, but take a look at the outer do-while loop. That loop runs until success. But if the inner loop exhausts all possibilities and does not find a correct password, success will remain 0. That means the do while will run once again, the inner loop will go through the file again, which obviously will not find a solution again, and so on.
Solution
There are many ways that code could be cleaned up, but the quick fix is to break after msg.ShowAsync();.
More details (that would belong to codereview.stackexchange.com):
Also I would not Peek the StreamReader. Use EndOfStream for that job. And you can skip the inner if, you simply break if you found a correct password. You can drop the outer loop as well. If you completed the inner loop without setting the success flag (which in turn should be a boolean) you can report to the user that no password worked.
I would do something along the lines of: (take it as a pseudo code, might not compile as it is)
private async void connectWiFi_Tapped(object sender, TappedRoutedEventArgs e)
{
var picker = new FileOpenPicker();
picker.ViewMode = PickerViewMode.Thumbnail;
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add(".txt");
StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
bool success = false;
string _line;
using (var inputStream = await file.OpenReadAsync())
using (var classicStream = inputStream.AsStreamForRead())
using (var streamReader = new StreamReader(classicStream))
{
while (!streamReader.EndOfStream)
{
_line = streamReader.ReadLine();
setConnectionStatus("Status: Checking WiFi network using passphrase " + _line);
if (await checkWifiPassword(_line) == true)
{
success = true;
setConnectionStatus("SUCCESS: Password successfully identified as " + _line);
firstAdapter.Disconnect();
var msg = new MessageDialog(connectionStatus.Text);
await msg.ShowAsync();
break;
}
else
{
setConnectionStatus("FAIL: Password " + _line + "is incorrect. Checking next password...");
}
}
}
if(!success){ /* report to the user*/ }
}

C# how to capture async results?

I am a newbie to C# & am trying to use the Lync SDK to search for a Lync user programmatically to get their status. I am not sure how to pass the results to the webservice response when the async callback gets executed.
This is the code:
Webservice Controller GET endpoint:
// GET: api/Lync/5
public String Get(int id)
{
log.Info("[GET] Search for Lync User: " + id);
Lync lync = new Lync();
////lync.signIn();
Boolean isSignedIn = lync.isUserSignedIn();
if (isSignedIn)
{
log.Info("User is Signed In");
Console.Write("Enter search key : ");
lync.Search("medina");
//lync.Search("medina",
//(lyncContacts) =>
//{
// log.Debug("Search Results Callback fired!");
// log.Info("Results found: " + lyncContacts.Count);
// return lyncContacts.ToString();
//});
//Console.WriteLine(name);
//Console.ReadLine();
return "testUser";
}
else
{
log.Info("User is not Signed In!");
// TODO: Return status 500
return "testUser";
}
//Console.ReadLine();
//return "testUser";
}
The above method calls the business service lync.search(..) which is as follows:
public void Search(string searchKey)
{
List<LyncContact> contactList = new List<LyncContact>();
//List<ContactInformationType> ContactInformationList = new List<ContactInformationType>();
//ContactInformationList.Add(ContactInformationType.Activity);
//ContactInformationList.Add(ContactInformationType.Availability);
// ContactInformationList.Add(ContactInformationType.CapabilityString);
//ContactSubscription contactSubscription = LyncClient.GetClient().ContactManager.CreateSubscription();
Console.WriteLine("Searching for contacts on " + searchKey);
LyncClient.GetClient().ContactManager.BeginSearch(
searchKey,
(ar) =>
{
SearchResults searchResults = LyncClient.GetClient().ContactManager.EndSearch(ar);
if (searchResults.Contacts.Count > 0)
{
log.Info("Search results found: " + searchResults.Contacts.Count);
Console.WriteLine(searchResults.Contacts.Count.ToString() + " found");
foreach (Contact contact in searchResults.Contacts)
{
String displayName = contact.GetContactInformation(ContactInformationType.DisplayName).ToString();
ContactAvailability currentAvailability = (ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability);
Console.WriteLine(
contact.GetContactInformation(ContactInformationType.DisplayName).ToString() + " " + contact.GetContactInformation(ContactInformationType.Availability).ToString());
log.Debug("Display Name: " + displayName);
log.Debug("Availability: " + currentAvailability);
LyncContact lyncContact = new LyncContact.Builder().DisplayName("Snehil").Availability("Busy").build();
contactList.Add(lyncContact);
//done(contactList);
}
return;
}
else
{
log.Info("No Results found!");
//done(contactList);
return;
}
},
null);
} else
{
log.Info("No Results found!");
//done(contactList);
return;
}
},
null);
}
I tried to use Task+await but I am having a hard time trying to figure out how can i return the results from the callback funtion as results from the search method. How do i capture this in the controller class?
If you would like to use the async/await pattern for this use Task.FromAsync https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskfactory.fromasync(v=vs.110).aspx
Google for examples. Like this:
public async Task<List<LyncContact>> SearchAsync(string searchKey)
{
List<LyncContact> contactList = new List<LyncContact>();
Console.WriteLine("Searching for contacts on " + searchKey);
var cm = LyncClient.GetClient().ContactManager;
var searchResults = await Task<SearchResults>.Factory.FromAsync<String>(
cm.BeginSearch,
cm.EndSearch, searchKey, null);
if (searchResults.Contacts.Count > 0)
{
Console.WriteLine(searchResults.Contacts.Count.ToString() + " found");
foreach (Contact contact in searchResults.Contacts)
{
String displayName = contact.GetContactInformation(ContactInformationType.DisplayName).ToString();
ContactAvailability currentAvailability = (ContactAvailability)contact.GetContactInformation(ContactInformationType.Availability);
Console.WriteLine(
contact.GetContactInformation(ContactInformationType.DisplayName).ToString() + " " + contact.GetContactInformation(ContactInformationType.Availability).ToString());
LyncContact lyncContact = new LyncContact.Builder().DisplayName("Snehil").Availability("Busy").build();
contactList.Add(lyncContact);
}
}
return contactList
}
In the controller you then can do:
public async Task<String> Get(int id)
{
log.Info("[GET] Search for Lync User: " + id);
Lync lync = new Lync();
////lync.signIn();
Boolean isSignedIn = lync.isUserSignedIn();
if (isSignedIn)
{
log.Info("User is Signed In");
Console.Write("Enter search key : ");
var lyncContacts = await SearchAsync("medina");
log.Info("Results found: " + lyncContacts.Count);
return lyncContacts.ToString();
}
else
{
log.Info("User is not Signed In!");
// TODO: Return status 500
return "testUser";
}
//Console.ReadLine();
//return "testUser";
}
Or you can use a TaskCompletionSource, see http://blog.stephencleary.com/2012/07/async-interop-with-iasyncresult.html (older post but principle is still valid.
Warning
I cannot compile this since I do not have access to the libraries you use. It should work but there might be some compile errors
What does it do
Callbacks are not a nice model to work with, as you have found out. using Tasks and the async/await model are far easier to work with. Luckily for you and me they have created several methods that can act as a bridge between the old and the new model. Using Task.FromAsync you can transform IAsyncResult methods to an easier to use Task based methodology. Stephen Cleary has written some nice blogs about them. See the post I mentioned earlier.
It looks like, by looking at your commented out code, that you tried to provide some sort of callback to your Search function. That would work, it should be of type Action<Microsoft.Lync.Model.SearchResults>
public void Search(string searchKey, Action<SearchResults> callback)
{
....
LyncClient.GetClient().ContactManager.BeginSearch(
searchKey,
(ar) =>
{
SearchResults searchResults = LyncClient.GetClient().ContactManager.EndSearch(ar);
callback(searchResults);
});
....
}
Then just uncomment the code in your controller:
lync.Search("medina",
(lyncContacts) =>
{
log.Debug("Search Results Callback fired!");
log.Info("Results found: " + lyncContacts.AllResults.Count);
});

Finding Bluetooth Mac address in Windows10 UWP without pairing

I'm trying to write an app that reads all MAC addresses around on Windows 10 IoT. These lines of code is returning all paired devices even if they aren't turn on.
var selector = BluetoothDevice.GetDeviceSelector();
var devices = await DeviceInformation.FindAllAsync(selector);
listBox.Items.Add(devices.Count);
foreach (var device in devices)
{
listBox.Items.Add(device.Id);
}
And I also found this line of code
await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort));
This returning null though. Is there any way to scan for all MAC addresses in a Windows 10 universal app?
You are very close to finding the answer of your question. You might try getting a BluetoothDevice instance from the DeviceId property. You will then be able to get all the specific Bluetooth information including the Bluetooth address
var selector = BluetoothDevice.GetDeviceSelector();
var devices = await DeviceInformation.FindAllAsync(selector);
foreach (var device in devices)
{
var bluetoothDevice = await BluetoothDevice.FromIdAsync(device.Id);
if (bluetoothDevice != null)
{
Debug.WriteLine(bluetoothDevice.BluetoothAddress);
}
Debug.WriteLine(device.Id);
foreach(var property in device.Properties)
{
Debug.WriteLine(" " + property.Key + " " + property.Value);
}
}
There is a new approach using BluetoothLEAdvertisementWatcher to scan for all Bluetooth LE device around.
Here is a piece of code I use in my project:
var advertisementWatcher = new BluetoothLEAdvertisementWatcher()
{
SignalStrengthFilter.InRangeThresholdInDBm = -100,
SignalStrengthFilter.OutOfRangeThresholdInDBm = -102,
SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(2000)
};
advertisementWatcher.Received += AdvertisementWatcher_Received;
advertisementWatcher.Stopped += AdvertisementWatcher_Stopped;
advertisementWatcher.Start();
and later
advertisementWatcher.Stop();
advertisementWatcher.Received -= AdvertisementWatcher_Received;
advertisementWatcher.Stopped -= AdvertisementWatcher_Stopped;
private void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
{
//MessAgeChanged(MsgType.NotifyTxt, "FR_NAME:"+ eventArgs.Advertisement.LocalName + "BT_ADDR: " + eventArgs.BluetoothAddress);
string sDevicename = setup.Default.BLEName.Text;
BluetoothLEDevice.FromBluetoothAddressAsync(eventArgs.BluetoothAddress).Completed = async (asyncInfo, asyncStatus) =>
{
if (asyncStatus == AsyncStatus.Completed)
{
if (asyncInfo.GetResults() == null)
{
if (!FailMsg)
{
MessAgeChanged(MsgType.NotifyTxt, "None");
}
}
else
{
BluetoothLEDevice currentDevice = asyncInfo.GetResults();
Boolean contain = false;
foreach (BluetoothLEDevice device in DeviceList.ToArray())/
{
if (device.DeviceId == currentDevice.DeviceId)
{
contain = true;
}
}
if (!contain)
{
byte[] _Bytes1 = BitConverter.GetBytes(currentDevice.BluetoothAddress);
Array.Reverse(_Bytes1);
// The received signal strength indicator (RSSI)
double rssi = eventArgs.RawSignalStrengthInDBm;
DeviceList.Add(currentDevice);
MessAgeChanged(MsgType.NotifyTxt, currentDevice.Name + " " + BitConverter.ToString(_Bytes1, 2, 6).Replace('-', ':').ToLower() + " " + rssi);
DeviceWatcherChanged(MsgType.BleDevice, currentDevice);
}
}
}
};
}

Categories

Resources