KnowledgeBase Archive

An Archive of Early Microsoft KnowledgeBase Articles

View on GitHub

Q291535: HOWTO: Find Installed Modems with Visual FoxPro

Article: Q291535
Product(s): Microsoft FoxPro
Version(s): 5.0,5.0a,6.0
Operating System(s): 
Keyword(s): kbActiveX kbCtrl kbOOP kbvfp500 kbvfp600
Last Modified: 30-MAR-2001

-------------------------------------------------------------------------------
The information in this article applies to:

- Microsoft Visual FoxPro for Windows, versions 5.0, 5.0a, 6.0 
-------------------------------------------------------------------------------

SUMMARY
=======

With the robust development environment offered by Visual FoxPro (VFP),
developers can build and deploy a wide variety of applications, such as
applications that incorporate the use of modems and communication ports. Because
of this, VFP developers often require a programmatic method to determine the
available modems on a computer.

MORE INFORMATION
================

Using the MSComm ActiveX control (Mscomm32.ocx) that ships with Visual FoxPro
versions 5.0 and 6.0, it is possible to locate a modem on a computer and
determine the COM port that it uses.

The following code:

1. Uses the INIT event to loop through 10 COM ports, and attempts to open each
  port in order to populate a list of active COM ports.

2. Presents a list of active COM ports that exist on the computer.

3. Tests each of the active COM ports by sending Hayes-compatible commands to
  the port and reviewing the response (if any) when the user clicks the command
  button on the form.

To determine an available modem, follow these steps:

