Solved

Query available COM-ports

Posted on 2002-07-26
17
436 Views
Last Modified: 2013-12-03
How to retrieve from windows what hardware is connected. I need to know how many COM-ports the system has.
Thanks,
Luc
0
Comment
Question by:LucHoltkamp
  • 6
  • 4
  • 3
  • +3
17 Comments
 
LVL 32

Expert Comment

by:jhance
ID: 7181267
Windows version?
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 7181269
You could use something like:

BOOL CommPortAvailable( unsigned int index ) {

    HANDLE hFile = NULL;
    TCHAR com_port[MAX_PATH];
    sprintf( com_port, "COM%d", index );
    hFile = CreateFile( com_port,
            0,
            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
            NULL,
            OPEN_EXISTING,
            0,
            NULL );
    if( INVALID_FILE_HANDLE != hFile ) {
        CloseHandle();
        return( TRUE );
    }
    return( FALSE );
}

unsigned int GetCommCount() {
    unsigned int count = 0;
    while( CommPortAvailable( ++count ) );
    return( --count );
}

There may be a better approach -- probably a system call. But this one should work at least....
0
 
LVL 86

Expert Comment

by:jkr
ID: 7181412
>>There may be a better approach

Nope, that's it - I'd have suggested the same...
0
 
LVL 3

Author Comment

by:LucHoltkamp
ID: 7182712
I see you use 0 as desired access mode. Would this also work if another program already opened the com-port with exclusive access??
But how is it possible you can't get any information about the hardware in the PC?, There must be some bios/plug&play info somewhere??
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 7182899
The MSDN states for dwDesiredAccess:

0 : Specifies device query access to the object. An application can query device attributes without accessing the device.

So I believe there shouldn't be a problem if any other application has the comm port in use. I agree on your second question though -- I didn't think that my code would be the best you can get. I don't know if jkr is really into this stuff and knows for sure. I certainly don't. I have looked through system calls to directly access hardware drivers by sending commands but couldn't find anything about comm ports.

I guess either jkr or some other expert would be the one to give you a definate answer.
0
 
LVL 86

Expert Comment

by:jkr
ID: 7182904
If another program opened a port, this port is no longer available until that other program realeases it. Period. :o)

(you would need certain serial protocols and at at least an arbiter/demux if not)
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 7182908
So are you saying that it is no longer available for access or cannot not even be queried?
0
 
LVL 86

Expert Comment

by:jkr
ID: 7182913
Hmm, it is simply that "CreateFile()" on a "port in use" will fail.
0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
LVL 8

Expert Comment

by:fl0yd
ID: 7182921
In that case the above code doesn't return the number of COM-Ports in a system, but only the subset that currently isn't in use.

Would you know of a registry key that could be queried to find out the number of physically present com-ports?
0
 
LVL 3

Author Comment

by:LucHoltkamp
ID: 7183956
Let me give you abit of background-info:
We are making a product that can be configured via RS232. The question from the marketing-department is to use auto-detection. So I made a little class that installs a thread for each available COMM-port, that looks for the device. If it finds one it returns the COMM bort and baudrate to the client-code. To anticipate the fact that a user can have several devices already running it releases and reclaims the port if it finds nothing. In this way it will also find devices that were used by another program before. However if I do not know which ports are present on the system I have to test the whole possible range of COMM-ports over and over again. This is wastefull of course.

But the OS itself knows what is present on the system... Or must I assume Microsoft doesn't want user-mode apps to know what hardware is installed?

Luc
0
 
LVL 4

Expert Comment

by:mblat
ID: 7184186
Actually....

   DWORD dwNeeded,dwReturned;

    EnumPorts(
  NULL,       // pointer to server name
  1,        // specifies type of port info structure
  szTarget,      // pointer to buffer to receive array of port
                      // info. structures
  10000,        // specifies size, in bytes, of buffer
  &dwNeeded,  // pointer to number of bytes stored into
                      // buffer (or required buffer size)
  &dwReturned  // pointer to number of PORT_INFO_*.
                      // structures stored into buffer
);

    unsigned char* pb = szTarget;
    for(int ix = 0; ix < dwReturned; ix++)
    {
        PORT_INFO_1* p = (PORT_INFO_1*)pb;
        pb += sizeof(PORT_INFO_1);
    }
That will return you ALL ports on the system, including COM, LPT and others you may have.  All you need to do is to look for ones that start with "COM".  Only then I would try to open them with CreateFile to see if they "really" present, cause it is possible that some junk will be returned from EnumPorts....

Of cause 10000 is just some "VERY BIG" number.  Look at EnumPorts for proper use of it...


Hope it helps...
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 7184200
Are you sure this enumerates com ports? The MSDN says: "The EnumPorts function enumerates the ports that are available for printing on a specified server." Being located in the "Platform SDK: Windows GDI"-section makes me a bit suspicious.
0
 
LVL 4

Accepted Solution

by:
mblat earned 200 total points
ID: 7184205
It enumerates ALL communication port....

But really there is even better way: ( just found it in one of my projects)

unsigned char szTarget[10000];
DWORD dwBufferSize = QueryDosDevice(
                              NULL, // pointer to MS-DOS device name string
                              (char*)szTarget,  // pointer to buffer for storing query results
                              10000         // maximum storage capacity of buffer
                            );
 
   DWORD dwWordIdx = 0;
   CString cs;

   do
   {
       cs.Empty();

       while(szTarget[dwWordIdx] != 0)
       {
           cs += szTarget[dwWordIdx];
           dwWordIdx++;
       }

       cs.MakeUpper();
       if(cs.Find("COM") != -1)
           TRACE("Found: %s\n",cs);
       dwWordIdx++;
   }
   while(dwWordIdx < dwBufferSize);

