MS Bluetooth Stack: Com port herausfinden



  • Hi,

    hoffe hier kann mir jemand helfen, sonst bleibt mir nur noch, Microsoft direkt zu kontaktieren.....habe schon Ewigkeiten damit verbracht.
    Ich muss den virtuellen Com port zu einer bth Adresse herausfinden. Die Bluetoothgeräte finde ich mit BluetoothFindFirstDevice und habe somit auch den Namen und die Adresse. Mein Problem ist nun, dass die BluetoothDeviceInfo Struktur nicht den virtuellen Com port enthält und ich auch sonst keine Anhaltspunkte habe, wo ich den herbekommen könnte. Habe auch schon mit der SetupDi-Api versucht, irgendwas herauszufiltern, aber an die Verknüpfung zwischen Adresse (oder Namen) und COM port ist einfach nicht heranzukommen....
    Weiß jemand weiter???

    Vielen Dank schon mal
    MfG



  • Hi tuxman,

    das was Du erreichen willst, ist nicht ganz trivial.
    Aber machbar, wenn auch mit viel Arbeit.
    Daher die Frage: Wieviel Programmiererfahrung hast Du bisher schon?

    Vorneweg kann ich Dir ein Lösungsprinzip vorschlagen, welches ich schon anwendete.

    In meiner Applikation ist ein ähnliches Prinzip angewendet worden.
    In der werden alle verfügbaren COM-Ports des PCs aufgelistet, und dazu werden gleich die sog. "User-friendly Names" mit angezeigt.

    Je nach Hersteller bzw. Treiber erscheint dann z.B.:
    "Standardmäßige Seriell-über-Bluetooth-Verbindung"
    "Standardmodem über Bluetooth-Verbindung" oder
    "Bluetooth Communications Port (COMxx)"

    So habe ich also für jeden COM-Port den zugehörigen "user-friendly Namen", und damit kann ich also auswählen, welche COM-Ports an Bluetooth verknüpft sind.

    //1) Im ersten Versuch mittels SetupAPI-Funktionen und S_GUID_DEVINTERFACE_COMPORT:
    //   Schlüsselprinzip: Funktionen: SetupDiEnumDeviceInterfaces() und SetupDiGetDeviceInterfaceDetail().
    //                     Vorteile: +Erfaßt auch Modem-Devices, die unter COM-Port angesprochen werden.
    //                               +Liefert Device-Path des Devices.
    //                     Nachteil: -Funktioniert nicht unter Win9xMe und NT.
    //
    //2) Im zweiten Versuch mittels mittels SetupAPI-Funktionen und Klassen-Name "Ports":
    //   Schlüsselprinzip: Funktionen: SetupDiClassGuidsFromName() und SetupDiEnumDeviceInfo().
    //                     Vorteil: +Funktioniert auch unter Win98, 98SE und Me.
    //                     Nachteile: -Funktioniert nicht unter Win95 und NT.
    //                                -Modem-Devices werden nicht erkannt.
    //                                -Device-Path kann nicht ermittelt werden.
    //
    //3) Im dritten Versuch mittels WMI über WMI-Klasse "Win32_SerialPort":
    //   Verwendete COM-Interfaces: IWbemLocator, IWbemServices, IEnumWbemClassObject, IWbemClassObject.
    //                              (WBEM = Web-based Enterprise Management)
    

    Warum drei verschiedene Versuche?
    Das liegt daran, daß manche Hersteller nicht auf allen drei Möglichkeiten die Informationen bereitstellt.
    Beispielsweise stellt der Treiber vom Hersteller A nur über WMI die nötigen Informationen bereit, während ein anderer Treiber vom Hersteller B diese nur über SetupDiClassGuidsFromName() bereitstellt, usw.

    HTH,
    Martin



  • hi,

    Vielen Dank.
    Programmiererfahrung sollte ich ausreichend haben 😉
    Wie schon gesagt, habe ich mit der SetupDi Api versucht die Namen der Schnittstellen zu bekommen, das hat auch mit dem Enumerator "BTHENUM" geklappt. Hier bekomme ich jetzt "Standardmäßige Seriell-über-Bluetooth-Verbindung", aber ich weiß jetzt nicht, wie ich diesen Namen (es sind 5 oder mehr Verbindungen mit gleichem Namen) jeweils deren Adresse oder COM port herausbekomme. Mir fehlt hier irgendwie die Verknüpfung zwischen Adresse, dem "FriendlyName" und dem Com Port. Diese sind auch in dem Reg Schluessel, welchen SetupDi durchsucht, nicht gespeichert, wenn ich in der Registry nachgucke....

    MfG



  • Ich kenne mich da jetzt nicht aus, aber braucht man den COM-Port zum kommunizieren mit dem Gerät überhaupt? Ich dachte das läuft da irgendwie mit über Winsock und der COM-Port wäre nur nen zusätzliche Feature des Bluetooth-Treibers für Software die halt nen COM-Port zwingend benötigt?



  • richtig, man kann es auch über sockets machen, aber in die software, für die ich die bluetooth sachen schreiben soll, arbeitet über serielle schnittstellen 😉



  • Ist vielleicht nur eine kleine Hilfe aber unter http://www.naughter.com/enumser.html findet man die verschiedenen Arten wie man COM Ports enumerieren kann.



  • tuxman schrieb:

    Mir fehlt hier irgendwie die Verknüpfung zwischen Adresse, dem "FriendlyName" und dem Com Port.

    Hi und sorry, daß es etwas länger gedauert hat.
    Mußte erst mal in meinen Sourcen durchwühlen, und alles überflüssige wegschmeißen.

    Nachfolgend ist mein Code aufgebaut, auf Basis von S_GUID_DEVINTERFACE_COMPORT
    (also nur dem ersten von den drei erwähnten Versuchen in meinem ersten Post! 🕶 )

    Im Prinzip suche ich hier alle COM-Ports mittels SetupDixxx-Funktionen ab. Und dazu ermittle ich die Friendly Names und die verknüpften COM-Port Nummern.
    Das Ermitteln der verknüpften COM-Port Nummern müßte auch auf Deine Bluetooth-Klasse übertragbar sein. Das mußt Du selber herausfinden ob es auch so klappt.

    Hier die Start-Funktion ***EnumSerialPortFriendlyName()***:

    uint32 ui32_bufsize;
    uint32 ui32_device_idx;
    uint32 ui32_device_portnr;
    TCHAR tcsz_portname_friendly[NUI32_TEXTLAENGE_SERPORTENUM_FRIENDLYNAME + 1];
    HDEVINFO hdevinfoset;
    GUID guid_DevInterfaceComport;
    SP_DEVINFO_DATA s_spdevinfodata;
    SP_DEVICE_INTERFACE_DATA s_spdeviceinterfacedata;
    PSP_DEVICE_INTERFACE_DETAIL_DATA ps_spdeviceinterfacedetaildata;
    
    //You access the COM port through an instance of the COM port device interface class.
    //The GUID for this class is S_GUID_DEVINTERFACE_COMPORT, which is defined in ntddser.h.
    EXTERN_C const GUID S_GUID_DEVINTERFACE_COMPORT = { 0x86E0D1E0L, 0x8089, 0x11D0, 0x9C, 0xE4, 0x08, 0x00, 0x3E, 0x30, 0x1F, 0x73 };
    
    //COM-Port friendly Namen mit SetupDixx()-Funktionen und S_GUID_DEVINTERFACE_COMPORT ermitteln
    void EnumSerialPortFriendlyName( void )
    {
      guid_DevInterfaceComport = S_GUID_DEVINTERFACE_COMPORT;
      hdevinfoset = SetupDiGetClassDevs( &guid_DevInterfaceComport, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );
                                                              //Device information set erzeugen.
                                                              //Return devices that support device interfaces
                                                              //for the specified device interface classes.
                                                              //Return only devices that are currently present in a system.
      if ( hdevinfoset != INVALID_HANDLE_VALUE )
      {
        ui32_device_idx = 0;
        do
        {
          s_spdeviceinterfacedata.cbSize = sizeof( SP_DEVICE_INTERFACE_DATA );
          b_result = SetupDiEnumDeviceInterfaces( hdevinfoset, NULL, &guid_DevInterfaceComport,
                                                  ui32_device_idx, &s_spdeviceinterfacedata );
                                                              //Enumerates the device interfaces that
                                                              //are contained in a device information set.
          if ( b_result != FALSE )
          {
            SetupDiGetDeviceInterfaceDetail( hdevinfoset, &s_spdeviceinterfacedata,
                                             NULL, 0, &ui32_bufsize, NULL );
                                                              //With the parameters DeviceInterfaceDetailData = NULL and
                                                              //DeviceInterfaceDetailDataSize = 0 the function returns
                                                              //the required buffer size at RequiredSize and fails with
                                                              //GetLastError() returning ERROR_INSUFFICIENT_BUFFER.
            if ( ui32_bufsize >= sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA ) )
            {
              ps_spdeviceinterfacedetaildata = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc( ui32_bufsize );
                                                              //Speicher allozieren, bestehend aus der festen
                                                              //Struktur SP_DEVICE_INTERFACE_DETAIL_DATA plus
                                                              //die variable Stringlänge von DevicePath.
              if ( ps_spdeviceinterfacedetaildata != NULL )
              {
                ps_spdeviceinterfacedetaildata->cbSize = sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA );
                s_spdevinfodata.cbSize = sizeof( SP_DEVINFO_DATA );
                if ( SetupDiGetDeviceInterfaceDetail( hdevinfoset, &s_spdeviceinterfacedata,
                                                      ps_spdeviceinterfacedetaildata,
                                                      ui32_bufsize, NULL, &s_spdevinfodata ) == TRUE )
                {
                  SerPortName_GetSetupDiFriendlyName( hdevinfoset, &s_spdevinfodata,
                                                      tcsz_portname_friendly,
                                                      ps_spdeviceinterfacedetaildata->DevicePath );
                  ui32_device_portnr = SerPortName_GetPortNr( hdevinfoset, tcsz_portname_friendly, &s_spdevinfodata );
                  if ( ui32_device_portnr != 0 )              //Wurde eine COM-Port-Nr. ermittelt?
                  {
                    SerPortName_DatenUebernehmen( ui32_device_portnr, @@@@, ui32_comport_fifo_status,
                                                  ui32_comport_fifo_RX, ui32_comport_fifo_TX,
                                                  NULL, tcsz_portname_friendly,
                                                  ps_spdeviceinterfacedetaildata->DevicePath );
                  }
                }
                free( ps_spdeviceinterfacedetaildata );
              }
            }
          }
          ui32_device_idx++;
        } while ( b_result != FALSE );
        SetupDiDestroyDeviceInfoList( hdevinfoset );          //Device information set aus Speicher entfernen.
      }
    } //Ende von EnumSerialPortFriendlyName().
    

    Dazu noch SerPortName_GetSetupDiFriendlyName() (liefert den Friendly Namen unter ptcsz_friendlyname zurück):

    void SerPortName_GetSetupDiFriendlyName( const HDEVINFO hdevinfoset,
                                             const PSP_DEVINFO_DATA ps_spdevinfodata,
                                             LPTSTR ptcsz_friendlyname,
                                             const LPCTSTR ptcsz_devicepathname )
    {
      bit b_result;
      TCHAR tcsz_LocInfo[256];
    
      b_result = SetupDiGetDeviceRegistryProperty( hdevinfoset, ps_spdevinfodata, SPDRP_FRIENDLYNAME,
                                                   NULL, (PBYTE)ptcsz_friendlyname,
                                                   NUI32_TEXTLAENGE_SERPORTENUM_FRIENDLYNAME * sizeof( TCHAR ),
                                                   NULL );          //SPDRP_FRIENDLYNAME (0x0C): The function retrieves a
                                                                    //REG_SZ string that contains the friendly name of a device.
      if ( b_result == FALSE )
      {
        //Wenn das Ermitteln von SPDRP_FRIENDLYNAME fehlschlägt, versuchen wirs mit SPDRP_DEVICEDESC.
        b_result = SetupDiGetDeviceRegistryProperty( hdevinfoset, ps_spdevinfodata, SPDRP_DEVICEDESC,
                                                     NULL, (PBYTE)ptcsz_friendlyname,
                                                     NUI32_TEXTLAENGE_SERPORTENUM_FRIENDLYNAME * sizeof( TCHAR ),
                                                     NULL );        //SPDRP_DEVICEDESC (0x00): The function retrieves a
                                                                    //REG_SZ string containing the description of a device.
      }
      if ( SetupDiGetDeviceRegistryProperty( hdevinfoset, ps_spdevinfodata, SPDRP_LOCATION_INFORMATION,
                                             NULL, (PBYTE)tcsz_LocInfo, sizeof( tcsz_LocInfo ), NULL ) == TRUE )
                                                                    //SPDRP_LOCATION_INFORMATION (0x0D): The function retrieves a
                                                                    //REG_SZ string that contains the hardware location of a device.
                                                                    //SetupDiGetDeviceRegistryProperty() returns the
                                                                    //ERROR_INVALID_DATA error code if the requested property does
                                                                    //not exist for a device or if the property data is not valid.
      {
        ...
        //Diesen Block habe ich weggelassen, da hier (in meiner Applikation) andere Informationen verarbeitet werden.
        ...
      }
    } //Ende von SerPortName_GetSetupDiFriendlyName().
    

    Schließlich noch die alles entscheidende Funktion ***SerPortName_GetPortNr()***:

    uint32 SerPortName_GetPortNr( const HDEVINFO hdevinfoset,
                                  const LPTSTR ptcsz_friendlyname,
                                  const PSP_DEVINFO_DATA ps_spdevinfodata )
    {
      uint32 ui32_portnr;
      DWORD dword_regtype;
      size_t sizet_string;
      TCHAR tcsz_string[256];
      TCHAR* ptcsz_gefunden;
      HKEY hkey_device;
    
      ui32_portnr = 0;
      hkey_device = SetupDiOpenDevRegKey( hdevinfoset, ps_spdevinfodata,
                                          DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE );
      if ( hkey_device != INVALID_HANDLE_VALUE )
      {
        sizet_string = sizeof( tcsz_string );
        dword_regtype = 0;
        if ( RegQueryValueEx( hkey_device, TEXT( "PortName" ), NULL, &dword_regtype,
                              (LPBYTE)tcsz_string, (LPDWORD)&sizet_string ) == ERROR_SUCCESS )
        {
          if ( dword_regtype == REG_SZ )
          {
            String_COMx_to_Ui32( tcsz_string, &ui32_portnr );       //COM-Port Nr. aus Text ermitteln.
                                                                    //Stringvergleich mit "COM" ist case-sensitive.
          }
        }
        RegCloseKey( hkey_device );
      }
    
      if ( ui32_portnr == 0 )
      {
        //Die Ermittlung der COM-Port-Nr.aus dem Port-Namen mittels RegQueryValueEx( "PortName" )
        //ist fehlgeschlagen -> neuer Versuch mit "COMx" im friendlyName!
        ptcsz_gefunden = _tcsstr( ptcsz_friendlyname, TEXT( "COM" ) );
                                                                    //Stringvergleich mit "COM" ist case-sensitive.
        if ( ptcsz_gefunden != NULL )
        {
          String_MaxCountIgnoreBlank_to_Ui32( &ptcsz_gefunden[3], 4, &ui32_portnr );
                                                                    //COM-Port Nr. aus Text ermitteln, Wert bis 4096 möglich.
          if ( ui32_portnr > COMDB_MIN_PORTS_ARBITRATED )           //Mehr als COM256 wird nicht von mir unterstützt.
          {
            ui32_portnr = 0;
          }
        }
      }
      return( ui32_portnr );
    } //Ende von SerPortName_GetPortNr().
    

    Die Funktion SerPortName_GetPortNr() liefert als Rückgabewert die COM-Port Nummer des zugehörigen Devices.
    Wenn sie nicht ermittelt werden konnte, gibt sie 0 zurück (COM0 gibt es bekanntlich nicht... )

    (Syntaxfehler oder vergessene Definitionen möge man mir verzeihen, einfach hier nachfragen. 🙄 )
    Martin



  • DANKE für die Mühe den ganzen Code rauszusuchen!!! 👍
    Sieht gut aus, ich werde es mal ausprobieren. Kanns allerdings erst Freitag machen. Sage dann nochmal bescheid, ob es geklappt hat.
    Gibt aber in der MSDN nicht wirkliche Hilfen dazu, jedenfalls nicht, wenn man nich weiß wonach man suchen muss.

    Mfg



  • so, hallo

    es funktioniert soweit alles prima, bis auf eine kleinigkeit:
    wenn ich als nicht-admin diese code verwende, funktioniert er nicht. dann gibt SetupDiOpenDevRegKey() immer invalid handle zurück. als admin, kein problem...
    Ist dieses Problem überhaupt zu lösen, ohne per hand die berechtigungen in der Registry zu ändern?

    MfG



  • Freut mich, daß das Prinzip auch bei Dir funktioniert

    Nun, hast Du für SetupDiOpenDevRegKey() auch den Security-Access Schlüssel KEY_QUERY_VALUE verwendet?
    Und nicht etwa z.B. KEY_ALL_ACCESS ?

    Denn beim zweiten Schlüssel verweigert Dir der Sicherheitsmechanismus von Windows jeglichen Zugriff, wenn Du nicht über die nötigen Rechte (sprich Admin) verfügst.
    Selbst wenn Du nur lesen möchtest...

    Bei mir lassen sie sich als normal eingeloggten User auslesen...

    Ansonsten, versuchs mal alternativ mit dem Schlüssel KEY_EXECUTE (d.h. Permission for read access).

    Martin



  • hi,

    jo, hab KEY_QUERY_VALUE genommen, mit KEY_EXECUTE geht es auch nicht 😕
    ich habe auhc nich mal leserechte für den gesamte ENUM schlüssel in der registry

    EDIT: ich bin hauptbenutzer



  • D.h. Du kannst also anscheinend auch nicht über die ganz gewöhnlichen Registry-Funktionen wie z.B. RegQueryValueEx() lesend auf diverse Registry-Schlüssel zugreifen?

    Wenn ja, dann hast Du ein anderes Problem mit den Zugriffsrechten!
    (hat also nichts mehr mit Deinem ursprünglichen Problem mit der COM-Port Nummer Verknüpfung zu tun)

    Ups, jetzt ist wirklich guter Rat teuer.
    Kann es sein, daß der Bluetooth-Treiber unter einem anderen User-Login (mit veränderten Rechten) installiert wurde und deshalb einem anderen User wie Dir als Hauptbenutzer die Zugriff verwehrt wird?



  • richtig, mit RegQueryValueEx kann ich auhc nicht drauf zugreifen...
    Das mit dem Treiber kann sein, ich habe den Rechner übernommen, weiß also nicht genau was damit passiert ist...

    Ich danke für die vielen Ratschläge, dann muss ich mich noch einmal umschauen mit den Rechten.

    MfG


Anmelden zum Antworten