Click here to Skip to main content
Click here to Skip to main content

Win32 SDK Serial Comm Made Easy

, 21 May 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
This article describes the creation of a non-MFC custom RS232 control.

Demo screenshot

Contents

Introduction

This is the fourth in a series of articles concerning Windows SDK development that I have written over the years. The first three are:

  1. Win32 SDK Data Grid View Made Easy [^]
  2. Win32 SDK C Tab Control Made Easy [^]
  3. Win32 SDK C Autocomplete Combobox Made Easy [^]

Several years ago, I wanted to have a simple to use, message based, serial port control that I could employ in my Win32 C projects. I started experimenting with the idea and playing around with the serial port in my spare time, but as so often happens, the project ended up on the shelf when I got busy again.

Recently, I came across that project and, with a better understanding of how serial port communications work on Windows, decided to finish it. Since I haven't encountered a serial port wrapper that takes this approach anywhere, I decided to publish it on The Code Project.

A message based RS232 component

The Windows SDK provides methods for configuring the serial port. Other methods are available for monitoring the port and raising events, as well as file stream methods for reading and writing data. Take it all together, and serial port communication can become a somewhat daunting exercise. This control packs most of that functionality into a single Windows class that can be included in a Win32 project and accessed using standard Windows messaging and notifications.

Using the serial port control

To begin, include the serial port control's header file in the project:

#include "serialport.h"