1. In VFP, paste the following code in a new program:

  *!* Q291535: HOWTO: Find installed modems using Microsoft Visual FoxPro
  *!* This article use Mscom32.ocx to detect if any modems are installed
  *!* and reports the COM port it is using.
  *!* 

  PUBLIC oform1

  oform1=CREATEOBJECT([MODEMFORM])
  oform1.Show
  RETURN

  **************************************************
  *
  DEFINE CLASS MODEMFORM AS form

  	Height = 128
  	Width = 160
  	DoCreate = .T.
  	AutoCenter = .T.
  	BorderStyle = 2
  	Caption = [Com Port Test]
  	MaxButton = .F.
  	MinButton = .F.
  	Visible = .F.
  	AlwaysOnTop = .T.
  	BackColor = RGB(0,0,255)
  	Name = [Form1]

  	*-- Form array that stores information about available machine com ports.
  	DIMENSION acommports[1,2]

  	ADD OBJECT ocxCOMM AS MSCOMM_OCX WITH ;
  		Top = 101, ;
  		Left = 72, ;
  		Height = 21, ;
  		Width = 38, ;
  		Name = [ocxCOMM]

  	ADD OBJECT lblava_ports AS label WITH ;
  		AutoSize = .T., ;
  		FontName = [Verdana], ;
  		Caption = [Available COM Ports:], ;
  		Height = 16, ;
  		Left = 14, ;
  		Top = 15, ;
  		Width = 133, ;
  		ForeColor = RGB(255,255,255), ;
  		BackColor = RGB(0,0,255), ;
  		Name = [lblAVA_PORTS]

  	ADD OBJECT command1 AS commandbutton WITH ;
  		Top = 78, ;
  		Left = 11, ;
  		Height = 27, ;
  		Width = 138, ;
  		FontName = [Tahoma], ;
  		Caption = [\<Test Ports for Modem], ;
  		Name = [Command1]

  	ADD OBJECT timer1 AS timer WITH ;
  		Top = 105, ;
  		Left = 12, ;
  		Height = 23, ;
  		Width = 23, ;
  		Enabled = .F., ;
  		Interval = 1500, ;
  		Name = [Timer1]

  	ADD OBJECT lblports AS label WITH ;
  		FontName = [Verdana], ;
  		Alignment = 2, ;
  		Caption = [lblPORTS], ;
  		Height = 17, ;
  		Left = 42, ;
  		Top = 46, ;
  		Width = 77, ;
  		ForeColor = RGB(255,255,255), ;
  		BackColor = RGB(0,0,255), ;
  		Name = [lblPORTS]

  	PROCEDURE WaitForResponse
  		*~ This method loops for the specified
  		*~ amount of time calling DOEVENTS().
  		*~ This allows time for the modem to respond to the requests.

  		LPARAMETERS lnDelayInSeconds

  		LOCAL lnStartTime
  		lnStartTime = SECO()

  		DO WHILE SECO() <= (lnStartTime + lnDelayInSeconds) ;
  				AND NOT SECO() < lnStartTime
  			DOEVENTS()
  		ENDDO
  	ENDPROC

  	PROCEDURE Error
  		LPARAMETERS nError, cMethod, nLine
  		*~ In THIS.INIT we loop through 10 COM ports, 
  		*~ attempting to open each with the MSCOMM32.OCX control.
  		*~ We trap the errors here and if it's an OLE error,
  		*~ nError will be 1429 (this came from the control).

  		IF INLIST(nError, 1429, 1426)
  		*~  Populate an error array and check the OLE message.
  			LOCAL aErrArray(1)
  			AERROR(aErrArray)
  		*~ 	If the error msg was "Invalid Port Number"
  		*~  the control tried to open a port that either
  		*~  doesn't exist or has nothing on it.
  		*~  Set a global var (glBadPort) to .T.
  		*~  this var is used in THIS.INIT loop.
  			IF [INVALID PORT NUMBER] $ UPPER(aErrArray(3)) or ;
  				[UNKNOWN OLE STATUS CODE] $ UPPER(aErrArray(3))
  				glBadPort = .T.
  			ENDIF
  		ELSE
  		*~  We experienced a non - OLE error. Report it.
  			MESSAGEBOX([Error #: ] + ALLT(STR(nError)) + CHR(13) + ;
  				[Message: ] + MESSAGE() + CHR(13) + ;
  				[Line with error: ] + ALLT(STR(nLine)) + CHR(13) + ;
  				[Method: ] + cMethod)
  		ENDIF
  	ENDPROC

  	PROCEDURE Init
  		*~ Var that indicates a non-existent or inactive COM port. 
  		*~ This var is flipped by THISFORM.ERROR()
  		_VFP.AutoYield=.F.
  		PUBLIC glBadPort
  		glBadPort = .F.

  		LOCAL i, lcGoodPorts
  		*~ lcGoodPorts used to build string displayed
  		*~ in THISFORM.lblPORTS.CAPTION
  		lcGoodPorts = []

  		*~ Set initial properties of MSCOMM .OCX
  		WITH THIS.ocxCOMM
  			.RThreshold = 1
  			.SThreshold = 1
  			.Settings = [9600,n,8,1]
  			.RTSEnable = .F.
  			.InputLen = 0
  		ENDWITH

  		*~ Loop through 10 ports, trying to open each.
  		FOR i = 1 TO 10
  			WITH THIS.ocxCOMM
  				.CommPort = i
  				.PortOpen = .T.

  		*~      glBadPort set to .T. by THIS.ERROR if the # was bad.
  				IF !glBadPort
  		*~          Expand the array if i>1. The array is initally 1 row.
  					IF i > 1
  						DIMENSION THIS.aCommPorts[ALEN(THIS.aCommPorts,1)+1,2]
  					ENDIF
  		*~          Valid port, so store the port # to the first column 
  		*~          and a blank to the second column of the current row
  		*~          in THISFORM.aCommPorts. We will use this space later
  		*~          in THISFORM.COMMAND1.CLICK() to store return value
  		*~          communication with the port.
  		*~          Also store the port # to the lcGoodPorts variable.
  					THIS.aCommPorts(i,1) = i
  					THIS.aCommPorts(i,2) = []
  					lcGoodPorts = lcGoodPorts + ALLT(STR(i)) + [  ]
  				ENDIF

  		*~      Reset glBadPort for next loop.
  				glBadPort = .F.
  		*~      Close the port if it was opened to avoid error on next try.
  				IF .PortOpen
  					.PortOpen = .F.
  				ENDIF
  			ENDWITH
  		ENDFOR

  		*~ List the valid ports in THISFORM.lblPORTS.CAPTION
  		THIS.lblPORTS.CAPTION = lcGoodPorts
  		RELEASE glBadPort
  	ENDPROC

  	PROCEDURE ocxCOMM.OnComm
  		*** ActiveX Control Event ***

  		*~ CommEvent = 2 means the control received data.

  		*~  Store the data the control received to
  		*~  the appropriate array row/column.
  		*~  The row/column to store to is determined by
  		*~  global variable "gnPortNum" which is set in the
  		*~  thisform.command1.click() loop.

  		IF THIS.commevent = 2
  			THISFORM.aCommPorts(gnPortNum,2) = ;
  				THISFORM.aCommPorts(gnPortNum,2) + THIS.INPUT
  		ENDIF
  	ENDPROC

  	PROCEDURE command1.Error
  		LPARAMETERS nError, cMethod, nLine
  		*~ Trap OLE error from the COMM .OCX

  		*~ nError = 1429: we experienced an OLE error. This may
  		*~ be because we tried to open or communicate via
  		*~ the port and there is a non-standard object attached to
  		*~ it (such as a printer). Do nothing here; it's not a modem
  		*~ since we cannot open AND communicate with it.
  		*~ You may want to enhance this error trap.

  		IF INLIST(nError, 1429, 1426)
  		ELSE
  		*~  We experienced a non - OLE error. Report it.
  			MESSAGEBOX([Error #: ] + ALLT(STR(nError)) + CHR(13) + ;
  				[MESSAGE: ] + MESSAGE() + CHR(13) + ;
  				[LINE WITH ERROR: ] + ALLT(STR(nLine)) + CHR(13) + ;
  				[Method: ] + cMethod)
  		ENDIF
  	ENDPROC

  	PROCEDURE command1.Click
  		*~ gnPortNum is used in the ONCOMM event of the .OCX
  		PUBLIC gnPortNum
  		LOCAL i, lnModemPort
  		lnModemPort = 0

  		WITH THISFORM
  		*~  Loop through the form array...
  			FOR i = 1 TO ALEN(.aCommPorts,1)
  				gnPortNum = i
  				WAIT WINDOW [Testing COM ] + ;
  					ALLT(STR(.aCommPorts(i,1))) + [...] NOWAIT NOCLEAR
  				IF .ocxCOMM.PortOpen
  					.ocxCOMM.PortOpen = .F.
  				ENDIF
  		*~       Open the port and send standard Hayes-compatible
  		*~       commands to it. These commands first reset ([ATZ])
  		*~       the modem if it's there, then tell it to return
  		*~       an ID string ([ATI0]). The return string from a modem
  		*~       will contain an [OK], which is how we can tell it's a modem.
  				.ocxCOMM.CommPort = .aCommPorts(i,1)
  				.ocxCOMM.PortOpen = .T.
  				.ocxCOMM.OUTPUT = [ATZ] + CHR(13)
  				.WaitForResponse(2)
  				.ocxCOMM.OUTPUT = [ATI0] + CHR(13)
  				.WaitForResponse(2)
  				WAIT CLEAR
  			ENDFOR
  			WAIT WINDOW [Compiling Results...] NOWAIT NOCLEAR
  			.timer1.ENABLED = .T.
  		ENDWITH
  	ENDPROC

  	PROCEDURE timer1.Timer
  		*~ Clear the variable used in the ONCOMM event
  		*~ of the .OCX.
  		RELE gnPortNum
  		LOCAL i, lnModemPort
  		lnModemPort = 0
  		*~ Disable this timer so the code only runs once.
  		THIS.ENABLED = .F.

  		WITH THISFORM
  		*~  Close the port if it is open.
  			.ocxCOMM.PortOpen = .F.

  		*~  Loop through the completed array.
  		*~  If the first element is <> 0, 
  		*~  check the second in the row for an "OK".
  		*~  If present, that means the port has a modem.
  			FOR i = 1 TO ALEN(.aCommPorts,1)
  				IF .aCommPorts(i,1) <> 0
  					IF [OK] $ UPPER(.aCommPorts(i,2))
  		*~	            Modem found, exit the loop.
  						lnModemPort = .aCommPorts(i,1)
  						EXIT
  					ENDIF
  				ENDIF
  			NEXT i
  		ENDWITH

  		*~ Report to user.
  		WAIT CLEAR
  		IF  lnModemPort <> 0
  			MESSAGEBOX([Modem Found On COM ] + ALLT(STR(lnModemPort)),0,[])
  		ELSE
  			MESSAGEBOX([No Modem Found.] ,0,[])
  		ENDIF
  	ENDPROC

  ENDDEFINE
  *
  *-- EndDefine: form1
  **************************************************

  DEFINE CLASS MSCOMM_OCX AS OLECONTROL
               OLECLASS = [MSCOMMLib.MSComm.1]
  ENDDEFINE 

2. Save and run the program.

3. A form with the title "Com Port Test" lists the active COM ports on the
  computer. Click the Test Ports for Modem command button to test each of the
  listed ports.

4. When testing is complete, a message box reports which port has an available
  modem.

NOTE: In its current form, this code only tests for the presence of one modem.
However, the TIMER1.TIMER() method code can be modified to test for multiple
modems.

REFERENCES
==========

For more information, see the following Web site:

  Hayes AT Command Set http://www.option.co.za/hayesat.htm
  (http://www.option.co.za/hayesat.htm)

(c) Microsoft Corporation 2001, All Rights Reserved. Contributions by Trevor
Hancock, Microsoft Corporation.


Additional query words:

======================================================================
Keywords          : kbActiveX kbCtrl kbOOP kbvfp500 kbvfp600 
Technology        : kbVFPsearch kbAudDeveloper kbVFP500 kbVFP600 kbVFP500a
Version           : :5.0,5.0a,6.0
Issue type        : kbhowto

=============================================================================

THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Copyright Microsoft Corporation 1986-2002.