My xamarin.forms app is meant to join a Zoom meeting on a mobile device using Launcher.OpenAsync() with a uri scheme of the form "zoomus://zoom.us/join?confno=1234567890&pwd=123456".
This works fine on Android, but on iOS it doesn't seem to do anything at all. I call Launcher.CanOpenAsync() beforehand, and that returns true, so the uri should be OK. The Zoom app is already installed. In info.plist I have added zoomus (and zoom) to LSApplicationQueriesSchemes.
My code looks like this:
private void RunZoomAsync()
{
Task zoomTask = Task.Run(async () =>
{
if (await Launcher.CanOpenAsync(selectedMedia.Uri))
{
Message = "Launching Zoom";
await Launcher.OpenAsync(selectedMedia.Uri).ConfigureAwait(false);
}
else
{
Message = "Zoom not found. You must install Zoom from your App Store";
}
});
}
I see the message on the screen, so I know it's getting to the right bit of code.
I tried sending the same link to the iPhone in an email and that does nothing either. (I tried that on the Android phone, and the email app wouldn’t even display the link as a hyperlink). Is there some setting on the iPhone, or in my app, to allow deep linking?
As you may have guessed, I am not normally an iPhone user. I’m using an old iPhone 6 for testing, running iOS 12.4.8.
I have sought help from the Zoom developer forum, who suggested that when using url schemes in iOS, there is an AppDelegate function that needs to be overridden:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool
I'm guessing the above is not C#, and that the Essentials Launcher class deals with whatever is required in iOS.
Am I missing something, or is this a bug? Any help gratefully appreciated.
I tried sending the same link to the iPhone in an email and that does
nothing either. (I tried that on the Android phone, and the email app
wouldn’t even display the link as a hyperlink). Is there some setting
on the iPhone, or in my app, to allow deep linking?
This needs to work if you have installed the app and if you are using the proper protocol and if your hardware and operating system is working properly. Your problem is not caused by your code (well unless you are using the wrong protocol), so this is the answer for this site as it doesn't deal with non-coding problems.
Why your Zoom app is not working as expected - you should start with the Zoom customer support as they are in the best position to give you the answer if it exists, but it is extremely likely that your device is faulty in some way.
My problem was that I wasn't running the Launcher in the main thread, so (I assume) the Zoom app didn't have access to the screen. So I just changed:
Task zoomTask = Task.Run(async () =>
to:
MainThread.BeginInvokeOnMainThread(async () =>
and it's now working!
I'm developing Xamarin application using Visual Studio 2019. I have to connect to another device through Bluetooth and send some data and receive acknowledgement back. Tried these two samples out
https://github.com/msthrax/BLEApp
and
https://github.com/didourebai/BLEPluginDemo.
But didn't help me while scanning for nearby Bluetooth devices, since the below codes are not giving expected result respectively..
1.
listView_DeviceList.ItemsSource = CrossBluetooth.Adaptor.GetListOfDiscoveredDevices();
and
2.
adapter.DeviceDiscovered += (s, a) =>
{
deviceList.Add(a.Device);
};
Both Bluetooth and location turned on in my devices. Can anyone let me know the possibilities of problem here. I don't have any build errors in both of the above samples. Whereas, I have one surprise also. Below line is giving proper result, which is of no use for me currently.
listView_PairedDeviceList.ItemsSource = CrossBluetooth.Adaptor.GetPairedDevices();
After enabling the Location Permission for the app(earlier, I turned on Device Location, but we should allow the app to get device location), all my above problems are solved out.
I'm trying to write a cross-platform Xamarin Forms application (in C#) to talk to Bluetooth LE devices. I've downloaded a few packages (Plugin.BLE and Acr.Ble) and neither one works (they both scan but won't connect), so I thought I would try using the Android API directly to see if that would help me understand what's failing. BTW, I'm running on a Nexus 7 tablet running Android version 6.0.1.
I'm successfully scanning for devices:
BluetoothManager bluetoothManager = (BluetoothManager)Forms.Context.GetSystemService (Android.Content.Context.BluetoothService);
m_adapter = bluetoothManager.Adapter;
if ((m_adapter == null) || (!m_adapter.IsEnabled))
return false;
m_scanCallback = new BlueCallback (this);
m_adapter.BluetoothLeScanner.StartScan (m_scanCallback);
and I see the device I want to talk to (in this case, a TI development board MSP-EXP430F5438 in Server mode, running their SPPLE demo application). So I stop the scan:
m_adapter.BluetoothLeScanner.StopScan (m_scanCallback);
and then I connect to the desired device:
m_gattCallback = new BlueGattCallback ();
m_gatt = m_selectedDevice.ConnectGatt (Forms.Context, false, m_gattCallback);
and I pretty much immediately get a call back saying the connection failed:
BlueGattCallback.OnConnectionStateChange(gatt, status=133, newState=Disconnected)
I read this Google bug report so in my callback I tried calling Connect() directly in my callback:
if ( ((int)status == 133) && (numRetries < 10) )
{
numRetries++;
bool connect = gatt.Connect ();
Debug.WriteLine (" gatt.Connect() returned " + connect);
}
This code fails with error 133 repeatedly and quite quickly (all 10 retries take about 3 seconds).
Any idea what's going wrong here?
As this is dependant on the BLE stack which each vendor develops,
the error generally occurs on Samsung devices more than any other type, Android 6 being the most unstable.
So for anyone running into the 133 error and having many sleepless nights because of it. I would recommend using the Sweetblue
wrapper, you will however need to wrap the library yourself for use in C#.
It abstracts many of the unstable parts of BLE and provides good retry mechanisms as well as graceful degradation in certain cases.
But in the end this does not solve all problems and you will need to handle some of the instability yourself.
I'm trying to open a serial port in a Universal app following the instructions found in https://ms-iot.github.io/content/en-US/win10/samples/SerialSample.htm
My app has the correct DeviceCapability in Package.appxmanifest and I can enumerate correctly all the devices and all of them are found when calling to:
var selector = SerialDevice.GetDeviceSelector();
var devices = await DeviceInformation.FindAllAsync(selector);
The problem arise when I try to open any of the serial ports. The call to SerialDevice.FromIdAsync always returns null.
var serialPort = await SerialDevice.FromIdAsync(devices[0].Id);
Then, as explained in https://channel9.msdn.com/events/Build/2015/3-81, I try to analyze the problem calling to DeviceAccessInformation.CreateFromDeviceId and the surprise is that this call throws a FileNotFoundException.
The string I'm passing to DeviceAccessInformation.CreateFromDeviceId is:
"\\?\ROOT#UBLOXVCP#0000#{86e0d1e0-8089-11d0-9ce4-08003e301f73}"
but it also throws the exception with:
"\\?\ACPI#PNP0501#1#{86e0d1e0-8089-11d0-9ce4-08003e301f73}"
both of them throws the FileNotFoundException.
In the Channel 9 video, Arvind Aiyar says something like you can reboke the access to the serial ports with the Privacy configuration of Windows 10, but I can't found anything in relationship with Serial Ports in the Privacy configuration.
To test what's happening I tried to disassembly the DeviceAccessInformation.CreateFromDeviceId method with dotPeek and no way to access to the WinRT part so I decided to call to a different API.
If you search {86e0d1e0-8089-11d0-9ce4-08003e301f73} you'll see that it is the device interface class is defined for COM ports, so I called to:
DeviceAccessInformation.CreateFromDeviceClassId(new Guid("{86e0d1e0-8089-11d0-9ce4-08003e301f73}"));
and this method doesn't throws any exception and returns DeviceAccessStatus.Unspecified
I am using Windows 10 Pro x64 version 1511, compilation 10586.71, Visual Studio 2015 Update 1.
Am I doing something wrong?
Is all about permissions?
I am using GsmComm to connect to usb modem. The com ports for the modem I am using doesn't show up in device manager when I initially connect the modem to the computer. Computer shows it as a removable drive. However, when I run the application provided with the modem the com ports show up in device manager.
So every time I want to use the device with my application, I have to first connect it to the pc, run their software to initialize the com ports, and then run my application.
But is there any way to initialize the com ports from my application with C#?
I have read something about creating virtual com ports to connect to usb devices, but I have no idea how to do it. Any help or pointers will be highly appreciated.
Update 14 Feb 2016
I followed antiduh's answer and found that the device is recognized as a cdrom when first connected.
After running their application the link changes to harddiskvolume -
and three new com links are created.
Virtual serial ports are emulated by the device driver that came with the device. If they don't show up in Device Manager until you run their software then it either installs a device driver dynamically or it sends a secret handshake to the driver to tell it to start emulating the port.
The former requires UAC elevation and a .sys file, if you don't see a prompt when you run their software and don't see an installed service that might do it nor that .sys file then you can scratch that possibility. The latter is usually done through a DeviceIoControl() call, the kind you can spy on with a filter driver. Like IoSpy, a utility that is included with the WDK. Using Dumpbin.exe /imports on the utility can provide useful implementation details, as does SysInternals' Process Monitor.
Hardly a guarantee for success, best to ask the manufacturer for the details. They however don't often return the phone call and do not include such details in the manual. They of course prefer anybody to use their shovelware. Keep in mind that you see the curly tail of a pig in a poke, best to cut your losses by returning the device and buying another one from a different manufacturer.
I have a hypothesis.
Have you ever seen the Windows Object Manager? It's a neat little namespace in Windows that is used for wiring and exposing all types of crazy little objects, including devices-as-files. Think of it as Window's horrible version of `/dev'.
Interestingly though, user-space programs can access it by calling CreateFile, using a special prefix.
For instance, one way that you open serial ports in windows is by calling CreateFile(#"\\.\COM3"). This is a path that maps to the Object Manager path \GLOBAL??\COM3.
Here is what the path looks like using WinObj:
In my case, you can see that \GLOBAL??\COM3 is actually wired to \Device\QTUSBSerial0.
If you were to watch WinObj before and after this special software runs, you might find out what target device is symlinked to COMX, and then you might be able to find out if that real device is actually always there.
Naively, I would think it would be possible to path arbitrary Object Manager paths to CreateFile to access objects without having to rely on the \\.\ -to-\GLOBAL??\ mapping. However, it seems there's a hangup - according to this answer, CreateFile will only accept arguments that target the \GLOBAL??\ section of the object manager - it will only accept \\.\ as the path prefix, and won't accept, for instance \\.\Device\QTUSBSerial0 or some similar string.
There's still one possible out: Create a really small device driver / kernel module to create the symlink yourself, using IoCreateSymbolicLink. Write a driver that creates the Object Manager symlink \GLOBAL??\CrazyDevice --> \Device\CrazyDevice, and then use the hacked-up SerialPortNet code to call CreateFile(#"\\.\CrazyDevice").
It's a bit of a stretch, but maybe it'll solve your issue.
Disclaimer: I've never written a Windows device driver or manipulated the Object Manager. I barely know what I'm doing here.
Look its been ages since I have used windows, however, the concept still applies. First of all it seems this usb modem has mode switching, meaning it first identifies it self as a CD-ROM to enable you to acquire the drivers, then it mode switches once the installation finishes to a mass storage device which contains the script that created the 3 COM port symbolic links.
To use this in an application, you need a few things, first is the usb PID and VID to be able to enumerate the device on your computers hub. Second, you need a copy of that script that triggers the symbolic link creation and you need to call that script from your application once the device is detected ( by enumerating for VID and PID) once the script executes, three com ports will appear automatically and you should be able to access them as usual. also you might want to check if the CD-ROM application in the beginning installed dlls (almost certainly it did & almost certainly the second script checks for the dlls before creating the COM port links, so make sure the dlls stay where they are supposed to). you will need to link those with your application as well to gain any extra functionality that those provide (but that opens up pandoras box for native interface, if your not comfortable with that dont do it ... I can do it / show example in Java but not C#) , otherwise, if just using the com port is what you want and you actually know how to talk to the device (AT Commands) then forget about it and open com ports and fire away. last point, you will have to figure out the native interface (not really native interface, just a system call to execute a bash command to run a script/exe, thats all) for C#, so look for a system call function built in the .NET framework.
Let me know if you need any further clarifications on the steps.
UPDATE:
for usb enumeration, you can use a library similar to javas usb4java and implement a function similar to the following
public Device findDevice(short vendorId, short productId)
{
// Read the USB device list
DeviceList list = new DeviceList();// ---> Here is empty device list
int result = LibUsb.getDeviceList(null, list);// ---> Now list is populated
if (result < 0) throw new LibUsbException("Unable to get device list", result);
try
{
// Iterate over all devices and scan for the right one
for (Device device: list)
{
DeviceDescriptor descriptor = new DeviceDescriptor();
result = LibUsb.getDeviceDescriptor(device, descriptor);
if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to read device descriptor", result);
//Match the VID and PID (function inputs)---> if you find a match, then return success/or the device
// you can find the VID and PID from the device manager
if (descriptor.idVendor() == vendorId && descriptor.idProduct() == productId)
return device;
}
}
finally
{
// Ensure the allocated device list is freed
LibUsb.freeDeviceList(list, true);
}
// Device not found
return null;
}
This function allows you to access the usb device directly from the application. you can then start initialization sequence and bulk transfers (most probably will work for a Virtual COM port device, that would be typical for an ftdi chip for example, but since this is an unknown Chines chip, this is as far as we can go at low level, we have to build on whats provided and let windows do the driver dirty work) ...
At this point your program knows for a fact that the usb device is plugged in, if fucntion returns null, then sleep for a second and keep polling till the device is plugged.
from this point, you need to run the script that will create the symbolic links, I will assume its a .exe file. Here is a copy and past for a C# code posted by another member
using System.Diagnostics;
// Prepare the process to run
ProcessStartInfo start = new ProcessStartInfo();
// Enter in the command line arguments, everything you would enter after the executable name itself
start.Arguments = arguments;
// Enter the executable to run, including the complete path
start.FileName = "C:/path/to/your/.exe";
// Do you want to show a console window?
start.WindowStyle = ProcessWindowStyle.Hidden;
start.CreateNoWindow = true;
int exitCode;
// Run the external process & wait for it to finish
using (Process proc = Process.Start(start))
{
proc.WaitForExit();
// Retrieve the app's exit code
exitCode = proc.ExitCode;
}
having the exit code is very useful to indicate whether the script successfully created the symbolic links or not (hopefully the Chines guy who made this followed proper coding practices).
EDIT:
The script might fail. The reason is simple: It doesnt know the ProductID and VendorID to enumerate and perform the initialization on. (99.99999% they didnt recompile a simple initialization script for every single unit just to hard code the pid and vid) so it probably receives the pid and vid as args (best case) or reads from usb mass storage hidden sectors (at which point you might have path issues if your running the script from non-root location)... you might have to gdb to find out if some args are missing specially if the .exe doesnt output anything to stderr
finally at this point you can start looking for a list of COM ports using standard C# libraries using something like :
Code Source
var portNames = SerialPort.GetPortNames();
foreach(var port in portNames) {
//Try for every portName and break on the first working
}
and when you find the port you are looking for you can open it using
Code Source
public static void Main()
{
string name;
string message;
StringComparer stringComparer = StringComparer.OrdinalIgnoreCase;
Thread readThread = new Thread(Read);
// Create a new SerialPort object with default settings.
_serialPort = new SerialPort();
// Allow the user to set the appropriate properties.
_serialPort.PortName = SetPortName(_serialPort.PortName);
_serialPort.BaudRate = SetPortBaudRate(_serialPort.BaudRate);
_serialPort.Parity = SetPortParity(_serialPort.Parity);
_serialPort.DataBits = SetPortDataBits(_serialPort.DataBits);
_serialPort.StopBits = SetPortStopBits(_serialPort.StopBits);
_serialPort.Handshake = SetPortHandshake(_serialPort.Handshake);
// Set the read/write timeouts
_serialPort.ReadTimeout = 500;
_serialPort.WriteTimeout = 500;
_serialPort.Open();
_continue = true;
readThread.Start();
Console.Write("Name: ");
name = Console.ReadLine();
Console.WriteLine("Type QUIT to exit");
while (_continue)
{
message = Console.ReadLine();
if (stringComparer.Equals("quit", message))
{
_continue = false;
}
else
{
_serialPort.WriteLine(
String.Format("<{0}>: {1}", name, message));
}
}
readThread.Join();
_serialPort.Close();
}
public static void Read()
{
while (_continue)
{
try
{
string message = _serialPort.ReadLine();
Console.WriteLine(message);
}
catch (TimeoutException) { }
}
}
Hope that helps get you started !