This serial port control is a message based, custom control, and as such must be initialized before use. One way to handle this is to call the initializer in the WinMain() method of the application just after the call to InitCommonControlsEx().

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
           LPSTR lpszCmdLine, int nCmdShow)
{
    INITCOMMONCONTROLSEX icc;
    WNDCLASSEX wcx;

    ghInstance = hInstance;

    /* Initialize common controls. Also needed for MANIFEST's */

    icc.dwSize = sizeof(icc);
    icc.dwICC = ICC_WIN95_CLASSES/*|ICC_COOL_CLASSES|ICC_DATE_CLASSES|
                   ICC_PAGESCROLLER_CLASS|ICC_USEREX_CLASSES*/;
    InitCommonControlsEx(&icc);

    InitSerialPortControl(hInstance);

To make things simple though, I combined this step in the control's pseudo constructor. It is called only once, the first time a new serial port control is instantiated.

HWND New_SerialPortControl(HWND hParent, DWORD dwID)
{
    static ATOM aSerialPortControl = 0;
    static HWND hSerial;

    HINSTANCE hinst = (HINSTANCE) GetWindowLongPtr(hParent,GWLP_HINSTANCE);

    // Only need to register serial control once
    if (!aSerialPortControl)
        aSerialPortControl = InitSerialPortControl(hinst);

    hSerial = CreateWindowEx(0, g_szClassName, _T(""), WS_CHILD, 0, 0, 0, 0, 
                             hParent, (HMENU)dwID, hinst, NULL);

    return hSerial;
}

Here is a snippet showing a very simple write/read operation with default configurations and no serial port monitoring:

char buf[256];
memset(&buf,0,sizeof(buf));

// Create an instance of the control
HWND hCom = New_SerialPortControl(hDialog,IDC_SERIAL);

// Open Com 2 with default settings
CONFIG cg;
cg.mask = SPCF_PORTNAME;
cg.pszPortName = "COM2";

if(SerialPort_Open(hCom, &cg))
{
  //Request ID of Modem attached to COM2
  SerialPort_WriteString(hCom, "ATI3\r");

  Sleep(1000);//Wait a second

  SerialPort_ReadString(hCom, &buf, 255);
}

SerialPort_Close(hCom);

Here is a snippet showing a more robust write/read technique, polling the serial port to determine whether all of the bytes have been received before reading them from the port.

LPTSTR Query(HWND hSerial, LPTSTR szCommand, INT iTimeout)
{
     LPTSTR buf;

     SerialPort_WriteString(hSerial, szCommand);

     DWORD dwTimeStamp = timeGetTime();
     BOOL fElapsedTime = FALSE;
     BOOL found = FALSE;

     //Wait for first bytes
     do
     {
          if (SerialPort_BytesToRead(hSerial) > 0) found = TRUE;
          fElapsedTime = timeGetTime() - dwTimeStamp > iTimeout;
          Sleep(100);
     } while (!fElapsedTime && !found);

     DWORD dwCount = 0;

     BOOL fReading = TRUE;
     while(fReading)//Wait till all data is sent.
     {
          if (0 == SerialPort_BytesToRead(hSerial)) fReading = FALSE;
          else if (dwCount == SerialPort_BytesToRead(hSerial)) fReading = FALSE;
          else dwCount = SerialPort_BytesToRead(hSerial);
          Sleep(100);//Give the serial port time to catch up
     }
     
     if(1 > dwCount)
         return "";
     else
     {
          //Allocate temp buffer and get bytes
          buf = TmpStr(dwCount);
          SerialPort_ReadString(hSerial, buf, dwCount);
          return buf;
     }
}

And here is a snippet showing the use of the SPN_DATARECEIVED notification to respond to the arrival of bytes on the serial port:

static LRESULT MainDlg_OnNotify(HWND hwnd, INT id, LPNMHDR pnm)
{
     switch(pnm->code)
     {
          case SPN_DATARECEIVED:
          {
              if(EV_RXFLAG == ((LPNMSERIAL) pnm)->dwCode) return FALSE;

              DWORD dwCount = 0;
              BOOL fReading = TRUE;
              while(fReading)//Wait till all data is sent.
              {
                  if (0 == SerialPort_BytesToRead(pnm->hwndFrom)) fReading = FALSE;
                  else if (dwCount == SerialPort_BytesToRead(pnm->hwndFrom)) fReading = FALSE;
                  else dwCount = SerialPort_BytesToRead(pnm->hwndFrom);
                  Sleep(100);//Give the serial port time to catch up
              }

              if (0 == dwCount) return FALSE;

              //Allocate temp buffer and get bytes
              LPTSTR lpBuf = TmpStr(dwCount);

              SerialPort_ReadBytes(pnm->hwndFrom, lpBuf, dwCount)
          }
     }
     return TRUE;
}

These examples show some of the ways that the control can be simply implemented in a Win32 project. To demonstrate the class in a useful context, I put together a demo that makes use of two modems connected to two land lines. I happened to have the lines and hardware available to play with, but even without them, the demo code should give a fairly complete idea of how to use the class.

What follows is a programming reference for the serial port control class.

Public data structures

CONFIG

The CONFIG structure specifies or receives attributes for an instance of the serial port control.

typedef struct tagConfig {
     UINT mask;
     LPTSTR pszPortName;
     INT cchTextMax;
     DWORD dwBaudRate;
     BYTE bParity;
     BYTE bDataBits;
     BYTE bStopBits;
     BOOL fDiscardNull;
     FLOWCONTROL flowControl;
} CONFIG, *LPCONFIG;

Members

  • mask: A set of bit flags that specify the attributes of this data structure or of an operation that is using this structure. The following bit flags specify the members of the CONFIG structure that contain valid data or need to be filled in. One or more of these bit flags may be set:
    • SPCF_PORTNAME - The pszPortName member is valid or needs to be filled in.
    • SPCF_BAUDRATE - The dwBaudRate member is valid or needs to be filled in.
    • SPCF_PARITY - The bParity member is valid or needs to be filled in.
    • SPCF_DATABITS - The bDataBits member is valid or needs to be filled in.
    • SPCF_STOPBITS - The bStopBits member is valid or needs to be filled in.
    • SPCF_NULLDISCARD - The fDiscardNull member is valid or needs to be filled in.
    • SPCF_FLOWCONT - The flowControl member is valid or needs to be filled in.
    • SPCF_ALLSETTINGS - All members are valid or need to be filled in.
  • pszPortName - Pointer to a null-terminated string that contains the port name (e.g.: "COM1") if the structure specifies item attributes. If the structure is receiving item attributes, this member is the pointer to the buffer that receives the item text.
  • cchTextMax - Size of the buffer pointed to by the pszPortName member if the structure is receiving item attributes. If the structure specifies item attributes, this member is ignored.
  • dwBaudRate - Specifies the baud rate at which the communications device operates. This member can be an actual baud rate value, or one of the following baud rate indexes:
    • CBR_110 - 110
    • CBR_300 - 300
    • CBR_600 - 600
    • CBR_1200 - 1200
    • CBR_4800 - 4800
    • CBR_9600 - 9600
    • CBR_14400 - 14400
    • CBR_19200 - 19200
    • CBR_38400 - 38400
    • CBR_56000 - 56000
    • CBR_57600 - 57600
    • CBR_115200 - 115200
    • CBR_128000 - 128000
    • CBR_256000 - 256000
  • bParity - Specifies the parity scheme to be used. This member can be one of the following values:
    • NOPARITY - No parity
    • ODDPARITY - Odd
    • EVENPARITY - Even
    • MARKPARITY - Mark
    • SPACEPARITY - Space
  • bDataBits - Specifies the number of bits in the bytes transmitted and received.
  • bStopBits - Specifies the number of stop bits to be used. This member can be one of the following values:
    • ONESTOPBIT - 1 stop bit
    • ONE5STOPBITS - 1.5 stop bits
    • TWOSTOPBITS - 2 stop bits
  • fDiscardNull - Specifies whether null bytes are discarded. If this member is TRUE, null bytes are discarded when received.
  • flowControl - Specifies the type of flow control desired/employed. This member can be one of the following enumerated values:
    • NoFlowControl
    • CtsRtsFlowControl
    • CtsDtrFlowControl
    • DsrRtsFlowControl
    • DsrDtrFlowContro
    • XonXoffFlowControl
  • Remarks - The address of this structure is specified as the lParam parameter of the SPM_SETCONFIG, SPM_GETCONFIG, and SPM_OPEN messages.

FLOWCONTROL

The FLOWCONTROL enumerated type is used in the CONFIG structure to specify the type of flow control desired or employed by the serial port control.

typedef enum tagFlowControl {
    NoFlowControl,
    CtsRtsFlowControl,
    CtsDtrFlowControl,
    DsrRtsFlowControl,
    DsrDtrFlowControl,
    XonXoffFlowControl
}FLOWCONTROL;

Messages and Macros

Configure the control to do what you want using Windows messages. To make this easy and as a way of documenting the messages, I created macros for each message. If you prefer to call SendMessage() or PostMessage() explicitly, please refer to the macro defs in the header for usage.

SerialPort_GetPortNames

LPTSTR* SerialPort_GetPortNames(
     HWND hwnd
     LPDWORD lpCount
     );
/* Parameters
hwnd
     Handle to the serial port control.
lpCount
     Pointer to number of items returned.

Return Values
Returns a list of installed serial ports.*/

SerialPort_SetConfigurations

BOOL SerialPort_SetConfigurations(
     HWND hwnd
     LPCONFIG lpConfig
     );
/*Parameters
hwnd
     Handle to the serial port control.
lpConfig
     Pointer to a CONFIG structure that contains configuration attributes.
Return Values
Returns TRUE if successful or FALSE otherwise.*/

SerialPort_GetConfigurations

BOOL SerialPort_GetConfigurations(
     HWND hwnd
     LPCONFIG lpConfig
     );
/*Parameters
hwnd
     Handle to the serial port control.
lpConfig
     Pointer to a CONFIG structure that specifies the information to retrieve and receives 
     information about the serial port control.
     
Return Values
Returns TRUE if successful or FALSE otherwise.*/

SerialPort_SetReadTimeout

BOOL SerialPort_SetReadTimeout(
     HWND hwnd
     DWORD dwTimeout
     );
/*Parameters
hwnd
     Handle to the serial port control.
dwTimeout
     Read timeout in milliseconds.

Return Values
Returns TRUE if successful or FALSE otherwise.*/

SerialPort_GetReadTimeout

DWORD SerialPort_GetReadTimeout(
     HWND hwnd
     );
/*Parameters
hwnd
     Handle to the serial port control.

Return Values
Returns Read timeout in milliseconds.*/

SerialPort_SetWriteTimeout

BOOL SerialPort_SetWriteTimeout(
     HWND hwnd
     DWORD dwTimeout
     );
/*Parameters
hwnd
     Handle to the serial port control.
dwTimeout
     Write timeout in milliseconds.

Return Values
Returns TRUE if successful or FALSE otherwise.*/

SerialPort_GetWriteTimeout

DWORD SerialPort_GetWriteTimeout(
     HWND hwnd
     );
/*Parameters
hwnd
     Handle to the serial port control.

Return Values
Returns Write timeout in milliseconds.*/

SerialPort_GetCTS

BOOL SerialPort_GetCTS(
     HWND hwnd
     );
/*Parameters
hwnd
     Handle to the serial port control.

Return Values
Returns TRUE if the CTS (clear-to-send) signal is on.*/

SerialPort_GetDSRs

BOOL SerialPort_GetDSR(
     HWND hwnd
     );
/*Parameters
hwnd
     Handle to the serial port control.

Return Values
Returns TRUE if the DSR (data-set-ready) signal is on.*/

SerialPort_BytesToRead

DWORD SerialPort_BytesToRead(
     HWND hwnd
     );
/*Parameters
hwnd
     Handle to the serial port control.

Return Values
Returns The number of bytes available to read from the rx buffer.*/

SerialPort_BytesToWrite

DWORD SerialPort_BytesToWrite(
     HWND hwnd
     );
/*Parameters
hwnd
     Handle to the serial port control.

Return Values
Returns The number of bytes remaining in the tx buffer.*/

SerialPort_WriteBytes

BOOL SerialPort_WriteBytes(
     HWND hwnd
     LPBYTE lpData
     DWORD dwSize
     );
/*Parameters
hwnd
     Handle to the serial port control.
lpData
     pointer to byte data to write to port.
dwSize
     The number of bytes to write.
Return Values
Returns TRUE if successful or FALSE otherwise.*/

SerialPort_ReadBytes

DWORD SerialPort_ReadBytes(
     HWND hwnd
     LPBYTE lpBuf
     DWORD dwSize
     );
/*Parameters
hwnd
     Handle to the serial port control.
lpBuf
     pointer to a buffer to receive data.
dwSize
     The buffer size.
Return Values
Returns The number of bytes read.*/

SerialPort_WriteString

BOOL SerialPort_WriteString(
     HWND hwnd
     LPTSTR lpsztext
     );
/*Parameters
hwnd
     Handle to the serial port control.
lpsztext
     The string to write to port.
Return Values
Returns TRUE if successful or FALSE otherwise.*/

SerialPort_ReadString

DWORD SerialPort_ReadString(
     HWND hwnd
     LPTSTR lpszBuf
     DWORD dwSize
     );
/*Parameters
hwnd
     Handle to the serial port control.
lpszBuf
     pointer to a buffer to receive string.
dwSize
     The buffer size.
Return Values
Returns The number of characters read.*/

SerialPort_Close

BOOL SerialPort_Close(
     HWND hwnd
     );
/*Parameters
hwnd
     Handle to the serial port control.
Return Values
Returns TRUE if successful or FALSE otherwise.*/

SerialPort_Open

BOOL SerialPort_Open(
     HWND hwnd
     LPCONFIG lpConfig
     );
/*Parameters
hwnd
     Handle to the serial port control.
lpConfig
     Pointer to a CONFIG structure that contains configuration attributes.
     
Return Values
Returns TRUE if successful or FALSE otherwise.*/

SerialPort_FlushReadBuf

BOOL SerialPort_FlushReadBuf(
     HWND hwnd
     );
/*Parameters
hwnd
     Handle to the serial port control.
Return Values
Returns TRUE if successful or FALSE otherwise.*/

SerialPort_FlushWriteBuf

BOOL SerialPort_FlushWriteBuf(
     HWND hwnd
     );
/*Parameters
hwnd
     Handle to the serial port control.
Return Values
Returns TRUE if successful or FALSE otherwise.*/

SerialPort_SetReceiveByteThreshold

BOOL SerialPort_SetReceiveByteThreshold(
     HWND hwnd
     DWORD dwNumBytes
     );
/*Parameters
hwnd
     Handle to the serial port control.
dwNumBytes
     The number of bytes necessesary to trigger the SPN_DATARECEIVED 
     notification.
Return Values
Returns TRUE if successful or FALSE otherwise.*/

SerialPort_GetReceiveByteThreshold

DWORD SerialPort_GetReceiveByteThreshold(
     HWND hwnd
     );
/*Parameters
hwnd
     Handle to the serial port control.
Return Values
Returns The number of bytes necessesary to trigger 
        the SPN_DATARECEIVED notification.*/

Notifications

The serial port control provides several notifications via WM_NOTIFY. The lParam parameter of these notification messages points to an NMSERIAL structure.

NMSERIAL

The NMSERIAL structure contains information about a serial port control notification message.

typedef struct tagNMSERIAL {
     NMHDR hdr;
     DWORD dwCode;
} NMSERIAL, *LPNMSERIAL;

/*Members
hdr
     Specifies an NMHDR structure. The code member of the NMHDR structure can 
     one of the following notification codes that identify the message being sent:
          SPN_DATARECEIVED,
          SPN_PINCHANGED,
          SPN_ERRORRECEIVED.
dwCode
     Specifies a notification-specific code. 
     
Remarks
     The address of this structure is specified as the lParam parameter of the 
     WM_NOTIFY message for all serial port control notification messages.*/

SPN_DATARECEIVED

The SPN_DATARECEIVED notification message notifies a serial port control's parent window that data was received. This notification message is sent in the form of a WM_NOTIFY message.

SPN_DATARECEIVED 
pnm = (NMSERIAL *) lParam; 
 
/*Parameters
pnm
     Pointer to an NMSERIAL structure that specifies 
     one of the following notification specific codes:
          EV_RXCHAR - The receive byte threshold number of characters 
          was received and placed in the  input buffer.
          EV_RXFLAG - The end of file character was received and 
          placed in the input buffer.

Return Values
No return value.*/

SPN_PINCHANGED

The SPN_PINCHANGED notification message notifies a serial port control's parent window that a pin has changed. This notification message is sent in the form of a WM_NOTIFY message.

SPN_PINCHANGED 
pnm = (NMSERIAL *) lParam; 
 
/*Parameters
pnm
     Pointer to an NMSERIAL structure that specifies one of the following notification 
     specific codes:
          EV_CTS - The Clear to Send (CTS) signal changed state.
          EV_DSR - The Data Set Ready (DSR) signal changed state.
          EV_RLSD - The Carrier Detect (CD) signal changed state.
          EV_RING - A ring indicator was detected.
          EV_BREAK - A break was detected on input.
          
Return Values
No return value.*/

SPN_ERRORRECEIVED

The SPN_ERRORRECEIVED notification message notifies a serial port control's parent window that an error was received. This notification message is sent in the form of a WM_NOTIFY message.

SPN_ERRORRECEIVED 
pnm = (NMSERIAL *) lParam; 
 
/*Parameters
pnm
     Pointer to an NMSERIAL structure that specifies one of the following notification 
     specific codes:
          CE_TXFULL - The application tried to transmit a character, 
          but the output buffer was full.
          CE_RXOVER - An input buffer overflow has occurred.
          CE_OVERRUN - A character-buffer overrun has occurred.
          CE_RXPARITY - The hardware detected a parity error.
          CE_FRAME - The hardware detected a framing error.
          
Return Values
No return value.*/

Receiving concurrent notifications

In order to have a serial port control with notifications, I employed asynchronous overlapped IO along with WaitCommEvent() in a working thread that listens to the serial port and posts any activity to the main thread. I created methods to launch, and if necessary, terminate this thread. Here is the code that runs in the working thread:

DWORD WINAPI Listner_Proc(LPVOID StartParam)
{
    BOOL fStarting = TRUE;
    DWORD dwEvtMask = 0;
    OVERLAPPED ov;

    ov.Offset = 0;
    ov.OffsetHigh = 0;
    ov.hEvent = g_lpInst->hListnerEvent;
    ResetEvent(ov.hEvent);

    if (INVALID_HANDLE_VALUE != g_lpInst->hComm)
    {
        while (!g_lpInst->fEndListner)
        {
            // Specify the events and start the event thread
            if (SetCommMask(g_lpInst->hComm, EV_BREAK | EV_CTS | 
                  EV_DSR | EV_ERR | EV_RING | EV_RLSD | EV_RXCHAR | EV_RXFLAG))
            {
                //Tells the main thread that this thread is ready for action.
                if (fStarting)
                {
                    SetEvent(g_lpInst->hStartEvent);
                    fStarting = FALSE;
                }
                if (!WaitCommEvent(g_lpInst->hComm, &dwEvtMask, &ov))
                {
                    if (GetLastError() == ERROR_IO_PENDING)
                    {
                        DWORD numBytes;
                        BOOL flag = WaitForSingleObject(ov.hEvent, -1);
                        do
                        {
                            flag = GetOverlappedResult(g_lpInst->hComm, 
                                                       &ov, &numBytes, FALSE);
                        }
                        while ((GetLastError() == ERROR_IO_PENDING) && !flag);
                    }
                }
                PostMessage((HWND)StartParam, SPM_DISPATCHNOTIFICATIONS, 
                            (DWORD)dwEvtMask, 0L);
            }
        }
    }
    return 0xDEAD;
}

Posted event messages are queued, and will be processed by the main thread in the order that they are received by the SPM_DISPATCHNOTIFICATIONS event handler. The upside of this is that you can respond to notifications and update the user interface without the threat of cross threading. The downside is that no new notifications will reach the parent's WM_NOTIFY handler until you have finished responding to the current notification.

One scenario where this behavior is undesirable is when the application is handling the SPN_DATARECEIVED notification but you want to monitor pin changes concurrently. The notifications will arrive after they are needed when the SPN_DATARECEIVED handler returns. The solution is to launch a working thread in response to the SPN_DATARECEIVED notification so that WM_NOTIFY returns to process the next queued notification.

Here is an example of this technique:

static LRESULT MainDlg_OnNotify(HWND hwnd, INT id, LPNMHDR pnm)
{
     switch(pnm->code)
     {
          case SPN_DATARECEIVED:
          {
              if(EV_RXFLAG == ((LPNMSERIAL) pnm)->dwCode) return FALSE;
              
              if(NULL == hWorkerThread)
              {
                    _hWorkerThread = CreateThread(NULL, 0, On_DataReceived, 
                                                 hwnd, 0, &dwWorkerID);
                    SetThreadPriority(_hWorkerThread, THREAD_PRIORITY_NORMAL);
              }
          }
          case SPN_PINCHANGED:
          {
               OnPinChanged(hwnd, pnm);
          }
     }
}

Once the SPN_DATARECEIVED handler completes, be sure to set the stored handle to NULL so that a new thread will be launched in response to the next SPN_DATARECEIVED notification.

DWORD WINAPI On_DataReceived(LPVOID StartParam)
{
    __try
    {
        //
        // Do Stuff
        //
    }
    __finally
    {
        _hWorkerThread = NULL;
    }
    return 0;
}

History

  • October 12, 2009: Version 1.0.0.0.
  • August 3, 2010: Version 1.1.0.0 - Fixed allocation bug in GetPortNames().
  • May 18, 2011: Version 1.2.0.0 - Several bug fixes: I rewrote TerminateListner() method to exit listner thread gracefully instead of using the less desirable TerminateThread().
  • May 20, 2011: Version 1.3.0.0 - Fixed bug introduced in the GetPortNames() method during last update.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

David MacDermot

United States United States
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pinmemberw-peuker26-Jul-12 1:43 
GeneralVisualStudio 2010 PinmemberS.Simone25-May-11 7:54 
GeneralRe: VisualStudio 2010 PinmemberDavid MacDermot25-May-11 13:26 
GeneralSome Hints PinmemberRene Koenig18-May-11 2:55 
I only had a quick look so far, but I see some serious problems: First, you call TerminateThread when closing the port. Did you notice, that the thread's stack remains allocated when running under XP? When using the defaults, you get a hole of 1 MB on every close in your process's address space. And nobody is closing ov.hEvent...
 
Again when closing the port (also when opening the port), you're checking the port's handle:
if (g_lpInst->hComm != NULL || g_lpInst->hComm != INVALID_HANDLE_VALUE)
 
This short-form of this is this:
if (g_lpInst->hComm)
 
I'm quite sure that this is not what you meant to say, you meant to say:
if (g_lpInst->hComm != NULL && g_lpInst->hComm != INVALID_HANDLE_VALUE)
 
Not a problem but wrong:
GetLastError(); // just clear any error
 
GetLastError simply reads the last set error-code, GetLastError does not clear anything. All these stand-alone calls are superfluous.
 
Also not a problem but wrong is the initialization of lenName prior the call of RegEnumValues. The function expects the buffer-size in bytes, not in array-elements. Simply use sizeof.
 
BTW: You should check the values returned by malloc and realloc! And when talking about malloc, another thing comes up. You allocate *lpCount + 1 PTCHARs in GetPortNames, but I can't see the initialization of the last element. Maybe I'm missing something, but I would add a line to GetPortNames:
(*lpPortList)[*lpCount] = NULL;
GeneralRe: Some Hints PinmemberDavid MacDermot18-May-11 13:13 
GeneralRe: Some Hints PinmemberRene Koenig20-May-11 4:24 
GeneralRe: Some Hints PinmemberDavid MacDermot20-May-11 5:47 
QuestionWarnings Pinmembermoeterieks23-Apr-11 21:29 
AnswerRe: Warnings PinmemberDavid MacDermot27-Apr-11 6:38 
GeneralRe: Warnings Pinmembermoeterieks3-May-11 4:15 
QuestionCross-thread Rx/Tx PinmemberHsiao12-Jan-11 17:14 
AnswerRe: Cross-thread Rx/Tx PinmemberDavid MacDermot14-Jan-11 6:35 
GeneralMy vote of 5 Pinmember__erfan__2-Nov-10 3:07 
GeneralThis could be my silver bullet PinmemberJohn Whitmire17-Aug-10 7:46 
GeneralRe: This could be my silver bullet [modified] PinmemberDavid MacDermot17-Aug-10 8:37 
GeneralMy vote of 3 PinmemberRolf Kristensen15-Aug-10 10:39 
GeneralRe: My vote of 3 PinmemberDavid MacDermot16-Aug-10 6:39 
GeneralRe: My vote of 3 PinmemberRolf Kristensen16-Aug-10 6:44 
GeneralRe: My vote of 3 PinmemberDezhi Zhao16-Aug-10 12:02 
GeneralRe: My vote of 3 Pinmembermin_2_max23-May-11 15:42 
GeneralRe: My vote of 3 PinmemberDezhi Zhao24-May-11 7:46 
GeneralRe: My vote of 3 [modified] Pinmembermin_2_max24-May-11 15:56 
GeneralRe: My vote of 3 Pinmembermin_2_max24-May-11 18:43 
GeneralGetPortNames PinmemberMember 46874812-Aug-10 10:19 
GeneralRe: GetPortNames PinmemberDavid MacDermot12-Aug-10 15:26 
GeneralThanks PinmemberFastfootskater9-Mar-10 6:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 21 May 2011
Article Copyright 2009 by David MacDermot
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid