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 !
Related
The Question
I'm having to work with a rather awkward API at the moment which insists on me giving the address of a device, linked via USB port, in the form COM*. However, on the Ubuntu machine on which I'm working, and have to use, if I plug in this device it will automatically be assigned an address in the form /dev/ttyUSB*.
Given that I can't modify the source code of the API - which I would dearly like to do! - what is the least painful way getting the API to talk to said device?
Extra Detail
An example of how to use the API from the manual:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using com.caen.RFIDLibrary;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
CAENRFIDReader MyReader = new CAENRFIDReader();
MyReader.Connect(CAENRFIDPort.CAENRFID_RS232, "COM3");
CAENRFIDLogicalSource MySource = MyReader.GetSource("Source_0");
CAENRFIDTag[] MyTags = MySource.InventoryTag();
if (MyTags.Length > 0)
{
for (int i = 0; i < MyTags.Length; i++)
{
String s = BitConverter.ToString(MyTags[i].GetId());
Console.WriteLine(s);
}
}
Console.WriteLine("Press a key to end the program.");
Console.ReadKey();
MyReader.Disconnect();
}
}
}
The line MyReader.Connect(CAENRFIDPort.CAENRFID_RS232, "COM3"); is where I'm running into problems.
A little later in the manual, it states that the Connect method is to have two parameters:
ConType: The communication link to use for the connection.
Address: Depending on ConType parameter: IP address for TCP/IP communications ("xxx.xxx.xxx.xxx"), COM port for RS232 communications ("COMx"), an index for USB communications (not yet supported).
Bonus Question
The API in question seems to have been written on the assumption that it would be run on a Windows machine. (It's in C#.) The COM* format seems to be favoured - I'm happy to be corrected on this point - by Windows architectures, whereas Ubuntu seems to favour the ttyUSB* format. Assuming that I can funnel the data from my device from a ttyUSB* port to a COM* port, will the API actually be able to find said data? Or will it incorrectly follow the default Windows path?
Given the new information i suspect you can just give the ttyUSB as the parameter, mono will handle the connection correctly. However the same caution for the line endings below still applies. You might also consider making the parameter a command-line parameter thus making your code run on any platform by being able to supply the COM/USB through the command line parameters. I see no other issues using this code. Did you try it yet?
PS: i think your confusion is actually the statement usb id's are not supported yet, i suspect that is because the library relies on a (text-based) serial connection wich are fundamentally different from direct USB connections (wich drivers normally handle) that handle the connection in a more direct way. The ttyUSB ports on linux however DO represent the (UART) serial connections the same way as windows COM-ports, these are not direct USB connections.
Some handy info about the differences: https://rfc1149.net/blog/2013/03/05/what-is-the-difference-between-devttyusbx-and-devttyacmx/
Old answer
I am assuming you run this program on Mono?
Mono expects the path to the port, so COM* will not do. You could try creating a symlink named COM* to the ttyUSB*. Preferrably located in the environment directory. Once you get them linked the program should see no difference. However line endings in the data/program might be different than on windows. If the device expects CRLF and the program uses Environment.NewLine you might get unexpected behaviour too. It might just be easier if you have the permission/rights to edit the assembly with recompilation tools.
I am taking what was traditionally a single-instance-only application and updating it so multiple instances can execute on the same PC. Each instance will require exclusive access to a USB device. The first application instance opens a USB device with the following command (some Kernel32 wrappers for C# are in place) and gets a valid handle, as expected:
this.handle = Kernel32.CreateFile(
pathToUsbDevice,
Convert.ToUInt32(FileRead.Read | FileRead.Write),
Convert.ToUInt32(FileShare.None),
IntPtr.Zero,
Convert.ToUInt32(CreateDisposition.OpenExisting),
Convert.ToUInt32(FileFlags.Overlapped),
IntPtr.Zero);
The problem is that I then open the second instance of the application and it is able to also get a valid handle to the same device (while the first instance still has its handle). This seems incorrect to me, as my understanding of CreateFile is that it should return an invalid file handle if that device is already opened exclusively (sharing set to 0, or None).
Am I doing something wrong in CreateFile? Perhaps my assumption that it will return an invalid handle if the device is already exclusively opened is incorrect? How can I exclusively open the device for a single application instance so that other application instances cannot open it?
In case it matters, the development & test PC is Windows 7 Professional, 64-bit, building the app via Visual Studio Express 2013.
Looks like it is a device driver issue. I've found a similar discussion:
It's up to the driver to manage shared/non-shared, not the IO manager. So you
have two choices:
Mark your device exclusive in the INF (see the Exclusive registry value under
http://msdn.microsoft.com/en-us/library/windows/hardware/ff546320(v=3Dvs.85).aspx
)
Add file create/close handlers in your driver and manage the count of clients
yourself
You might want to use some other approach to ensure only one instance of your app is accessing device (like using a named global mutex to guard device access).
I'm now trying to write a simple program in C# that sends command to the printer to print a plain text but don't know how to. There are 2 main problems that I'm facing now:
1. How to communicate with the printer?
After doing some google search but not getting a satisfying result I went to Brothers' main page and found there a so-called b-PAC3 SDK.
The b-PAC* Software Development Kit is a software tool for Microsoft® Windows® that allows customized labels to be printed from within your own applications.
After having downloaded and installed it, in the directory where it's installed, I found a folder named "Samples"- there are sample codes written in some different language (VB, VS, VSC, ...) I hoped that these sample codes would work since this SDK and the printer come from the same company. But they didn't. Let me show you one of these samples here: (code in C#)
/*************************************************************************
b-PAC 3.0 Component Sample (RfidRW)
(C)Copyright Brother Industries, Ltd. 2009
*************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleSampleCSharp
{
class Program
{
private const int NOERROR = 0;
private const string ANTENNA_READER_WRITER = "Reader/Writer side";
static void Main(string[] args)
{
// Create Rfid Instance
bpac.RfidClass rfid = new bpac.RfidClass(); // Rfid Instance
string selectedDevice; // selected device
/* GetInstalledDevices */
Console.WriteLine("==GetInstalledDevices()==");
object[] arrDevices = (object[])rfid.GetInstalledDevices();
if (rfid.ErrorCode == NOERROR)
{
Console.WriteLine("Succeed to GetInstalledDevices()");
int index = 0;
foreach (string device in arrDevices)
{
Console.WriteLine(String.Format("[{0}] {1}", index, device));
index++;
}
// select device
Console.WriteLine("Please Select Device");
int selectedDeviceIndex = int.Parse(Console.ReadLine());
selectedDevice = arrDevices[selectedDeviceIndex].ToString();
}
else
{
Console.WriteLine("Failed to GetInstalledDevices()");
goto CleanUp;
}
// ....
}
}
}
When I run this code, the first problem comes out: (it displayed exactly as in quote bellow, sorry, I can't post image due to low reputation):
==GetInstalledDevices()==
Succeed to GetInstalledDevices()
Please Select Device
There wasn't any error but seems like program can't find my device, I don't have any idea why this happens.
2. How to write a QL-style command?
I know that each kind of printer has its own command language so after searching on Brother's site I found a reference:
Brother QL Series
Command Reference
(QL-500/550/560/570/580N/
650TD/700/1050/1060N)
I myself have no experience in working with thermal printer and unfortunately there isn't any sample in this command reference which makes it really difficult for me to figure out how the command should be written.
Has anyone worked with Brother QL serie printers before?
P.S: The printer that I'm using is Brother QL 560.
To communicate with the printer, you need a few things:
Get a USB library, like libusb (http://libusb.info/)
Install a driver that will allow you to access the printer via libusb, like Zadig for example (http://zadig.akeo.ie/)
Download the printer's Command Reference from the Internet ("Brother QL Series Command Reference")
Using the information provided in chapter 7 of the command reference and the samples that come with libusb, make a small routine that will detect and open a communication channel with the printer via USB.
Then, using the rest of the information available in the manual, send a series of ESC commands to the printer to either configure it or print labels.
PS: If you need to improve your background on USB communication, I recommend an excellent reference called "USB in a Nutshell", available at beyondlogic dot org (I can't post more than two links).
I think OPOS (from Microsoft) should be the one of the solutions for your case, provided with Brother QL 560 offering its own opos driver. Once you get the driver (in dll), you can just start developing as easily as using general web controls.
I am developing a C# device application for Motorola MC55 devices (Bluetopia is used). This application sets the bluetooth inquiry and pairing procedure in motion (by means of the Enterprise Mobility Developer Kit) and writes down the virtual serial port #:
public void DoConnection(IRemoteBTDevice rd)
{
RemoteDevice remoteDevice = new RemoteDevice(rd.DeviceName, rd.Id.Replace(":", ""), "");
this.bluetooth.RemoteDevices.Add(remoteDevice);
this.bluetooth.RemoteDevices.Refresh();
if (!remoteDevice.IsPaired)
{
remoteDevice.Pair(rd.Pin);
}
//TODO Note serial port # (get it by means of remoteDevice.LocalComPort)
//Works smoothly, but a second process has to do this (requirement).
remoteDevice.OpenPort();
}
But as soon as a second process (e.g. another c# device application) opens that serial port, BTExplorer is launched. How do I suppress this phenomenon?
this.comX = new SerialPort(this.BluetoothPortName);
this.comX.Open();
I'm just using another Assembly: http://32feet.codeplex.com/. It works smoothly for Motorola ES400 (Microsoft Stack). After seeing this: http://32feet.codeplex.com/wikipage?title=Stonestreet%20One%20Bluetopia, I used the same code, but it doesn't work trouble-free for MC55 yet.
Thank you for your support,
Roger Huber
As I understand it, it is BTExplorer.exe that actually provides/controls the virtual COM port service, so it needs to be running when a virtual COM port is in use...
Do be sure to let know what the issues you see on your MC55 when using my 32feet.NET Bluetopia support. As I noted at 32feet.NET: Stonestreet One Bluetopia I tested on a M3 Mobile device which has a quite recent version of the Bluetopia stack. Maybe StoneStreetOne changed something between your versions... (If Bluetopia start-up fails completely then I've more logging in version 3.3 -- which I'm not too far away from releasing).
Of course if that second C# program needs to use a virtual COM port then 32feet.NET won't help -- BTExplorer is still required as discussed above. However if you could change that program to use BluetoothClient etc instead of COM ports... (Remembering of course the "Bluetopia one at a time" restriction of course).
Alan
I'm working on a Mono application that will run on Linux, Mac, and Windows, and need the ability for apps (on a single os) to send simple string messages to each other.
Specifically, I want a Single Instance Application. If a second instance is attempted to be started, it will instead send a message to the single instance already running.
DBus is out, as I don't want to have that be an additional requirement.
Socket communication seems to be hard, as windows seems to not allow permission to connect.
Memory Mapped Files seems not to be supported in Mono.
Named Pipes appears not to be supported in Mono.
IPC seems not to be supported on Mono.
So, is there a simple method to send string messages on a single machine to a server app that works on each os, without requiring permissions, or additional dependencies?
On my ubuntu (10.10 mono version: 2.6.7) I've tried using WCF for interprocess communication with BasicHttpBinding, NetTcpBinding and NetNamedPipeBinding. First 2 worked fine, for NetNamedPipeBinding I got an error:
Channel type IDuplexSessionChannel is
not supported
when calling ChannelFactory.CreateChannel() method.
I've also tried using Remoting (which is a legacy technology since WCF came out) with IpcChannel; example from this msdn page started and worked without problems on my machine.
I suppose you shouldn't have problems using WCF or Remoting on Windows either, not sure about Mac though, don't have any of those around to test. Let me know if you need any code examples.
hope this helps, regards
I wrote about this on the mono-dev mailing list. Several general-purpose inter-process messaging systems were considered, including DBus, System.Threading.Mutex class, WCF, Remoting, Named Pipes... The conclusions were basically mono doesn't support Mutex class (works for inter-thread, not for inter-process) and there's nothing platform agnostic available.
I have only been able to imagine three possible solutions. All have their drawbacks. Maybe there's a better solution available, or maybe just better solutions for specific purposes, or maybe there exist some cross-platform 3rd party libraries you could include in your app (I don't know.) But these are the best solutions I've been able to find so far:
Open or create a file in a known location, with exclusive lock. (FileShare.None). Each application tries to open the file, do its work, and close the file. If failing to open, Thread.Sleep(1) and try again. This is kind of ghetto, but it works cross-platform to provide inter-process mutex.
Sockets. First application listens on localhost, some high numbered port. Second application attempts to listen on that port, fails to open (because some other process already has it) so second process sends a message to the first process, which is already listening on that port.
If you have access to a transactional database, or message passing system (sqs, rabbitmq, etc) use it.
Of course, you could detect which platform you're on, and then use whatever works on that platform.
Solved my problem with two techniques: a named mutex (so that the app can be run on the same machine by different users), and a watcher on a message file. The file is opened and written to for communication. Here is a basic solution, written in IronPython 2.6:
(mutex, locked) = System.Threading.Mutex(True, "MyApp/%s" % System.Environment.UserName, None)
if locked:
watcher = System.IO.FileSystemWatcher()
watcher.Path = path_to_user_dir
watcher.Filter = "messages"
watcher.NotifyFilter = System.IO.NotifyFilters.LastWrite
watcher.Changed += handleMessages
watcher.EnableRaisingEvents = True
else:
messages = os.path.join(path_to_user_dir, "messages")
fp = file(messages, "a")
fp.write(command)
fp.close()
sys.exit(0)
For your simple reason for needing IPC, I'd look for another solution.
This code is confirmed to work on Linux and Windows. Should work on Mac as well:
public static IList Processes()
{
IList<Process> processes = new List<Process>();
foreach (System.Diagnostics.Process process in System.Diagnostics.Process.GetProcesses())
{
Process p = new Process();
p.Pid = process.Id;
p.Name = process.ProcessName;
processes.Add(p);
}
return processes;
}
Just iterate through the list and look for your own ProcessName.
To send a message to your application, just use MyProcess.StandardInput to write to the applications standard input. This only works assuming your application is a GUI application though.
If you have problems with that, then you could maybe use a specialized "lock" file. Using the FileSystemWatcher class you can check when it changes. This way the second instance could write a message in the file and then the first instance notice that it changes and can read in the contents of the file to get a message.