VB -> PB conversion

Windows specific forum
User avatar
dhouston
Enthusiast
Enthusiast
Posts: 430
Joined: Tue Aug 21, 2007 2:44 pm
Location: USA (Cincinnati)
Contact:

VB -> PB conversion

Post by dhouston »

I have some VB code which I probably got originally from some online VB code archive. I've long used it to enumerate the COM ports. I want to convert it to PB but am still too new to PB to manage without lots of help. It has always proven reliable, unlike other methods which rely on registry entries. It returns hardware COM ports, USB->serial adapters and virtual ports used with ethernet serial device servers. Here's the VB code...

Code: Select all

Private Type PORT_INFO_2
  pPortName As String
  pMonitorName As String
  pDescription As String
  fPortType As Long
  Reserved As Long
End Type
Private comPorts(0 To 100) As PORT_INFO_2

Private Function GetAvailablePorts(ServerName As String) As Long

  Dim ret As Long
  Dim PortsStruct(0 To 100) As API_PORT_INFO_2
  Dim pcbNeeded As Long
  Dim pcReturned As Long
  Dim TempBuff As Long
  Dim i As Integer

  'Get the number of bytes needed to contain the data returned by the API call
  ret = EnumPorts(ServerName, 2, TempBuff, 0, pcbNeeded, pcReturned)
  'Allocate the Buffer
  TempBuff = HeapAlloc(GetProcessHeap(), 0, pcbNeeded)
  ret = EnumPorts(ServerName, 2, TempBuff, pcbNeeded, pcbNeeded, pcReturned)
  If ret Then
    'Convert the returned String Pointer Values to VB String Type
    'CopyMem is aliased RtlMoveMemory
    CopyMem PortsStruct(0), ByVal TempBuff, pcbNeeded
    For i = 0 To pcReturned - 1
      comPorts(i).pDescription = Ptr2Str(PortsStruct(i).pDescription)
      comPorts(i).pPortName = Ptr2Str(PortsStruct(i).pPortName)
      comPorts(i).pMonitorName = Ptr2Str(PortsStruct(i).pMonitorName)
      comPorts(i).fPortType = PortsStruct(i).fPortType
    Next
  End If
  GetAvailablePorts = pcReturned
  'Free the Heap Space allocated for the Buffer
  If TempBuff Then
    HeapFree GetProcessHeap(), 0, TempBuff
  End If

End Function
I really don't yet grasp the details of structures in PB. Also, the EnumPorts API function returns Unicode which needs to be converted to ASCII.

The data returned tend to contain duplicate entries which have to be filtered and also returns Infrared and other ports which also need filtering. In VB, I use the filtered results to add items to a Port menu.
Last edited by dhouston on Tue Jan 15, 2008 1:26 pm, edited 1 time in total.
ABBKlaus
Addict
Addict
Posts: 1143
Joined: Sat Apr 10, 2004 1:20 pm
Location: Germany

Post by ABBKlaus »

here is my approach :

Code: Select all

Structure API_PORT_INFO_2 
  PortName.s
  MonitorName.s
  Description.s
  PortType.l
  Reserved.l
EndStructure

Global NewList Portinfos.API_PORT_INFO_2()

Procedure.l GetAvailablePorts(ServerName.s="")
;http://www.purebasic.fr/english/viewtopic.php?p=215002#215002
  Protected res.l
  Protected pcbNeeded.l
  Protected pcReturned .l
  Protected *TempBuff
  Protected i.l
  Protected *pName
  
  If ServerName=""
    *pName=0
  Else
    *pName=@ServerName
  EndIf
  
  ;Dim PortsStruct(0 To 100) As API_PORT_INFO_2 
  
  ;Get the number of bytes needed To contain the Data returned by the API call 
  ret = EnumPorts_(*pName, 2, 0, 0, @pcbNeeded, @pcReturned) 
  If pcbNeeded
    Debug "pcbNeeded = "+Str(pcbNeeded)+" Bytes"
    *TempBuff = AllocateMemory(pcbNeeded) ; Allocate the Buffer 
    ret = EnumPorts_(*pName, 2, *TempBuff, pcbNeeded, @pcbNeeded, @pcReturned) 
    If ret
      Debug "Ports received = "+Str(pcReturned)
      ;Conversion is not needed, PB makes it ;-)
      For i = 0 To pcReturned - 1 
        ;set structure over the memory area
        *strPortinfos.PORT_INFO_2=*TempBuff+(i*SizeOf(PORT_INFO_2))
        AddElement(Portinfos())
        Portinfos()\PortName=PeekS(*strPortinfos\pPortName)
        Portinfos()\MonitorName=PeekS(*strPortinfos\pMonitorName)
        Portinfos()\Description=PeekS(*strPortinfos\pDescription)
        Portinfos()\PortType=*strPortinfos\fPortType
      Next
    EndIf
    ;Free the Heap Space allocated for the Buffer 
    If *TempBuff
      FreeMemory(*TempBuff)
    EndIf 
  EndIf
  
  ProcedureReturn pcReturned 