Well,,, methid of retrieving 0 terminated strings from the buffer is somewhat sloppy - I admit that, but that was done more for demonstration that as "REAL" code.

But this DOES return ALL installed DOS devices on the system and com ports happend to be one of them....
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 7185189
It IS possible to open any file/device with access 0 even when it is already opened exclusively for read and write.
It is even explicitly allowed to do DeviceIoControl calls on the file descriptor.
0
 
LVL 3

Author Comment

by:LucHoltkamp
ID: 7189300
Thank you guys for this great information!
I made a nice little function from it that I will share with you all, it work just great :-)
The function returns the devices in a vector<string> and has some search options:

Usage:

vector<string> vec;
QueryDosDevice(vec);              // find all devices
QueryDosDevice(vec, NULL, "COM"); // find COM devices
//etc

//START OF CPP FILE
#include <windows.h>
#include <string.h>
#include <string>
#include <vector>

using namespace std;

//*********************************************************************
/*!
    \brief Query dos devices
    \pre None
    \post None
    \param ret            [OUT] This vector will be filled
                                with the found devices
    \param pzsDevice      [IN]  Device to Query, if NULL all
                                devices are returned
    \param pzsIncludeMask [IN]  Only devices that contain this
                                string are added to the vector.
                                If NULL or empty it is ignored.
    \param pzsExcludeMask [IN]  Devices that contain this
                                string are \b NOT added to the vector.
                                If NULL or empty it is ignored.
    \param bCaseSensitive [IN]  If true, the masks are case-sensitive
    \return \c true if succesfull, \c false otherwise.
*/
bool
QueryDosDevice( vector<string>& ret,
                const char* pzsDevice      = NULL,
                const char* pzsIncludeMask = NULL,
                const char* pzsExcludeMask = NULL,
                bool        bCaseSensitive = false
              )
{
    char* pBuffer;
    DWORD dwSize    = 256;
    DWORD dwLength  = 0;

    // try ::QueryDosDevice with an increasing buffer-size
    // until we know for certain we have all data
    while (0 == dwLength)
    {
        pBuffer = new char[dwSize];
        if (NULL == pBuffer)
        {
            return false;   // out of memory
        }

        dwLength = ::QueryDosDeviceA(pzsDevice, pBuffer, dwSize);

        if (0 == dwLength)
        {
            delete []pBuffer;
            if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
            {
                return false; // QueryDosDevice failed
            }
            // try a bigger size
            dwSize *= 2;
        }
    }
    // ok, we have our devices, now fill the vector
    ret.clear();

    // make our masks
    char *pzsIM = _strdup(NULL ==  pzsIncludeMask ? "" : pzsIncludeMask);
    if (NULL == pzsIM)
    {
        delete []pBuffer;
        return false;    // out of mem
    }
    char *pzsEM = _strdup(NULL ==  pzsExcludeMask ? "" : pzsExcludeMask);
    if (NULL == pzsEM)
    {
        delete []pBuffer;
        free(pzsIM);
        return false;    // out of mem
    }
    if (!bCaseSensitive)
    {
        _strupr(pzsIM);
        _strupr(pzsEM);
    }

    DWORD dwPos = 0;
    while ('\0' != pBuffer[dwPos])
    {
        char *ps = pBuffer + dwPos;

        // make string
        string str(bCaseSensitive ? ps : _strupr(ps));

        // filter
        if ( ( '\0' == *pzsIM  || string::npos != str.find(pzsIM) ) &&
             ( '\0' == *pzsEM  || string::npos == str.find(pzsEM) )
           )
        {
            ret.push_back(str);
        }

        // next string
        dwPos += strlen(ps) + 1;
    }  

    // cleanup
    delete []pBuffer;
    free(pzsIM);
    free(pzsEM);
    return true;
}
//END OF CPP FILE
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 7189387
Aiiii, so you're using Doxygen, too. I have encountered a problem with it though. I haven't updated lately, so maybe you could tell me if it has been fixed, or whether I'm doing something wrong. I usually like to put \todo tags right in the function at the place where it applies. If I do so, the tag is either connected to the next or previous function (don't remember which one). I'd like to hear from you.

.f
0
 
LVL 3

Author Comment

by:LucHoltkamp
ID: 8106022
About doxygen:

I make sure I do not place any doxygen comments INSIDE functions, only in headers etc.

Sometimes if doxygen loses track (with some macro's for instance), it often helps to set an explicit tag. So if you generate documentation, and you see it misplaces a comment, just add a \class or \def or \fn tag in the comment.

For \todo, I place it in the header of the function and I also use my own tags inside functions that I simply find with grep. Something like:
//@LH 3-10-2003: Commented out next line to fix PAR 3114, need to investigate further
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

This tutorial is about how to put some of your C++ program's functionality into a standard DLL, and how to make working with the EXE and the DLL simple and seamless.   We'll be using Microsoft Visual Studio 2008 and we will cut out the noise; that i…
This article shows how to make a Windows 7 gadget that extends its U/I with a flyout panel -- a window that pops out next to the gadget.  The example gadget shows several additional techniques:  How to automatically resize a gadget or flyout panel t…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now