首页 文章

初始化USB调制解调器的COM端口

提问于
浏览
7

我正在使用 GsmComm 连接到USB调制解调器 . 当我最初将调制解调器连接到计算机时,我正在使用的调制解调器的COM端口未显示在设备管理器中 . 计算机将其显示为可移动驱动器 . 但是,当我运行随调制解调器提供的应用程序时,com端口将显示在设备管理器中 .

因此,每次我想将设备与我的应用程序一起使用时,我必须首先将其连接到PC,运行其软件以初始化COM端口,然后运行我的应用程序 .

但有没有办法用 C# 初始化我的应用程序的COM端口?

我已经阅读了有关创建虚拟COM端口以连接到USB设备的一些内容,但我不知道该怎么做 . 任何帮助或指示将受到高度赞赏 .

2016年2月14日更新

我按照antiduh的回答发现第一次连接时设备被识别为cdrom .

enter image description here

运行他们的应用程序后,链接更改为 harddiskvolume -

enter image description here

并创建了三个新的COM链接 .

enter image description here

3 回答

  • 6

    虚拟串行端口由设备随附的设备驱动程序模拟 . 如果它们在运行其软件之前未显示在设备管理器中,则它会动态安装设备驱动程序,或者向驱动程序发送秘密握手以告知它开始模拟端口 .

    前者需要UAC提升和.sys文件,如果您没有看到可能执行此操作的已安装服务,也无法查看.sys文件,那么您可以 grab 这种可能性 . 后者通常通过DeviceIoControl()调用来完成,这是您可以使用过滤器驱动程序监视的类型 . 与IoSpy类似,是WDK附带的实用程序 . 在实用程序上使用Dumpbin.exe / imports可以提供有用的实现细节,SysInternals的Process Monitor也是如此 .

    几乎没有成功的保证,最好向制造商询问细节 . 但是,它们通常不会返回电话,也不会在手册中包含此类详细信息 . 他们当然更喜欢任何人使用他们的铲子 . 请记住,你看到猪的卷曲尾巴,最好通过返回设备并从另一个制造商处购买另一个来减少损失 .

  • 3

    我有一个假设 .

    你见过Windows Object Manager吗?它是_/377028_的'/ dev'的可怕版本 .

    有趣的是,用户空间程序可以通过使用特殊前缀调用CreateFile来访问它 .

    例如,在Windows中打开串行端口的一种方法是调用 CreateFile(@"\\.\COM3") . 这是映射到对象管理器路径 \GLOBAL??\COM3 的路径 .

    以下是使用WinObj的路径:
    Screenshot of the Windows Object Manager program showing OM paths

    在我的例子中,您可以看到 \GLOBAL??\COM3 实际上已连接到 \Device\QTUSBSerial0 .

    如果您在此特殊软件运行之前和之后观看WinObj,您可能会发现哪些目标设备符号链接到COMX,然后您可能能够找出该真实设备是否实际上始终存在 .

    天真地,我认为可以将任意对象管理器路径路径到 CreateFile 来访问对象,而不必依赖于 \\.\ -to- \GLOBAL??\ 映射 . 然而,似乎有一个挂断 - 根据this answerCreateFile 将只接受以对象管理器的 \GLOBAL??\ 部分为目标的参数 - 它只接受 \\.\ 作为路径前缀,并且不接受,例如 \\.\Device\QTUSBSerial0 或某些类似的字符串 .

    还有一个可能:创建一个非常小的设备驱动程序/内核模块来自己创建符号链接,使用IoCreateSymbolicLink . 编写一个创建对象管理器符号链接 \GLOBAL??\CrazyDevice - > \Device\CrazyDevice 的驱动程序,然后使用被黑客攻击的SerialPortNet代码调用 CreateFile(@"\\.\CrazyDevice") .

    这有点紧张,但也许它会解决你的问题 .

    免责声明:我从未编写Windows设备驱动程序或操纵对象管理器 . 我几乎不知道我在这里做什么 .

  • 2

    看看自从我使用Windows以来已经过了多久,但这个概念仍然适用 . 首先,似乎这个USB调制解调器具有模式切换,这意味着它首先将其自身识别为CD-ROM以使您能够获取驱动程序,然后在安装完成后模式切换到包含创建的脚本的大容量存储设备3个COM端口的符号链接 . 要在应用程序中使用它,您需要一些东西,首先是usb PID和VID,以便能够枚举计算机集线器上的设备 . 其次,您需要一个触发符号链接创建的脚本副本一旦检测到设备(通过枚举VID和PID),需要从应用程序调用该脚本,一旦脚本执行,将自动出现三个COM端口,您应该能够像往常一样访问它们 . 另外你可能想检查一下CD-ROM应用程序是否安装了dll(几乎可以肯定它确实是第二个脚本在创建COM端口链接之前检查了dll,所以要确保dll保持在它们应该的位置) . 你需要将它们与你的应用程序链接起来以获得那些提供的任何额外功能(但是这会为本机界面打开pandoras框,如果你不熟悉那就不要这样做......我可以做/显示示例Java,但不是C#),否则,如果只是使用com端口是你想要的,你实际上知道如何与设备通信(AT命令)然后忘记它并打开com端口并消失 . 最后一点,您必须找出C#的本机接口(不是真正的本机接口,只是执行bash命令来运行脚本/ exe的系统调用,这就是全部),所以寻找内置的系统调用函数.NET框架 .

    如果您需要进一步澄清步骤,请与我们联系 .

    UPDATE:

    对于usb枚举,您可以使用类似于javas usb4java的库,并实现类似于以下的函数

    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;
    }
    

    此功能允许您直接从应用程序访问USB设备 . 然后你可以开始初始化序列和批量传输(最有可能适用于虚拟COM端口设备,例如ftdi芯片的典型情况,但由于这是一个未知的Chines芯片,这是我们可以做到的低水平,我们必须 Build 在提供什么,并让Windows做驱动程序脏工作)...

    此时,您的程序知道usb设备已插入的事实,如果fucntion返回null,则休眠一秒钟并继续轮询直到设备插入 .

    从这一点来说,你需要运行将创建符号链接的脚本,我将假设它是一个.exe文件 . 以下是其他成员发布的C#代码的副本和过去

    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;
    }
    

    使用退出代码非常有用,可以指示脚本是否成功创建了符号链接(希望是Chines的人员遵循正确的编码实践) .


    EDIT:

    脚本可能会失败 . 原因很简单:它不知道枚举和执行初始化的ProductID和VendorID . (99.99999%他们没有重新编译每个单元的简单初始化脚本只是为了硬编码pid和vid)所以它可能接收pid和vid作为args(最好的情况)或从usb大容量存储隐藏扇区读取(此时你如果从非root位置运行脚本,可能会出现路径问题... ...如果.exe没有输出任何内容到stderr,你可能需要gdb来查明是否有一些args丢失了


    最后,您可以使用标准C#库开始查找COM端口列表,例如:

    Code Source

    var portNames = SerialPort.GetPortNames();
    
    foreach(var port in portNames) {
        //Try for every portName and break on the first working
    }
    

    当您找到正在寻找的端口时,您可以使用它打开它

    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) { }
    }
    }
    

    希望有助于您入门!

相关问题