EndProcedure

If GetAvailablePorts("\\Klaus-Core2")
  ForEach Portinfos()
    Debug Portinfos()\PortName
    Debug Portinfos()\MonitorName
    Debug Portinfos()\Description
    Debug Portinfos()\PortType
    Debug "-------------------------------------------------------------"
  Next
EndIf
User avatar
dhouston
Enthusiast
Enthusiast
Posts: 430
Joined: Tue Aug 21, 2007 2:44 pm
Location: USA (Cincinnati)
Contact:

Post by dhouston »

Thank you.

Now, I'll try to work through it and understand the how and why of your conversion. I think this is the last major thing I needed get a handle on in order to start converting several VB apps to PB.
TerryHough
Enthusiast
Enthusiast
Posts: 781
Joined: Fri Apr 25, 2003 6:51 pm
Location: NC, USA
Contact:

Post by TerryHough »

Take a look here in my PureBasic web are too
http://elfecc.no-ip.info/purebasic/#Info_SerialPorts

Something I did long ago to check out the COM ports. Might help you.
User avatar
dhouston
Enthusiast
Enthusiast
Posts: 430
Joined: Tue Aug 21, 2007 2:44 pm
Location: USA (Cincinnati)
Contact:

Post by dhouston »

@TerryHough

I spent a few weeks reading the forums and cruising all the code archives I could find, bookmarking posts and web pages that looked helpful for the things I knew I would need to do. I bookmarked your page with the intention of returning to steal as much as I could carry but I haven't been able to connect to it since then until just now when the link worked fine. I have now downloaded your serial port, device enumeration, and atomic clock examples and will look them over. Thanks.
User avatar
dhouston
Enthusiast
Enthusiast
Posts: 430
Joined: Tue Aug 21, 2007 2:44 pm
Location: USA (Cincinnati)
Contact:

Post by dhouston »

Here's the debug output from my VB app followed by my partially converted PB app. COM1, COM3 & COM5 are valid serial ports.

Code: Select all

COM5          Virtual Serial Port          5 
COM3          USB to Serial Port           3 
COM1          Communications Port          1 
LPT1          EPSON Printer Port           1 
COM4          Infrared Serial (COM) Port   4 
LPT3          Infrared Printing (LPT) Port 3 
D-Link LPT port      Print Server          0 
FILE          Unknown Local Port           0 
FILE          Creates a file on disk       0 
COM5          Virtual Serial Port          5 
COM3          USB to Serial Port           3 
COM1          Communications Port          1 
LPT1          EPSON Printer Port           1 
COM4          Infrared Serial (COM) Port   4 
LPT3          Infrared Printing (LPT) Port  

pcbNeeded = 889 Bytes
Ports received = 15
COM5:    Local Port    Virtual Serial Port                1
-------------------------------------------------------------
COM3:    Local Port    USB to Serial Port                1
-------------------------------------------------------------
COM1:    Local Port    Communications Port           1
-------------------------------------------------------------
LPT1:      Local Port    EPSON Printer Port               3
-------------------------------------------------------------
COM4:    Local Port    Infrared Serial (COM) Port    1
-------------------------------------------------------------
LPT3:      Local Port    Infrared Printing (LPT) Port   3
-------------------------------------------------------------
D-Link LPT port     Local Port     Print Server           1
-------------------------------------------------------------
FILE:       Local Port    Unknown Local Port              1
-------------------------------------------------------------
FILE:       Local Port    Creates a file on disk           1
-------------------------------------------------------------
COM5:     Local Port    Virtual Serial Port                3
-------------------------------------------------------------
COM3:     Local Port    USB to Serial Port                3
-------------------------------------------------------------
COM1:     Local Port    Communications Port           3
-------------------------------------------------------------
LPT1:       Local Port    EPSON Printer Port              3
-------------------------------------------------------------
COM4:     Local Port    Infrared Serial (COM) Port   3
-------------------------------------------------------------
LPT3:       Local Port    Infrared Printing (LPT) Port  3
-------------------------------------------------------------
RichardL
Enthusiast
Enthusiast
Posts: 532
Joined: Sat Sep 11, 2004 11:54 am
Location: UK

Post by RichardL »

@dhouston
I have been trying to put a solution together to find all the real and virtual comm ports on a machine into a list; and stumbled on your code. After a bit of work I managed to get a list of ports that are supposed to be on my machine, but there seems to be too many :?

Did you ever take your code through to the bitter end? The problem I have is that some of the machines I run code on have a ton of Bluetooth virtual com ports and when I try to probe them to look for expected hardware I might as well go out for a beer it takes so long.

Any tips to stop me becoming an alcoholic?

Cheers
RichardL
User avatar
dhouston
Enthusiast
Enthusiast
Posts: 430
Joined: Tue Aug 21, 2007 2:44 pm
Location: USA (Cincinnati)
Contact:

Post by dhouston »

RichardL wrote:Did you ever take your code through to the bitter end? The problem I have is that some of the machines I run code on have a ton of Bluetooth virtual com ports and when I try to probe them to look for expected hardware I might as well go out for a beer it takes so long.
This code works well for me (under Windows) but I don't have any Bluetooth ports so it may need modification for them.

Code: Select all

;========================================================
;
; SerialPorts.pbi
;
;========================================================

Structure API_PORT_INFO_2
  PortName.s
  MonitorName.s
  Description.s
  PortType.l
  Reserved.l
EndStructure

#BufferIn = 1024
#BufferOut = 1024

Global ports.s,ComPort.s
Global NumPorts,ComID

Procedure.l GetAvailablePorts(ServerName.s="")
  NewList Portinfos.API_PORT_INFO_2()
  NewList ComPorts()
  Protected item.s,SaveSetting.s
  Protected res.l, pcbNeeded.l,pcReturned .l,i.l,ret.l
  Protected *TempBuff,*pName,*strPortinfos.PORT_INFO_2
  
  If ServerName=""
    *pName=0
  Else
    *pName=@ServerName
  EndIf
  
  ;Get the number of bytes needed To contain the Data returned by the API call
  ret=EnumPorts_(*pName,2,0,0,@pcbNeeded,@pcReturned)
  If pcbNeeded
    ;Debug "pcbNeeded = "+Str(pcbNeeded)+" Bytes"
    *TempBuff=AllocateMemory(pcbNeeded) ; Allocate the Buffer
    ret=EnumPorts_(*pName,2,*TempBuff,pcbNeeded,@pcbNeeded,@pcReturned)
    If ret
      ;Debug "Ports received = "+Str(pcReturned)
      For i = 0 To pcReturned - 1
        ;set structure over the memory area
        *strPortinfos.PORT_INFO_2=*TempBuff+(i*SizeOf(PORT_INFO_2))
        AddElement(Portinfos())
        Portinfos()\PortName=PeekS(*strPortinfos\pPortName)
        Portinfos()\MonitorName=PeekS(*strPortinfos\pMonitorName)
        Portinfos()\Description=PeekS(*strPortinfos\pDescription)
        Portinfos()\PortType=*strPortinfos\fPortType
;Debug Portinfos()\Description
;Debug Portinfos()\PortName
;Debug Portinfos()\PortType
;Debug Portinfos()\MonitorName
;Debug "-------------"
        If Not FindString(LCase(Portinfos()\Description),"infrared",1)
          If Not FindString(LCase(Portinfos()\Description),"modem",1)
            If Not FindString(ports,Portinfos()\PortName,1)
              If Left(Portinfos()\PortName,3)="COM" And FindString(Portinfos()\Description," Port",1)
                ComPort=RemoveString(Portinfos()\PortName,":")
                ComID=OpenSerialPort(#PB_Any,ComPort,19200,#PB_SerialPort_NoParity,8,1,#PB_SerialPort_NoHandshake,1024,1024)                                          
                If IsSerialPort(ComID)
                  AddElement(ComPorts())
                  ComPorts()=Val(Mid(Portinfos()\PortName,4,3))  ;COM1-COM256
                  CloseSerialPort(ComID):Delay(25)
                EndIf  
              EndIf  
            EndIf
          EndIf
        EndIf
      Next
    EndIf
    ComID=0:ports=""    
    SortList(ComPorts(),0)
    ForEach ComPorts()
      item="COM"+Str(ComPorts())+":"
      If FindString(ports,item,1)=0   ;avoid duplicates
        ports+item
        numPorts+1
      EndIf
    Next
;Debug ports    
    ;Free the Heap Space allocated For the Buffer
    If *TempBuff
      FreeMemory(*TempBuff)
    EndIf
  EndIf
  OpenPreferences("roZetta.prf"):PreferenceGroup("Global")
    ComPort=ReadPreferenceString("ComPort","COM1")
  ClosePreferences()
  ProcedureReturn pcReturned
EndProcedure
Any tips to stop me becoming an alcoholic?
You can send me the booze instead of drinking it yourself. :P
RichardL
Enthusiast
Enthusiast
Posts: 532
Joined: Sat Sep 11, 2004 11:54 am
Location: UK

Post by RichardL »

@dhouston

Hi, thanks for your assistance, I have delved into your code and modified it to make a simpler version for my purposes, which is to establish a list of available serial ports. I was quite surprised by the results when I tested it with various serial adaptors.

I have started a new thread with the title "Discovering ports with EnumPorts()", not in any way because your thread is a bad choice in which to continue, but to move under a more relevant title.

Again, thanks for your help.

RichardL
Post Reply