occcont.cpp Line: 926

MFC 2019. 9. 13. 03:28

regsvr32

 

C:\Windows\SysWOW64\regsvr32

 

'MFC' 카테고리의 다른 글

MFC에서 DDK 사용 시  (0) 2018.03.26
Using the Windows Headers  (0) 2016.06.29
COM9 보다 큰 직렬 포트를 지정  (0) 2016.06.07
Serial Communications  (0) 2016.06.07
FormatMessage function  (0) 2016.06.07
Posted by 곰돌이짱
,

MFC에서 DDK 사용 시

MFC 2018. 3. 26. 20:16

 

C:\WinDDK\7600.16385.1\inc\ddk

 

$(VCInstallDir)include
$(VCInstallDir)atlmfc\include

 

C:\WinDDK\7600.16385.1\inc\api

 

$(WindowsSdkDir)\include
$(FrameworkSDKDir)include

 

 

 

 

 


'MFC' 카테고리의 다른 글

occcont.cpp Line: 926  (0) 2019.09.13
Using the Windows Headers  (0) 2016.06.29
COM9 보다 큰 직렬 포트를 지정  (0) 2016.06.07
Serial Communications  (0) 2016.06.07
FormatMessage function  (0) 2016.06.07
Posted by 곰돌이짱
,

Using the Windows Headers

MFC 2016. 6. 29. 16:07

The following table describes the preferred macros used in the Windows header files. If you define NTDDI_VERSION, you must also define _WIN32_WINNT.

Minimum system requiredValue for NTDDI_VERSION
Windows 8.1NTDDI_WINBLUE (0x06030000)
Windows 8NTDDI_WIN8 (0x06020000)
Windows 7NTDDI_WIN7 (0x06010000)
Windows Server 2008NTDDI_WS08 (0x06000100)
Windows Vista with Service Pack 1 (SP1)NTDDI_VISTASP1 (0x06000100)
Windows VistaNTDDI_VISTA (0x06000000)
Windows Server 2003 with Service Pack 2 (SP2)NTDDI_WS03SP2 (0x05020200)
Windows Server 2003 with Service Pack 1 (SP1)NTDDI_WS03SP1 (0x05020100)
Windows Server 2003NTDDI_WS03 (0x05020000)
Windows XP with Service Pack 3 (SP3)NTDDI_WINXPSP3 (0x05010300)
Windows XP with Service Pack 2 (SP2)NTDDI_WINXPSP2 (0x05010200)
Windows XP with Service Pack 1 (SP1)NTDDI_WINXPSP1 (0x05010100)
Windows XPNTDDI_WINXP (0x05010000)

 

The following tables describe other macros used in the Windows header files.

Minimum system requiredMinimum value for _WIN32_WINNT and WINVER
Windows 8.1_WIN32_WINNT_WINBLUE (0x0602)
Windows 8_WIN32_WINNT_WIN8 (0x0602)
Windows 7_WIN32_WINNT_WIN7 (0x0601)
Windows Server 2008_WIN32_WINNT_WS08 (0x0600)
Windows Vista_WIN32_WINNT_VISTA (0x0600)
Windows Server 2003 with SP1, Windows XP with SP2_WIN32_WINNT_WS03 (0x0502)
Windows Server 2003, Windows XP_WIN32_WINNT_WINXP (0x0501)

 

Minimum version requiredMinimum value of _WIN32_IE
Internet Explorer 10.0_WIN32_IE_IE100 (0x0A00)
Internet Explorer 9.0_WIN32_IE_IE90 (0x0900)
Internet Explorer 8.0_WIN32_IE_IE80 (0x0800)
Internet Explorer 7.0_WIN32_IE_IE70 (0x0700)
Internet Explorer 6.0 SP2_WIN32_IE_IE60SP2 (0x0603)
Internet Explorer 6.0 SP1_WIN32_IE_IE60SP1 (0x0601)
Internet Explorer 6.0_WIN32_IE_IE60 (0x0600)
Internet Explorer 5.5_WIN32_IE_IE55 (0x0550)
Internet Explorer 5.01_WIN32_IE_IE501 (0x0501)
Internet Explorer 5.0, 5.0a, 5.0b_WIN32_IE_IE50 (0x0500)

 

Setting WINVER or _WIN32_WINNT

You can define these symbols by using the #define statement in each source file, or by specifying the /D compiler option supported by Visual C++.

For example, to set WINVER in your source file, use the following statement:

#define WINVER 0x0502

To set _WIN32_WINNT in your source file, use the following statement:

#define _WIN32_WINNT 0x0502

To set _WIN32_WINNT using the /D compiler option, use the following command:

cl -c /D_WIN32_WINNT=0x0502 source.cpp

For information on using the /D compiler option, see /D (preprocessor definitions).

Note that some features introduced in the latest version of Windows may be added to a service pack for a previous version of Windows. Therefore, to target a service pack, you may need to define _WIN32_WINNT with the value for the next major operating system release. For example, the GetDllDirectory function was introduced in Windows Server 2003 and is conditionally defined if _WIN32_WINNT is 0x0502 or greater. This function was also added to Windows XP with SP1. Therefore, if you were to define _WIN32_WINNT as 0x0501 to target Windows XP, you would miss features that are defined in Windows XP with SP1.

Controlling Structure Packing

Projects should be compiled to use the default structure packing, which is currently 8 bytes because the largest integral type is 8 bytes. Doing so ensures that all structure types within the header files are compiled into the application with the same alignment the Windows API expects. It also ensures that structures with 8-byte values are properly aligned and will not cause alignment faults on processors that enforce data alignment.

For more information, see /Zp (struct member alignment) or pack.

Faster Builds with Smaller Header Files

You can reduce the size of the Windows header files by excluding some of the less common API declarations as follows:

  • Define WIN32_LEAN_AND_MEAN to exclude APIs such as Cryptography, DDE, RPC, Shell, and Windows Sockets.

    #define WIN32_LEAN_AND_MEAN

  • Define one or more of the NOapi symbols to exclude the API. For example, NOCOMM excludes the serial communication API. For a list of support NOapi symbols, see Windows.h.

    #define NOCOMM


'MFC' 카테고리의 다른 글

occcont.cpp Line: 926  (0) 2019.09.13
MFC에서 DDK 사용 시  (0) 2018.03.26
COM9 보다 큰 직렬 포트를 지정  (0) 2016.06.07
Serial Communications  (0) 2016.06.07
FormatMessage function  (0) 2016.06.07
Posted by 곰돌이짱
,

https://support.microsoft.com/ko-kr/kb/130869


Microsoft Visual C++에서 컴파일러는 버전이 특정 버전 보다 오래 된 경우 GUID 초기화 해야 정확히 한 번만. 따라서 두 개의 서로 다른 버전 DEFINE_GUID 매크로 있습니다. 버전에는 단지 기호 이름에 대 한 외부 참조를 선언합니다. 다른 버전은 실제로 기호 이름을 GUID의 값을 초기화합니다. GUID의 기호 이름에 대 한 LNK2001 오류가 나타나면, GUID 초기화 되지 않았습니다.

참고 DEFINE_GUID 매크로 guiddef.h 헤더 파일에는 GUID를 선언합니다. 또한 GUID를 정의 하려면 해당 INITGUID 포함 됩니다. H 헤더 파일에 원본 파일의 GUID를 정의 해야 합니다.

사용자 GUID 두 가지 방법 중 하나로 초기화 해야 수 있습니다.

  • 미리 컴파일된 헤더 파일을 사용 하는 경우에 INITGUID 포함 됩니다. H 헤더 파일에서 초기화 되어야 합니다 구현 파일 GUID를 정의 하기 전에. (MFC 응용 프로그램 마법사에서 생성 된 프로젝트.cpp 파일을 기본적으로 사용 됩니다.)
  • 미리 컴파일된 헤더를 사용 하지 않는 경우에 OBJBASE를 포함 하기 전에 INITGUID 정의 합니다. H. (OBJBASE. H는 OLE2가 포함 되어 있습니다. H.)
참고 미리 컴파일된 헤더를 사용할 때 컴파일러의 버전이 특정 버전 보다 최신 경우는 INITGUID 포함할 수 있습니다. H 헤더 파일에 여러 헤더 파일을 포함 하는 단일 헤더 파일에 있습니다. 이 인해 GUID이 헤더 파일에 포함 되어 있는 모든 소스 파일에 정의 됩니다. 기호 정의 여러 번 해도 LNK2001 오류가 발생 하지 나타납니다. 즉, DECLSPEC_SELECTANY 키워드는 DEFINE_GUID 매크로에 사용 됩니다. 이 키워드 하면 링커가이 여러 정의 제대로 처리 됩니다.
자세한 내용

컴파일러는 버전이 특정 버전 보다 오래 된 경우

OBJBASE에 나타나는 DEFINE_GUID의 정의 다음과 같습니다. H:

      #ifndef INITGUID
      #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, \ 
                                b4, b5, b6, b7, b8)
          EXTERN_C const GUID FAR name
      #else

      #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, \ 
                                b4, b5, b6, b7, b8)
         EXTERN_C const GUID name \ 
            = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
      #endif // INITGUID
				
참고 INITGUID 기호가 정의 되어 있지 않으면 DEFINE_GUID 간단 하 게 정의 하는 외부 참조 이름입니다.

INITGUID. 찾기 (특히) H:

      #undef DEFINE_GUID

      // Other code . . .

      #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, \ 
                                b4, b5, b6, b7, b8)
         EXTERN_C const GUID __based(__segname("_CODE")) name \ 
                  = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
				
INITGUID 넣어서. OBJBASE 이후에 H입니다. H, DEFINE_GUID는 실제로 GUID를 초기화 하도록 수정 됩니다.

참고: 것이 이렇게 단 한 번만 각 DLL 또는 EXE 있는지 확인 해야 합니다. 두 개의 서로 다른 소스 파일에 있는 GUID 초기화 하 고 다음 함께 연결 하려는 경우이 오류가 발생 합니다.
LNK2005 < 기호 > 이미 정의 되었습니다.

컴파일러는 버전이 특정 버전 보다 최신 버전인 경우

OBJBASE에서 DEFINE_GUID의 정의 그대로 표시 됩니다. H는 다음과 같습니다.
#ifdef INITGUID
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
        EXTERN_C const GUID DECLSPEC_SELECTANY name \
                = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
#else
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
    EXTERN_C const GUID FAR name
#endif // INITGUID
Guiddef.h DECLSPEC_SELECTANY 키워드의 정의 다음과 같습니다.
#ifndef DECLSPEC_SELECTANY
#if (_MSC_VER >= 1100)
#define DECLSPEC_SELECTANY  __declspec(selectany)
#else
#define DECLSPEC_SELECTANY
#endif
#endif
참고 DECLSPEC_SELECTANY 키워드는 OBJBASE에서 사용 됩니다. H에서 참조 되지 않은 개체의 초기화 코드는 무시 됩니다. 따라서 컴파일러는 버전이 특정 버전 보다 새로운 오류가 발생 하지 않습니다.


'MFC > Error' 카테고리의 다른 글

OLE/COM Object Viewer - DllRegisterServer in IVIEWERS.DLL failed  (0) 2016.04.18
Debug Assertion Failed in occont.cpp Line 926  (0) 2015.11.20
C2143  (0) 2015.06.24
C2504  (0) 2015.05.27
LNK 4070  (0) 2014.09.11
Posted by 곰돌이짱
,


Createfile () 직렬 포트에 대 한 핸들을 가져오는 데 사용할 수 있습니다. CreateFile()"에 대한 Win32 프로그래머 레퍼런스" 항목은 공유 모드는 0, 생성 매개 변수는 OPEN_EXISTING 그리고 템플릿은 NULL으로 언급합니다.

Createfile()는 해당 파일의 이름에 대해 "COM9"를 통해 "COM1"을 사용할 때 성공 그러나 메시지

INVALID_HANDLE_VALUE
"COM10" 이상을 사용하는 경우 또는 그 이상인 경우 반환됩니다.

포트 이름이 \\.\COM10 경우 createfile () 호출에서 직렬 포트를 지정 하는 올바른 방법은 다음과 같습니다.
   CreateFile(
      "\\\\.\\COM10",     // address of name of the communications device
      fdwAccess,          // access (read-write) mode
      0,                  // share mode
      NULL,               // address of security descriptor
      OPEN_EXISTING,      // how to create
      0,                  // file attributes
      NULL                // handle of file with attributes to copy
   );

				
참고:이 구문은 포트 COM1 에서 COM9 까지일 때도 동작합니다. 특정 게시판 포트 이름을 사용자가 직접 선택할 수 있습니다. 이 구문은 해당 사용자의 이름을 사용할 수 있습니다.


'MFC' 카테고리의 다른 글

MFC에서 DDK 사용 시  (0) 2018.03.26
Using the Windows Headers  (0) 2016.06.29
Serial Communications  (0) 2016.06.07
FormatMessage function  (0) 2016.06.07
Retrieving the Last-Error Code  (0) 2016.06.07
Posted by 곰돌이짱
,

Serial Communications

MFC 2016. 6. 7. 16:49

https://msdn.microsoft.com/en-us/library/ff802693.aspx#serial_topic3


Allen Denver
Microsoft Windows Developer Support

December 11, 1995


Summary: Learn about serial communications in Microsoft Windows. The Multithreaded TTY (MTTTY) sample is included with this article. (35 printed pages)

Download the MTTTY sample (4918.exe) for this technical article.

Contents

Overview
Introduction
Opening a Port
Reading and Writing
Serial Status
Serial Settings
Conclusion
Bibliography

Overview

This article provides a foundation for learning about serial communications in Windows.

This article assumes you are familiar with the fundamentals of multiple threading and synchronization in Windows. In addition, a basic familiarity of the heap functions is useful to fully comprehend the memory management methods used by the sample, MTTTY, included with this article.

Application programming interfaces (APIs) that control user interface features of windows and dialog boxes, though not discussed here, are useful to know in order to fully comprehend the sample provided with this article. Readers unfamiliar with general Windows programming practices should learn some of the fundamentals of general Windows programming before taking on serial communications. In other words, get your feet wet before diving in head first. (36 printed pages)

Introduction

This article does not discuss TAPI. TAPI does deserve mention, however, in that it very nicely implements modem interfacing and call controlling. A production application that works with modems and makes telephone calls should implement these features using the TAPI interface. This will allow seamless integration with the other TAPI-enabled applications that a user may have. Furthermore, this article does not discuss some of the configuration functions, such as GetCommProperties.

The sample included with this article, MTTTY: Multithreaded TTY (4918.exe), implements many of the features discussed here. It uses three threads in its implementation: a user interface thread that does memory management, a writer thread that controls all writing, and a reader/status thread that reads data and handles status changes on the port. The sample employs a few different data heaps for memory management. It also makes extensive use of synchronization methods to facilitate communication between threads.

Opening a Port

The CreateFile function opens a communications port. There are two ways to call CreateFile to open the communications port: overlapped and nonoverlapped. The following is the proper way to open a communications resource for overlapped operation:

HANDLE hComm;
hComm = CreateFile( gszPort,  
                    GENERIC_READ | GENERIC_WRITE, 
                    0, 
                    0, 
                    OPEN_EXISTING,
                    FILE_FLAG_OVERLAPPED,
                    0);
if (hComm == INVALID_HANDLE_VALUE)
   // error opening port; abort

Removal of the FILE_FLAG_OVERLAPPED flag from the call to CreateFile specifies nonoverlapped operation. The next section discusses overlapped and nonoverlapped operations.

The Platform SDK documentation states that when opening a communications port, the call to CreateFile has the following requirements:

  • fdwShareMode must be zero. Communications ports cannot be shared in the same manner that files are shared. Applications using TAPI can use the TAPI functions to facilitate sharing resources between applications. For applications not using TAPI, handle inheritance or duplication is necessary to share the communications port. Handle duplication is beyond the scope of this article; please refer to the Platform SDK documentation for more information.
  • fdwCreate must specify the OPEN_EXISTING flag.
  • hTemplateFile parameter must be NULL.

One thing to note about port names is that traditionally they have been COM1, COM2, COM3, or COM4. The Windows API does not provide any mechanism for determining what ports exist on a system. Some systems even have more ports than the traditional maximum of four. Hardware vendors and serial-device-driver writers are free to name the ports anything they like. For this reason, it is best that users have the ability to specify the port name they want to use. If a port does not exist, an error will occur (ERROR_FILE_NOT_FOUND) after attempting to open the port, and the user should be notified that the port isn't available.

Reading and Writing

Reading from and writing to communications ports in Windows is very similar to file input/output (I/O) in Windows. In fact, the functions that accomplish file I/O are the same functions used for serial I/O. I/O can be done either of two ways: overlapped or nonoverlapped. The Platform SDK documentation uses the terms asynchronous and synchronous to connote these types of I/O operations. This article, however, uses the terms overlapped and nonoverlapped.

Nonoverlapped I/O is familiar to most developers because this is the traditional form of I/O, where an operation is requested and is assumed to be complete when the function returns. In the case of overlapped I/O, the system may return to the caller immediately even when an operation is not finished and will signal the caller when the operation completes. The program may use the time between the I/O request and its completion to perform some "background" work.

Nonoverlapped I/O

Nonoverlapped I/O is very straightforward, though it has limitations. An operation takes place while the calling thread is blocked. Once the operation is complete, the function returns and the thread can continue its work. This type of I/O is useful for multithreaded applications because while one thread is blocked on an I/O operation, other threads can still perform work. It is the responsibility of the application to serialize access to the port correctly. If one thread is blocked waiting for its I/O operation to complete, all other threads that subsequently call a communications API will be blocked until the original operation completes. For instance, if one thread were waiting for a ReadFile function to return, any other thread that issued a WriteFile function would be blocked.

One of the many factors to consider when choosing between nonoverlapped and overlapped operations is portability. Overlapped operation is not a good choice because most operating systems do not support it. Most operating systems support some form of multithreading, however, so multithreaded nonoverlapped I/O may be the best choice for portability reasons.

Overlapped I/O

Overlapped I/O is not as straightforward as nonoverlapped I/O, but allows more flexibility and efficiency. A port open for overlapped operations allows multiple threads to do I/O operations at the same time and perform other work while the operations are pending. Furthermore, the behavior of overlapped operations allows a single thread to issue many different requests and do work in the background while the operations are pending.

In both single-threaded and multithreaded applications, some synchronization must take place between issuing requests and processing the results. One thread will have to be blocked until the result of an operation is available. The advantage is that overlapped I/O allows a thread to do some work between the time of the request and its completion. If no work can be done, then the only case for overlapped I/O is that it allows for better user responsiveness.

Overlapped I/O is the type of operation that the MTTTY sample uses. It creates a thread that is responsible for reading the port's data and reading the port's status. It also performs periodic background work. The program creates another thread exclusively for writing data out the port.

Note   Applications sometimes abuse multithreading systems by creating too many threads. Although using multiple threads can resolve many difficult problems, creating excessive threads is not the most efficient use of them in an application. Threads are less a strain on the system than processes but still require system resources such as CPU time and memory. An application that creates excessive threads may adversely affect the performance of the entire system. A better use of threads is to create a different request queue for each type of job and to have a worker thread issue an I/O request for each entry in the request queue. This method is used by the MTTTY sample provided with this article.

An overlapped I/O operation has two parts: the creation of the operation and the detection of its completion. Creating the operation entails setting up an OVERLAPPED structure, creating a manual-reset event for synchronization, and calling the appropriate function (ReadFile or WriteFile). The I/O operation may or may not be completed immediately. It is an error for an application to assume that a request for an overlapped operation always yields an overlapped operation. If an operation is completed immediately, an application needs to be ready to continue processing normally. The second part of an overlapped operation is to detect its completion. Detecting completion of the operation involves waiting for the event handle, checking the overlapped result, and then handling the data. The reason that there is more work involved with an overlapped operation is that there are more points of failure. If a nonoverlapped operation fails, the function just returns an error-return result. If an overlapped operation fails, it can fail in the creation of the operation or while the operation is pending. You may also have a time-out of the operation or a time-out waiting for the signal that the operation is complete.

Reading

The ReadFile function issues a read operation. ReadFileEx also issues a read operation, but since it is not available on Windows 95, it is not discussed in this article. Here is a code snippet that details how to issue a read request. Notice that the function calls a function to process the data if the ReadFile returns TRUE. This is the same function called if the operation becomes overlapped. Note the fWaitingOnRead flag that is defined by the code; it indicates whether or not a read operation is overlapped. It is used to prevent the creation of a new read operation if one is outstanding.

DWORD dwRead;
BOOL fWaitingOnRead = FALSE;
OVERLAPPED osReader = {0};

// Create the overlapped event. Must be closed before exiting
// to avoid a handle leak.
osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

if (osReader.hEvent == NULL)
   // Error creating overlapped event; abort.

if (!fWaitingOnRead) {
   // Issue read operation.
   if (!ReadFile(hComm, lpBuf, READ_BUF_SIZE, &dwRead, &osReader)) {
      if (GetLastError() != ERROR_IO_PENDING)     // read not delayed?
         // Error in communications; report it.
      else
         fWaitingOnRead = TRUE;
   }
   else {    
      // read completed immediately
      HandleASuccessfulRead(lpBuf, dwRead);
    }
}

The second part of the overlapped operation is the detection of its completion. The event handle in the OVERLAPPED structure is passed to the WaitForSingleObject function, which will wait until the object is signaled. Once the event is signaled, the operation is complete. This does not mean that it was completed successfully, just that it was completed. The GetOverlappedResult function reports the result of the operation. If an error occurred, GetOverlappedResult returns FALSE and GetLastError returns the error code. If the operation was completed successfully, GetOverlappedResult will return TRUE.

Note   GetOverlappedResult can detect completion of the operation, as well as return the operation's failure status. GetOverlappedResult returns FALSE and GetLastError returns ERROR_IO_INCOMPLETE when the operation is not completed. In addition, GetOverlappedResult can be made to block until the operation completes. This effectively turns the overlapped operation into a nonoverlapped operation and is accomplished by passing TRUE as the bWait parameter.

Here is a code snippet that shows one way to detect the completion of an overlapped read operation. Note that the code calls the same function to process the data that was called when the operation completed immediately. Also note the use of the fWaitingOnRead flag. Here it controls entry into the detection code, since it should be called only when an operation is outstanding.

#define READ_TIMEOUT      500      // milliseconds

DWORD dwRes;

if (fWaitingOnRead) {
   dwRes = WaitForSingleObject(osReader.hEvent, READ_TIMEOUT);
   switch(dwRes)
   {
      // Read completed.
      case WAIT_OBJECT_0:
          if (!GetOverlappedResult(hComm, &osReader, &dwRead, FALSE))
             // Error in communications; report it.
          else
             // Read completed successfully.
             HandleASuccessfulRead(lpBuf, dwRead);

          //  Reset flag so that another opertion can be issued.
          fWaitingOnRead = FALSE;
          break;

      case WAIT_TIMEOUT:
          // Operation isn't complete yet. fWaitingOnRead flag isn't
          // changed since I'll loop back around, and I don't want
          // to issue another read until the first one finishes.
          //
          // This is a good time to do some background work.
          break;                       

      default:
          // Error in the WaitForSingleObject; abort.
          // This indicates a problem with the OVERLAPPED structure's
          // event handle.
          break;
   }
}

Writing

Transmitting data out the communications port is very similar to reading in that it uses a lot of the same APIs. The code snippet below demonstrates how to issue and wait for a write operation to be completed.

BOOL WriteABuffer(char * lpBuf, DWORD dwToWrite)
{
   OVERLAPPED osWrite = {0};
   DWORD dwWritten;
   DWORD dwRes;
   BOOL fRes;

   // Create this write operation's OVERLAPPED structure's hEvent.
   osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   if (osWrite.hEvent == NULL)
      // error creating overlapped event handle
      return FALSE;

   // Issue write.
   if (!WriteFile(hComm, lpBuf, dwToWrite, &dwWritten, &osWrite)) {
      if (GetLastError() != ERROR_IO_PENDING) { 
         // WriteFile failed, but isn't delayed. Report error and abort.
         fRes = FALSE;
      }
      else
         // Write is pending.
         dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);
         switch(dwRes)
         {
            // OVERLAPPED structure's event has been signaled. 
            case WAIT_OBJECT_0:
                 if (!GetOverlappedResult(hComm, &osWrite, &dwWritten, FALSE))
                       fRes = FALSE;
                 else
                  // Write operation completed successfully.
                  fRes = TRUE;
                 break;
            
            default:
                 // An error has occurred in WaitForSingleObject.
                 // This usually indicates a problem with the
                // OVERLAPPED structure's event handle.
                 fRes = FALSE;
                 break;
         }
      }
   }
   else
      // WriteFile completed immediately.
      fRes = TRUE;

   CloseHandle(osWrite.hEvent);
   return fRes;
}

Notice that the code above uses the WaitForSingleObject function with a time-out value of INFINITE. This causes the WaitForSingleObject function to wait forever until the operation is completed; this may make the thread or program appear to be "hung" when, in fact, the write operation is simply taking a long time to complete or flow control has blocked the transmission. Status checking, discussed later, can detect this condition, but doesn't cause the WaitForSingleObject to return. Three things can alleviate this condition:

  • Place the code in a separate thread. This allows other threads to execute any functions they desire while our writer thread waits for the write to be completed. This is what the MTTTY sample does.
  • Use COMMTIMEOUTS to cause the write to be completed after a time-out period has passed. This is discussed more fully in the "Communications Time-outs" section later in this article. This is also what the MTTTY sample allows.
  • Change the WaitForSingleObject call to include a real time-out value. This causes more problems because if the program issues another operation while an older operation is still pending, new OVERLAPPED structures and overlapped events need to be allocated. This type of recordkeeping is difficult, particularly when compared to using a "job queue" design for the operations. The "job queue" method is used in the MTTTY sample.

    Note: The time-out values in synchronization functions are not communications time-outs. Synchronization time-outs cause WaitForSingleObject or WaitForMultipleObjects to return WAIT_TIMEOUT. This is not the same as a read or write operation timing out. Communications time-outs are described later in this article.

Because the WaitForSingleObject function in the above code snippet uses an INFINITE time-out, it is equivalent to using GetOverlappedResult with TRUE for the fWait parameter. Here is equivalent code in a much simplified form:

BOOL WriteABuffer(char * lpBuf, DWORD dwToWrite)
{
   OVERLAPPED osWrite = {0};
   DWORD dwWritten;
   BOOL fRes;

   // Create this writes OVERLAPPED structure hEvent.
   osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   if (osWrite.hEvent == NULL)
      // Error creating overlapped event handle.
      return FALSE;

   // Issue write.
   if (!WriteFile(hComm, lpBuf, dwToWrite, &dwWritten, &osWrite)) {
      if (GetLastError() != ERROR_IO_PENDING) { 
         // WriteFile failed, but it isn't delayed. Report error and abort.
         fRes = FALSE;
      }
      else {
         // Write is pending.
         if (!GetOverlappedResult(hComm, &osWrite, &dwWritten, TRUE))
            fRes = FALSE;
         else
            // Write operation completed successfully.
            fRes = TRUE;
      }
   }
   else
      // WriteFile completed immediately.
      fRes = TRUE;

   CloseHandle(osWrite.hEvent);
   return fRes;
}

GetOverlappedResult is not always the best way to wait for an overlapped operation to be completed. For example, if an application needs to wait on another event handle, the first code snippet serves as a better model than the second. The call to WaitForSingleObject is easy to change to WaitForMultipleObjects to include the additional handles on which to wait. This is what the MTTTY sample application does.

A common mistake in overlapped I/O is to reuse an OVERLAPPED structure before the previous overlapped operation is completed. If a new overlapped operation is issued before a previous operation is completed, a new OVERLAPPED structure must be allocated for it. A new manual-reset event for the hEvent member of the OVERLAPPED structure must also be created. Once an overlapped operation is complete, the OVERLAPPED structure and its event are free for reuse.

The only member of the OVERLAPPED structure that needs modifying for serial communications is the hEvent member. The other members of the OVERLAPPED structure should be initialized to zero and left alone. Modifying the other members of the OVERLAPPED structure is not necessary for serial communications devices. The documentation for ReadFile and WriteFile state that the Offset and OffsetHigh members of the OVERLAPPED structure must be updated by the application, or else results are unpredictable. This guideline should be applied to OVERLAPPED structures used for other types of resources, such as files.

Serial Status

There are two methods to retrieve the status of a communications port. The first is to set an event mask that causes notification of the application when the desired events occur. The SetCommMask function sets this event mask, and the WaitCommEvent function waits for the desired events to occur. The second method for retrieving the status of the communications port is to periodically call a few different status functions. Polling is, of course, neither efficient nor recommended.

Communications Events

Communications events can occur at any time in the course of using a communications port. The two steps involved in receiving notification of communications events are as follows:

  • SetCommMask sets the desired events that cause a notification.
  • WaitCommEvent issues a status check. The status check can be an overlapped or nonoverlapped operation, just as the read and write operations can be.

    Note: The word event in this context refers to communications events only. It does not refer to an event object used for synchronization.

Here is an example of the SetCommMask function:

DWORD dwStoredFlags;

dwStoredFlags = EV_BREAK | EV_CTS   | EV_DSR | EV_ERR | EV_RING |\
                EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY ;
if (!SetCommMask(hComm, dwStoredFlags))
   // error setting communications mask

A description of each type of event is in Table 1.

Table 1. Communications Event Flags

Event FlagDescription
EV_BREAKA break was detected on input.
EV_CTSThe CTS (clear-to-send) signal changed state. To get the actual state of the CTS line, GetCommModemStatus should be called.
EV_DSRThe DSR (data-set-ready) signal changed state. To get the actual state of the DSR line, GetCommModemStatus should be called.
EV_ERRA line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. To find the cause of the error, ClearCommError should be called.
EV_RINGA ring indicator was detected.
EV_RLSDThe RLSD (receive-line-signal-detect) signal changed state. To get the actual state of the RLSD line, GetCommModemStatus should be called. Note that this is commonly referred to as the CD (carrier detect) line.
EV_RXCHARA new character was received and placed in the input buffer. See the "Caveat" section below for a discussion of this flag.
EV_RXFLAGThe event character was received and placed in the input buffer. The event character is specified in the EvtChar member of the DCB structure discussed later. The "Caveat" section below also applies to this flag.
EV_TXEMPTYThe last character in the output buffer was sent to the serial port device. If a hardware buffer is used, this flag only indicates that all data has been sent to the hardware. There is no way to detect when the hardware buffer is empty without talking directly to the hardware with a device driver.

After specifying the event mask, the WaitCommEvent function detects the occurrence of the events. If the port is open for nonoverlapped operation, then the WaitCommEvent function does not contain an OVERLAPPED structure. The function blocks the calling thread until the occurrence of one of the events. If an event never occurs, the thread may block indefinitely.

Here is a code snippet that shows how to wait for an EV_RING event when the port is open for nonoverlapped operation:

   DWORD dwCommEvent;

   if (!SetCommMask(hComm, EV_RING))
      // Error setting communications mask
      return FALSE;

   if (!WaitCommEvent(hComm, &dwCommEvent, NULL))
      // An error occurred waiting for the event.
      return FALSE;
   else
      // Event has occurred.
      return TRUE;

As noted, the code above can be blocked forever if an event never occurs. A better solution would be to open the port for overlapped operation and wait for a status event in the following manner:

   #define STATUS_CHECK_TIMEOUT      500   // Milliseconds

   DWORD      dwRes;
   DWORD      dwCommEvent;
   DWORD      dwStoredFlags;
   BOOL      fWaitingOnStat = FALSE;
   OVERLAPPED osStatus = {0};

   dwStoredFlags = EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING |\
                  EV_RLSD | EV_RXCHAR | EV_RXFLAG | EV_TXEMPTY ;
   if (!SetCommMask(comHandle, dwStoredFlags))
      // error setting communications mask; abort
      return 0;

   osStatus.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   if (osStatus.hEvent == NULL)
      // error creating event; abort
      return 0;

   for ( ; ; ) {
      // Issue a status event check if one hasn't been issued already.
      if (!fWaitingOnStat) {
         if (!WaitCommEvent(hComm, &dwCommEvent, &osStatus)) {
            if (GetLastError() == ERROR_IO_PENDING)
               bWaitingOnStatusHandle = TRUE;
            else
               // error in WaitCommEvent; abort
               break;
         }
         else
            // WaitCommEvent returned immediately.
            // Deal with status event as appropriate.
            ReportStatusEvent(dwCommEvent); 
      }

      // Check on overlapped operation.
      if (fWaitingOnStat) {
         // Wait a little while for an event to occur.
         dwRes = WaitForSingleObject(osStatus.hEvent, STATUS_CHECK_TIMEOUT);
         switch(dwRes)
         {
             // Event occurred.
             case WAIT_OBJECT_0: 
                 if (!GetOverlappedResult(hComm, &osStatus, &dwOvRes, FALSE))
                    // An error occurred in the overlapped operation;
                    // call GetLastError to find out what it was
                    // and abort if it is fatal.
                 else
                    // Status event is stored in the event flag
                    // specified in the original WaitCommEvent call.
                    // Deal with the status event as appropriate.
                    ReportStatusEvent(dwCommEvent);

                 // Set fWaitingOnStat flag to indicate that a new
                 // WaitCommEvent is to be issued.
                 fWaitingOnStat = FALSE;
                 break;

             case WAIT_TIMEOUT:
                 // Operation isn't complete yet. fWaitingOnStatusHandle flag 
                 // isn't changed since I'll loop back around and I don't want
                 // to issue another WaitCommEvent until the first one finishes.
                 //
                 // This is a good time to do some background work.
                DoBackgroundWork();
                 break;                       

             default:
                 // Error in the WaitForSingleObject; abort
                 // This indicates a problem with the OVERLAPPED structure's
                 // event handle.
                CloseHandle(osStatus.hEvent);
                return 0;
         }
      }
   }

   CloseHandle(osStatus.hEvent);

The code above very closely resembles the code for overlapped reading. In fact, the MTTTY sample implements its reading and status checking in the same thread using WaitForMultipleObjects to wait for either the read event or the status event to become signaled.

There are two interesting side effects of SetCommMask and WaitCommEvent. First, if the communications port is open for nonoverlapped operation, WaitCommEvent will be blocked until an event occurs. If another thread calls SetCommMask to set a new event mask, that thread will be blocked on the call to SetCommMask. The reason is that the original call to WaitCommEvent in the first thread is still executing. The call to SetCommMask blocks the thread until the WaitCommEvent function returns in the first thread. This side effect is universal for ports open for nonoverlapped I/O. If a thread is blocked on any communications function and another thread calls a communications function, the second thread is blocked until the communications function returns in the first thread. The second interesting note about these functions is their use on a port open for overlapped operation. If SetCommMask sets a new event mask, any pending WaitCommEvent will complete successfully, and the event mask produced by the operation is NULL.

Caveat

Using the EV_RXCHAR flag will notify the thread that a byte arrived at the port. This event, used in combination with the ReadFile function, enables a program to read data only after it is in the receive buffer, as opposed to issuing a read that waits for the data to arrive. This is particularly useful when a port is open for nonoverlapped operation because the program does not need to poll for incoming data; the program is notified of the incoming data by the occurrence of the EV_RXCHAR event. Initial attempts to code this solution often produce the following pseudocode, including one oversight covered later in this section:

DWORD dwCommEvent;
DWORD dwRead;
char  chRead;

if (!SetCommMask(hComm, EV_RXCHAR))
   // Error setting communications event mask.

for ( ; ; ) {
   if (WaitCommEvent(hComm, &dwCommEvent, NULL)) {
      if (ReadFile(hComm, &chRead, 1, &dwRead, NULL))
         // A byte has been read; process it.
      else
         // An error occurred in the ReadFile call.
         break;
   }
   else
      // Error in WaitCommEvent.
      break;
}

The above code waits for an EV_RXCHAR event to occur. When this happens, the code calls ReadFile to read the one byte received. The loop starts again, and the code waits for another EV_RXCHAR event. This code works fine when one or two bytes arrive in quick succession. The byte reception causes the EV_RXCHAR event to occur. The code reads the byte. If no other byte arrives before the code calls WaitCommEvent again, then all is fine; the next byte to arrive will cause the WaitCommEvent function to indicate the occurrence of the EV_RXCHAR event. If another single byte arrives before the code has a chance to reach the WaitCommEvent function, then all is fine, too. The first byte is read as before; the arrival of the second byte causes the EV_RXCHAR flag to be set internally. When the code returns to the WaitCommEvent function, it indicates the occurrence of the EV_RXCHAR event and the second byte is read from the port in the ReadFile call.

The problem with the above code occurs when three or more bytes arrive in quick succession. The first byte causes the EV_RXCHAR event to occur. The second byte causes the EV_RXCHAR flag to be set internally. The next time the code calls WaitCommEvent, it indicates the EV_RXCHAR event. Now, a third byte arrives at the communications port. This third byte causes the system to attempt to set the EV_RXCHAR flag internally. Because this has already occurred when the second byte arrived, the arrival of the third byte goes unnoticed. The code eventually will read the first byte without a problem. After this, the code will call WaitCommEvent, and it indicates the occurrence of the EV_RXCHAR event (from the arrival of the second byte). The second byte is read, and the code returns to the WaitCommEvent function. The third byte waits in the system's internal receive buffer. The code and the system are now out of sync. When a fourth byte finally arrives, the EV_RXCHAR event occurs, and the code reads a single byte. It reads the third byte. This will continue indefinitely.

The solution to this problem seems as easy as increasing the number of bytes requested in the read operation. Instead of requesting a single byte, the code could request two, ten, or some other number of bytes. The problem with this idea is that it still fails when two or more extra bytes above the size of the read request arrive at the port in quick succession. So, if two bytes are read, then four bytes arriving in quick succession would cause the problem. Ten bytes requested would still fail if twelve bytes arrived in quick succession.

The real solution to this problem is to read from the port until no bytes are remaining. The following pseudocode solves the problem by reading in a loop until zero characters are read. Another possible method would be to call ClearCommError to determine the number of bytes in the buffer and read them all in one read operation. This method requires more sophisticated buffer management, but it reduces the number of reads when a lot of data arrives at once.

DWORD dwCommEvent;
DWORD dwRead;
char  chRead;

if (!SetCommMask(hComm, EV_RXCHAR))
   // Error setting communications event mask

for ( ; ; ) {
   if (WaitCommEvent(hComm, &dwCommEvent, NULL)) {
      do {
         if (ReadFile(hComm, &chRead, 1, &dwRead, NULL))
            // A byte has been read; process it.
         else
            // An error occurred in the ReadFile call.
            break;
      } while (dwRead);
   }
   else
      // Error in WaitCommEvent
      break;
}

The above code does not work correctly without setting the proper time-outs. Communications time-outs, discussed later, affect the behavior of the ReadFile operation in order to cause it to return without waiting for bytes to arrive. Discussion of this topic occurs later in the "Communications Time-outs" section of this article.

The above caveat regarding EV_RXCHAR also applies to EV_RXFLAG. If flag characters arrive in quick succession, EV_RXFLAG events may not occur for all of them. Once again, the best solution is to read all bytes until none remain.

The above caveat also applies to other events not related to character reception. If other events occur in quick succession some of the notifications will be lost. For instance, if the CTS line voltage starts high, then goes low, high, and low again, an EV_CTS event occurs. There is no guarantee of how many EV_CTS events will actually be detected with WaitCommEvent if the changes in the CTS line happen quickly. For this reason, WaitCommEvent cannot be used to keep track of the state of the line. Line status is covered in the "Modem Status" section later in this article.

Error Handling and Communications Status

One of the communications event flags specified in the call to SetCommMask is possibly EV_ERR. The occurrence of the EV_ERR event indicates that an error condition exists in the communications port. Other errors can occur in the port that do not cause the EV_ERR event to occur. In either case, errors associated with the communications port cause all I/O operations to be suspended until removal of the error condition. ClearCommError is the function to call to detect errors and clear the error condition.

ClearCommError also provides communications status indicating why transmission has stopped; it also indicates the number of bytes waiting in the transmit and receive buffers. The reason why transmission may stop is because of errors or to flow control. The discussion of flow control occurs later in this article.

Here is some code that demonstrates how to call ClearCommError:

    COMSTAT comStat;
    DWORD   dwErrors;
    BOOL    fOOP, fOVERRUN, fPTO, fRXOVER, fRXPARITY, fTXFULL;
    BOOL    fBREAK, fDNS, fFRAME, fIOE, fMODE;

    // Get and clear current errors on the port.
    if (!ClearCommError(hComm, &dwErrors, &comStat))
        // Report error in ClearCommError.
        return;

    // Get error flags.
    fDNS = dwErrors & CE_DNS;
    fIOE = dwErrors & CE_IOE;
    fOOP = dwErrors & CE_OOP;
    fPTO = dwErrors & CE_PTO;
    fMODE = dwErrors & CE_MODE;
    fBREAK = dwErrors & CE_BREAK;
    fFRAME = dwErrors & CE_FRAME;
    fRXOVER = dwErrors & CE_RXOVER;
    fTXFULL = dwErrors & CE_TXFULL;
    fOVERRUN = dwErrors & CE_OVERRUN;
    fRXPARITY = dwErrors & CE_RXPARITY;

    // COMSTAT structure contains information regarding
    // communications status.
    if (comStat.fCtsHold)
        // Tx waiting for CTS signal

    if (comStat.fDsrHold)
        // Tx waiting for DSR signal

    if (comStat.fRlsdHold)
        // Tx waiting for RLSD signal

    if (comStat.fXoffHold)
        // Tx waiting, XOFF char rec'd

    if (comStat.fXoffSent)
        // Tx waiting, XOFF char sent
    
    if (comStat.fEof)
        // EOF character received
    
    if (comStat.fTxim)
        // Character waiting for Tx; char queued with TransmitCommChar

    if (comStat.cbInQue)
        // comStat.cbInQue bytes have been received, but not read

    if (comStat.cbOutQue)
        // comStat.cbOutQue bytes are awaiting transfer

Modem Status (a.k.a. Line Status)

The call to SetCommMask may include the flags EV_CTS, EV_DSR, EV_RING, and EV_RLSD. These flags indicate changes in the voltage on the lines of the serial port. There is no indication of the actual status of these lines, just that a change occurred. The GetCommModemStatus function retrieves the actual state of these status lines by returning a bit mask indicating a 0 for low or no voltage and 1 for high voltage for each of the lines.

Please note that the term RLSD (Receive Line Signal Detect) is commonly referred to as the CD (Carrier Detect) line.

Note   The EV_RING flag does not work in Windows 95 as mentioned earlier. The GetCommModemStatus function, however, does detect the state of the RING line.

Changes in these lines may also cause a flow-control event. The ClearCommError function reports whether transmission is suspended because of flow control. If necessary, a thread may call ClearCommError to detect whether the event is the cause of a flow-control action. Flow control is covered in the "Flow Control" section later in this article.

Here is some code that demonstrates how to call GetCommModemStatus:

   DWORD dwModemStatus;
   BOOL  fCTS, fDSR, fRING, fRLSD;

   if (!GetCommModemStatus(hComm, &dwModemStatus))
      // Error in GetCommModemStatus;
      return;

   fCTS = MS_CTS_ON & dwModemStatus;
   fDSR = MS_DSR_ON & dwModemStatus;
   fRING = MS_RING_ON & dwModemStatus;
   fRLSD = MS_RLSD_ON & dwModemStatus;

   // Do something with the flags.

Extended Functions

The driver will automatically change the state of control lines as necessary. Generally speaking, changing status lines is under the control of a driver. If a device uses communications port control lines in a manner different from RS-232 standards, the standard serial communications driver will not work to control the device. If the standard serial communications driver will not control the device, a custom device driver is necessary.

There are occasions when standard control lines are under the control of the application instead of the serial communications driver. For instance, an application may wish to implement its own flow control. The application would be responsible for changing the status of the RTS and DTR lines. EscapeCommFunction directs a communications driver to perform such extended operations. EscapeCommFunction can make the driver perform some other function, such as setting or clearing a BREAK condition.

Serial Settings

DCB Settings

The most crucial aspect of programming serial communications applications is the settings in the Device-Control Block (DCB) structure. The most common errors in serial communications programming occur in initializing the DCB structure improperly. When the serial communications functions do not behave as expected, a close examination of the DCB structure usually reveals the problem.

There are three ways to initialize a DCB structure. The first method is to use the function GetCommState. This function returns the current DCB in use for the communications port. The following code shows how to use the GetCommState function:

   DCB dcb = {0};

   if (!GetCommState(hComm, &dcb))
      // Error getting current DCB settings
   else
      // DCB is ready for use.

The second method to initialize a DCB is to use a function called BuildCommDCB. This function fills in the baud, parity type, number of stop bits, and number of data bits members of the DCB. The function also sets the flow-control members to default values. Consult the documentation of the BuildCommDCB function for details on which default values it uses for flow-control members. Other members of the DCB are unaffected by this function. It is the program's duty to make sure the other members of the DCB do not cause errors. The simplest thing to do in this regard is to initialize the DCB structure with zeros and then set the size member to the size, in bytes, of the structure. If the zero initialization of the DCB structure does not occur, then there may be nonzero values in the reserved members; this produces an error when trying to use the DCB later. The following function shows how to properly use this method:

   DCB dcb;

   FillMemory(&dcb, sizeof(dcb), 0);
   dcb.DCBlength = sizeof(dcb);
   if (!BuildCommDCB("9600,n,8,1", &dcb)) {   
      // Couldn't build the DCB. Usually a problem
      // with the communications specification string.
      return FALSE;
   }
   else
      // DCB is ready for use.

The third method to initialize a DCB structure is to do it manually. The program allocates the DCB structure and sets each member with any value desired. This method does not deal well with changes to the DCB in future implementations of Windows and is not recommended.

An application usually needs to set some of the DCB members differently than the defaults or may need to modify settings in the middle of execution. Once proper initialization of the DCB occurs, modification of individual members is possible. The changes to the DCB structure do not have any effect on the behavior of the port until execution of the SetCommState function. Here is a section of code that retrieves the current DCB, changes the baud, and then attempts to set the configuration:

   DCB dcb;

   FillMemory(&dcb, sizeof(dcb), 0);
   if (!GetCommState(hComm, &dcb))     // get current DCB
      // Error in GetCommState
      return FALSE;

   // Update DCB rate.
   dcb.BaudRate = CBR_9600 ;

   // Set new state.
   if (!SetCommState(hComm, &dcb))
      // Error in SetCommState. Possibly a problem with the communications 
      // port handle or a problem with the DCB structure itself.

Here is an explanation of each of the members of the DCB and how they affect other parts of the serial communications functions.

Note   Most of this information is from the Platform SDK documentation. Because documentation is the official word in what the members actually are and what they mean, this table may not be completely accurate if changes occur in the operating system.

Table 2. The DCB Structure Members

MemberDescription
DCBlengthSize, in bytes, of the structure. Should be set before calling SetCommState to update the settings.
BaudRateSpecifies the baud at which the communications device operates. This member can be an actual baud value, or a baud index.
fBinarySpecifies whether binary mode is enabled. The Windows API does not support nonbinary mode transfers, so this member should be TRUE. Trying to use FALSE will not work.
fParitySpecifies whether parity checking is enabled. If this member is TRUE, parity checking is performed and parity errors are reported. This should not be confused with the Parity member, which controls the type of parity used in communications.
fOutxCtsFlowSpecifies whether the CTS (clear-to-send) signal is monitored for output flow control. If this member is TRUE and CTS is low, output is suspended until CTS is high again. The CTS signal is under control of the DCE (usually a modem), the DTE (usually the PC) simply monitors the status of this signal, the DTE does not change it.
fOutxDsrFlowSpecifies whether the DSR (data-set-ready) signal is monitored for output flow control. If this member is TRUE and DSR is low, output is suspended until DSR is high again. Once again, this signal is under the control of the DCE; the DTE only monitors this signal.
fDtrControlSpecifies the DTR (data-terminal-ready) input flow control. This member can be one of the following values:
 ValueMeaning
 DTR_CONTROL_DISABLELowers the DTR line when the device is opened. The application can adjust the state of the line with EscapeCommFunction.
 DTR_CONTROL_ENABLERaises the DTR line when the device is opened. The application can adjust the state of the line with EscapeCommFunction.
 DTR_CONTROL_HANDSHAKEEnables DTR flow-control handshaking. If this value is used, it is an error for the application to adjust the line with EscapeCommFunction.
fDsrSensitivitySpecifies whether the communications driver is sensitive to the state of the DSR signal. If this member is TRUE, the driver ignores any bytes received, unless the DSR modem input line is high.
fTXContinueOnXoffSpecifies whether transmission stops when the input buffer is full and the driver has transmitted the XOFF character. If this member is TRUE, transmission continues after the XOFF character has been sent. If this member is FALSE, transmission does not continue until the input buffer is within XonLim bytes of being empty and the driver has transmitted the XON character.
fOutXSpecifies whether XON/XOFF flow control is used during transmission. If this member is TRUE, transmission stops when the XOFF character is received and starts again when the XON character is received.
fInXSpecifies whether XON/XOFF flow control is used during reception. If this member is TRUE, the XOFF character is sent when the input buffer comes within XoffLim bytes of being full, and the XON character is sent when the input buffer comes within XonLim bytes of being empty.
fErrorCharSpecifies whether bytes received with parity errors are replaced with the character specified by the ErrorChar member. If this member is TRUE and the fParity member is TRUE, replacement occurs.
fNullSpecifies whether null bytes are discarded. If this member is TRUE, null bytes are discarded when received.
fRtsControlSpecifies the RTS (request-to-send) input flow control. If this value is zero, the default is RTS_CONTROL_HANDSHAKE. This member can be one of the following values:
 ValueMeaning
 RTS_CONTROL_DISABLELowers the RTS line when the device is opened. The application can use EscapeCommFunction to change the state of the line.
 RTS_CONTROL_ENABLERaises the RTS line when the device is opened. The application can use EscapeCommFunction to change the state of the line.
 RTS_CONTROL_HANDSHAKEEnables RTS flow-control handshaking. The driver raises the RTS line, enabling the DCE to send, when the input buffer has enough room to receive data. The driver lowers the RTS line, preventing the DCE to send, when the input buffer does not have enough room to receive data. If this value is used, it is an error for the application to adjust the line with EscapeCommFunction.
 RTS_CONTROL_TOGGLESpecifies that the RTS line will be high if bytes are available for transmission. After all buffered bytes have been sent, the RTS line will be low. If this value is set, it would be an error for an application to adjust the line with EscapeCommFunction. This value is ignored in Windows 95; it causes the driver to act as if RTS_CONTROL_ENABLE were specified.
fAbortOnErrorSpecifies whether read and write operations are terminated if an error occurs. If this member is TRUE, the driver terminates all read and write operations with an error status (ERROR_IO_ABORTED) if an error occurs. The driver will not accept any further communications operations until the application has acknowledged the error by calling the ClearCommError function.
fDummy2Reserved; do not use.
wReservedNot used; must be set to zero.
XonLimSpecifies the minimum number of bytes allowed in the input buffer before the XON character is sent.
XoffLimSpecifies the maximum number of bytes allowed in the input buffer before the XOFF character is sent. The maximum number of bytes allowed is calculated by subtracting this value from the size, in bytes, of the input buffer.
ParitySpecifies the parity scheme to be used. This member can be one of the following values:
 ValueMeaning
 EVENPARITYEven
 MARKPARITYMark
 NOPARITYNo parity
 ODDPARITYOdd
StopBitsSpecifies the number of stop bits to be used. This member can be one of the following values:
 ValueMeaning
 ONESTOPBIT1 stop bit
 ONE5STOPBITS1.5 stop bits
 TWOSTOPBITS2 stop bits
XonCharSpecifies the value of the XON character for both transmission and reception.
XoffCharSpecifies the value of the XOFF character for both transmission and reception.
ErrorCharSpecifies the value of the character used to replace bytes received with a parity error.
EofCharSpecifies the value of the character used to signal the end of data.
EvtCharSpecifies the value of the character used to cause the EV_RXFLAG event. This setting does not actually cause anything to happen without the use of EV_RXFLAG in the SetCommMask function and the use of WaitCommEvent.
wReserved1Reserved; do not use.

Flow Control

Flow control in serial communications provides a mechanism for suspending communications while one of the devices is busy or for some reason cannot do any communication. There are traditionally two types of flow control: hardware and software.

A common problem with serial communications is write operations that actually do not write the data to the device. Often, the problem lies in flow control being used when the program did not specify it. A close examination of the DCB structure reveals that one or more of the following members may be TRUE: fOutxCtsFlow, fOutxDsrFlow, or fOutX. Another mechanism to reveal that flow control is enabled is to call ClearCommError and examine the COMSTAT structure. It will reveal when transmission is suspended because of flow control.

Before discussing the types of flow control, a good understanding of some terms is in order. Serial communications takes place between two devices. Traditionally, there is a PC and a modem or printer. The PC is labeled the Data Terminal Equipment (DTE). The DTE is sometimes called the host. The modem, printer, or other peripheral equipment is identified as the Data Communications Equipment (DCE). The DCE is sometimes referred to as the device.

Hardware flow control

Hardware flow control uses voltage signals on control lines of the serial cable to control whether sending or receiving is enabled. The DTE and the DCE must agree on the types of flow control used for a communications session. Setting the DCB structure to enable flow control just configures the DTE. The DCE also needs configuration to make certain the DTE and DCE use the same type of flow control. There is no mechanism provided by Windows to set the flow control of the DCE. DIP switches on the device, or commands sent to it typically establish its configuration. The following table describes the control lines, the direction of the flow control, and the line's effect on the DTE and DCE.

Table 3. Hardware Flow-control Lines

Line and DirectionEffect on DTE/DCE
CTS
(Clear To Send)
Output flow control
DCE sets the line high to indicate that it can receive data. DCE sets the line low to indicate that it cannot receive data.

If the fOutxCtsFlow member of the DCB is TRUE, then the DTE will not send data if this line is low. It will resume sending if the line is high.

If the fOutxCtsFlow member of the DCB is FALSE, then the state of the line does not affect transmission.

DSR
(Data Set Ready)
Output flow control
DCE sets the line high to indicate that it can receive data. DCE sets the line low to indicate that it cannot receive data.

If the fOutxDsrFlow member of the DCB is TRUE, then the DTE will not send data if this line is low. It will resume sending if the line is high.

If the fOutxDsrFlow member of the DCB is FALSE, then the state of the line does not affect transmission.

DSR
(Data Set Ready)
Input flow control
If the DSR line is low, then data that arrives at the port is ignored. If the DSR line is high, data that arrives at the port is received.

This behavior occurs if the fDsrSensitivity member of the DCB is set to TRUE. If it is FALSE, then the state of the line does not affect reception.

RTS
(Ready To Send)
Input flow control
The RTS line is controlled by the DTE.

If the fRtsControl member of the DCB is set to RTS_CONTROL_HANDSHAKE, the following flow control is used: If the input buffer has enough room to receive data (at least half the buffer is empty), the driver sets the RTS line high. If the input buffer has little room for incoming data (less than a quarter of the buffer is empty), the driver sets the RTS line low.

If the fRtsControl member of the DCB is set to RTS_CONTROL_TOGGLE, the driver sets the RTS line high when data is available for sending. The driver sets the line low when no data is available for sending. Windows 95 ignores this value and treats it the same as RTS_CONTROL_ENABLE.

If the fRtsControl member of the DCB is set to RTS_CONTROL_ENABLE or RTS_CONTROL_DISABLE, the application is free to change the state of the line as it needs. Note that in this case, the state of the line does not affect reception.

The DCE will suspend transmission when the line goes low. The DCE will resume transmission when the line goes high.

DTR
(Data Terminal Ready)
Input flow control
The DTR line is controlled by the DTE.

If the fDtrControl member of the DCB is set to DTR_CONTROL_HANDSHAKE, the following flow control is used: If the input buffer has enough room to receive data (at least half the buffer is empty), the driver sets the DTR line high. If the input buffer has little room for incoming data (less than a quarter of the buffer is empty), the driver sets the DTR line low.

If the fDtrControl member of the DCB is set to DTR_CONTROL_ENABLE or DTR_CONTROL_DISABLE, the application is free to change the state of the line as it needs. In this case, the state of the line does not affect reception.

The DCE will suspend transmission when the line goes low. The DCE will resume transmission when the line goes high.

The need for flow control is easy to recognize when the CE_RXOVER error occurs. This error indicates an overflow of the receive buffer and data loss. If data arrives at the port faster than it is read, CE_RXOVER can occur. Increasing the input buffer size may cause the error to occur less frequently, but it does not completely solve the problem. Input flow control is necessary to completely alleviate this problem. When the driver detects that the input buffer is nearly full, it will lower the input flow-control lines. This should cause the DCE to stop transmitting, which gives the DTE enough time to read the data from the input buffer. When the input buffer has more room available, the voltage on flow-control lines is set high, and the DCE resumes sending data.

A similar error is CE_OVERRUN. This error occurs when new data arrives before the communications hardware and serial communications driver completely receives old data. This can occur when the transmission speed is too high for the type of communications hardware or CPU. This can also occur when the operating system is not free to service the communications hardware. The only way to alleviate this problem is to apply some combination of decreasing the transmission speed, replacing the communications hardware, and increasing the CPU speed. Sometimes third-party hardware drivers that are not very efficient with CPU resources cause this error. Flow control cannot completely solve the problems that cause the CE_OVERRUN error, although it may help to reduce the frequency of the error.

Software flow control

Software flow control uses data in the communications stream to control the transmission and reception of data. Because software flow control uses two special characters, XOFF and XON, binary transfers cannot use software flow control; the XON or XOFF character may appear in the binary data and would interfere with data transfer. Software flow control befits text-based communications or data being transferred that does not contain the XON and XOFF characters.

In order to enable software flow control, the fOutX and fInX members of the DCB must be set to TRUE. The fOutX member controls output flow control. The fInX member controls input flow control.

One thing to note is that the DCB allows the program to dynamically assign the values the system recognizes as flow-control characters. The XoffChar member of the DCB dictates the XOFF character for both input and output flow control. The XonChar member of the DCB similarly dictates the XON character.

For input flow control, the XoffLim member of the DCB specifies the minimum amount of free space allowed in the input buffer before the XOFF character is sent. If the amount of free space in the input buffer drops below this amount, then the XOFF character is sent. For input flow control, the XonLim member of the DCB specifies the minimum number of bytes allowed in the input buffer before the XON character is sent. If the amount of data in the input buffer drops below this value, then the XON character is sent.

Table 4 lists the behavior of the DTE when using XOFF/XON flow control.

Table 4. Software flow-control behavior

Flow-control characterBehavior
XOFF received by DTEDTE transmission is suspended until XON is received. DTE reception continues. The fOutX member of the DCB controls this behavior.
XON received by DTEIf DTE transmission is suspended because of a previous XOFF character being received, DTE transmission is resumed. The fOutX member of the DCB controls this behavior.
XOFF sent from DTEXOFF is automatically sent by the DTE when the receive buffer approaches full. The actual limit is dictated by the XoffLim member of the DCB. The fInX member of the DCB controls this behavior. DTE transmission is controlled by the fTXContinueOnXoff member of the DCB as described below.
XON sent from the DTEXON is automatically sent by the DTE when the receive buffer approaches empty. The actual limit is dictated by the XonLim member of the DCB. The fInX member of the DCB controls this behavior.

If software flow control is enabled for input control, then the fTXContinueOnXoff member of the DCB takes effect. The fTXContinueOnXoff member controls whether transmission is suspended after the XOFF character is automatically sent by the system. If fTXContinueOnXoff is TRUE, then transmission continues after the XOFF is sent when the receive buffer is full. If fTXContinueOnXoff is FALSE, then transmission is suspended until the system automatically sends the XON character. DCE devices using software flow control will suspend their sending after the XOFF character is received. Some equipment will resume sending when the XON character is sent by the DTE. On the other hand, some DCE devices will resume sending after any character arrives. The fTXContinueOnXoff member should be set to FALSE when communicating with a DCE device that resumes sending after any character arrives. If the DTE continued transmission after it automatically sent the XOFF, the resumption of transmission would cause the DCE to continue sending, defeating the XOFF.

There is no mechanism available in the Windows API to cause the DTE to behave the same way as these devices. The DCB structure contains no members for specifying suspended transmission to resume when any character is received. The XON character is the only character that causes transmission to resume.

One other interesting note about software flow control is that reception of XON and XOFF characters causes pending read operations to complete with zero bytes read. The XON and XOFF characters cannot be read by the application, since they are not placed in the input buffer.

A lot of programs on the market, including the Terminal program that comes with Windows, give the user three choices for flow control: Hardware, Software, or None. The Windows operating system itself does not limit an application in this way. The settings of the DCB allow for Software and Hardware flow control simultaneously. In fact, it is possible to separately configure each member of the DCB that affects flow control, which allows for several different flow-control configurations. The limits placed on flow-control choices are there for ease-of-use reasons to reduce confusion for end users. The limits placed on flow-control choices may also be because devices used for communications may not support all types of flow control.

Communications Time-outs

Another major topic affecting the behavior of read and write operations is time-outs. Time-outs affect read and write operations in the following way. If an operation takes longer than the computed time-out period, the operation is completed. There is no error code that is returned by ReadFile, WriteFile, GetOverlappedResult, or WaitForSingleObject. All indicators used to monitor the operation indicate that it completed successfully. The only way to tell that the operation timed out is that the number of bytes actually transferred are fewer than the number of bytes requested. So, if ReadFile returns TRUE, but fewer bytes were read than were requested, the operation timed out. If an overlapped write operation times out, the overlapped event handle is signaled and WaitForSingleObject returns WAIT_OBJECT_O. GetOverlappedResult returns TRUE, but dwBytesTransferred contains the number of bytes that were transferred before the time-out. The following code demonstrates how to handle this in an overlapped write operation:

BOOL WriteABuffer(char * lpBuf, DWORD dwToWrite)
{
   OVERLAPPED osWrite = {0};
   DWORD dwWritten;
   DWORD dwRes;
   BOOL  fRes;

   // Create this write operation's OVERLAPPED structure hEvent.
   osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   if (osWrite.hEvent == NULL)
      // Error creating overlapped event handle.
      return FALSE;

   // Issue write
   if (!WriteFile(hComm, lpBuf, dwToWrite, &dwWritten, &osWrite)) {
      if (GetLastError() != ERROR_IO_PENDING) { 
         // WriteFile failed, but it isn't delayed. Report error.
         fRes = FALSE;
      }
      else
         // Write is pending.
         dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);
         switch(dwRes)
         {
            // Overlapped event has been signaled. 
            case WAIT_OBJECT_0:
                 if (!GetOverlappedResult(hComm, &osWrite, &dwWritten, FALSE))
                       fRes = FALSE;
                 else {
                  if (dwWritten != dwToWrite) {
                     // The write operation timed out. I now need to 
                     // decide if I want to abort or retry. If I retry, 
                     // I need to send only the bytes that weren't sent. 
                     // If I want to abort, I would just set fRes to 
                     // FALSE and return.
                     fRes = FALSE;
                  }
                  else
                     // Write operation completed successfully.
                     fRes = TRUE;
                }
                 break;
            
            default:
                 // An error has occurred in WaitForSingleObject. This usually 
                // indicates a problem with the overlapped event handle.
                 fRes = FALSE;
                 break;
         }
      }
   }
   else {
      // WriteFile completed immediately.

      if (dwWritten != dwToWrite) {
          // The write operation timed out. I now need to 
          // decide if I want to abort or retry. If I retry, 
          // I need to send only the bytes that weren't sent. 
          // If I want to abort, then I would just set fRes to 
          // FALSE and return.
          fRes = FALSE;
      }
      else
          fRes = TRUE;
   }

   CloseHandle(osWrite.hEvent);
   return fRes;
}

The SetCommTimeouts function specifies the communications time-outs for a port. To retrieve the current time-outs for a port, a program calls the GetCommTimeouts function. An applications should retrieve the communications time-outs before modifying them. This allows the application to set time-outs back to their original settings when it finishes with the port. Following is an example of setting new time-outs using SetCommTimeouts:

COMMTIMEOUTS timeouts;

timeouts.ReadIntervalTimeout = 20; 
timeouts.ReadTotalTimeoutMultiplier = 10;
timeouts.ReadTotalTimeoutConstant = 100;
timeouts.WriteTotalTimeoutMultiplier = 10;
timeouts.WriteTotalTimeoutConstant = 100;

if (!SetCommTimeouts(hComm, &timeouts))
   // Error setting time-outs.

Note   Once again, communications time-outs are not the same as time-out values supplied in synchronization functions. WaitForSingleObject, for instance, uses a time-out value to wait for an object to become signaled; this is not the same as a communications time-out.

Setting the members of the COMMTIMEOUTS structure to all zeros causes no time-outs to occur. Nonoverlapped operations will block until all the requested bytes are transferred. The ReadFile function is blocked until all the requested characters arrive at the port. The WriteFile function is blocked until all requested characters are sent out. On the other hand, an overlapped operation will not finish until all the characters are transferred or the operation is aborted. The following conditions occur until the operation is completed:

  • WaitForSingleObject always returns WAIT_TIMEOUT if a synchronization time-out is supplied. WaitForSingleObject will block forever if an INFINITE synchronization time-out is used.
  • GetOverlappedResult always returns FALSE and GetLastError returns ERROR_IO_INCOMPLETE if called directly after the call to GetOverlappedResult.

Setting the members of the COMMTIMEOUTS structure in the following manner causes read operations to complete immediately without waiting for any new data to arrive:

COMMTIMEOUTS timeouts;

timeouts.ReadIntervalTimeout = MAXDWORD; 
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0;

if (!SetCommTimeouts(hComm, &timeouts))
   // Error setting time-outs.

These settings are necessary when used with an event-based read described in the "Caveat" section earlier. In order for ReadFile to return 0 bytes read, the ReadIntervalTimeout member of the COMMTIMEOUTS structure is set to MAXDWORD, and the ReadTimeoutMultiplier and ReadTimeoutConstant are both set to zero.

An application must always specifically set communications time-outs when it uses a communications port. The behavior of read and write operations is affected by communications time-outs. When a port is initially open, it uses default time-outs supplied by the driver or time-outs left over from a previous communications application. If an application assumes that time-outs are set a certain way, while the time-outs are actually different, then read and write operations may never complete or may complete too often.

Conclusion

This article serves as a discussion of some of the common pitfalls and questions that arise when developing a serial communications application. The Multithreaded TTY sample that comes with this article is designed using many of the techniques discussed here. Download it and try it out. Learning how it works will provide a thorough understanding of the serial communications functions.

Bibliography

Brain, Marshall. Win32 System Services: The Heart of Windows NT. Englewood Cliffs, NJ: Prentice Hall, 1994.

Campbell, Joe. C Programmer's Guide to Serial Communications. 2d ed. Indianapolis, IN: Howard W. Sams & Company, 1994.

Mirho, Charles, and Andy Terrice. "Create Communications Programs for Windows 95 with the Win32 Comm API." Microsoft Systems Journal 12 (December 1994). (MSDN Library, Books and Periodicals)

'MFC' 카테고리의 다른 글

Using the Windows Headers  (0) 2016.06.29
COM9 보다 큰 직렬 포트를 지정  (0) 2016.06.07
FormatMessage function  (0) 2016.06.07
Retrieving the Last-Error Code  (0) 2016.06.07
Visual Studio Shortcuts(2013)  (0) 2015.06.25
Posted by 곰돌이짱
,

FormatMessage function

MFC 2016. 6. 7. 16:47

CString GetFormatMessageString(DWORD dwError)
{
     LPVOID lpMsgBuf;


     FormatMessage(  FORMAT_MESSAGE_ALLOCATE_BUFFER
                          | FORMAT_MESSAGE_FROM_SYSTEM
                          | FORMAT_MESSAGE_IGNORE_INSERTS
                          , NULL
                          , dwError
                          , MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
                          , (LPTSTR)&lpMsgBuf
                          , 0
                          , NULL );

     CString str;
   

str.Format(_T("%s"), lpMsgBuf);

   

LocalFree(lpMsgBuf);

return str;

}



https://msdn.microsoft.com/ko-kr/library/windows/desktop/ms679351(v=vs.85).aspx

FormatMessage function

Formats a message string. The function requires a message definition as input. The message definition can come from a buffer passed into the function. It can come from a message table resource in an already-loaded module. Or the caller can ask the function to search the system's message table resource(s) for the message definition. The function finds the message definition in a message table resource based on a message identifier and a language identifier. The function copies the formatted message text to an output buffer, processing any embedded insert sequences if requested.

Syntax

C++
DWORD WINAPI FormatMessage(
  _In_     DWORD   dwFlags,
  _In_opt_ LPCVOID lpSource,
  _In_     DWORD   dwMessageId,
  _In_     DWORD   dwLanguageId,
  _Out_    LPTSTR  lpBuffer,
  _In_     DWORD   nSize,
  _In_opt_ va_list *Arguments
);

Parameters

dwFlags [in]

The formatting options, and how to interpret the lpSource parameter. The low-order byte of dwFlags specifies how the function handles line breaks in the output buffer. The low-order byte can also specify the maximum width of a formatted output line.

This parameter can be one or more of the following values.

ValueMeaning
FORMAT_MESSAGE_ALLOCATE_BUFFER
0x00000100

The function allocates a buffer large enough to hold the formatted message, and places a pointer to the allocated buffer at the address specified by lpBuffer. The lpBuffer parameter is a pointer to an LPTSTR; you must cast the pointer to an LPTSTR (for example, (LPTSTR)&lpBuffer). The nSize parameter specifies the minimum number of TCHARs to allocate for an output message buffer. The caller should use the LocalFree function to free the buffer when it is no longer needed.

If the length of the formatted message exceeds 128K bytes, then FormatMessage will fail and a subsequent call to GetLastError will return ERROR_MORE_DATA.

In previous versions of Windows, this value was not available for use when compiling Windows Store apps. As of Windows 10 this value can be used.

Windows Server 2003 and Windows XP:  

If the length of the formatted message exceeds 128K bytes, then FormatMessage will not automatically fail with an error of ERROR_MORE_DATA.

Windows 10:  

LocalFree is not in the modern SDK, so it cannot be used to free the result buffer. Instead, use HeapFree (GetProcessHeap(), allocatedMessage). In this case, this is the same as calling LocalFree on memory.

Important: LocalAlloc() has different options: LMEM_FIXED, and LMEM_MOVABLE. FormatMessage() uses LMEM_FIXED, so HeapFree can be used. If LMEM_MOVABLE is used, HeapFree cannot be used.

FORMAT_MESSAGE_ARGUMENT_ARRAY
0x00002000

The Arguments parameter is not a va_list structure, but is a pointer to an array of values that represent the arguments.

This flag cannot be used with 64-bit integer values. If you are using a 64-bit integer, you must use the va_list structure.

FORMAT_MESSAGE_FROM_HMODULE
0x00000800

The lpSource parameter is a module handle containing the message-table resource(s) to search. If this lpSource handle is NULL, the current process's application image file will be searched. This flag cannot be used with FORMAT_MESSAGE_FROM_STRING.

If the module has no message table resource, the function fails with ERROR_RESOURCE_TYPE_NOT_FOUND.

FORMAT_MESSAGE_FROM_STRING
0x00000400

The lpSource parameter is a pointer to a null-terminated string that contains a message definition. The message definition may contain insert sequences, just as the message text in a message table resource may. This flag cannot be used with FORMAT_MESSAGE_FROM_HMODULE or FORMAT_MESSAGE_FROM_SYSTEM.

FORMAT_MESSAGE_FROM_SYSTEM
0x00001000

The function should search the system message-table resource(s) for the requested message. If this flag is specified with FORMAT_MESSAGE_FROM_HMODULE, the function searches the system message table if the message is not found in the module specified by lpSource. This flag cannot be used with FORMAT_MESSAGE_FROM_STRING.

If this flag is specified, an application can pass the result of the GetLastError function to retrieve the message text for a system-defined error.

FORMAT_MESSAGE_IGNORE_INSERTS
0x00000200

Insert sequences in the message definition are to be ignored and passed through to the output buffer unchanged. This flag is useful for fetching a message for later formatting. If this flag is set, the Arguments parameter is ignored.

 

The low-order byte of dwFlags can specify the maximum width of a formatted output line. The following are possible values of the low-order byte.

ValueMeaning
0

There are no output line width restrictions. The function stores line breaks that are in the message definition text into the output buffer.

FORMAT_MESSAGE_MAX_WIDTH_MASK
0x000000FF

The function ignores regular line breaks in the message definition text. The function stores hard-coded line breaks in the message definition text into the output buffer. The function generates no new line breaks.

 

If the low-order byte is a nonzero value other than FORMAT_MESSAGE_MAX_WIDTH_MASK, it specifies the maximum number of characters in an output line. The function ignores regular line breaks in the message definition text. The function never splits a string delimited by white space across a line break. The function stores hard-coded line breaks in the message definition text into the output buffer. Hard-coded line breaks are coded with the %n escape sequence.

lpSource [in, optional]

The location of the message definition. The type of this parameter depends upon the settings in the dwFlags parameter.

dwFlags SettingMeaning
FORMAT_MESSAGE_FROM_HMODULE
0x00000800

A handle to the module that contains the message table to search.

FORMAT_MESSAGE_FROM_STRING
0x00000400

Pointer to a string that consists of unformatted message text. It will be scanned for inserts and formatted accordingly.

 

If neither of these flags is set in dwFlags, then lpSource is ignored.

dwMessageId [in]

The message identifier for the requested message. This parameter is ignored if dwFlags includes FORMAT_MESSAGE_FROM_STRING.

dwLanguageId [in]

The language identifier for the requested message. This parameter is ignored if dwFlags includes FORMAT_MESSAGE_FROM_STRING.

If you pass a specific LANGID in this parameter, FormatMessage will return a message for that LANGID only. If the function cannot find a message for that LANGID, it sets Last-Error to ERROR_RESOURCE_LANG_NOT_FOUND. If you pass in zero, FormatMessage looks for a message for LANGIDs in the following order:

  1. Language neutral
  2. Thread LANGID, based on the thread's locale value
  3. User default LANGID, based on the user's default locale value
  4. System default LANGID, based on the system default locale value
  5. US English

If FormatMessage does not locate a message for any of the preceding LANGIDs, it returns any language message string that is present. If that fails, it returns ERROR_RESOURCE_LANG_NOT_FOUND.

lpBuffer [out]

A pointer to a buffer that receives the null-terminated string that specifies the formatted message. If dwFlags includes FORMAT_MESSAGE_ALLOCATE_BUFFER, the function allocates a buffer using the LocalAlloc function, and places the pointer to the buffer at the address specified in lpBuffer.

This buffer cannot be larger than 64K bytes.

nSize [in]

If the FORMAT_MESSAGE_ALLOCATE_BUFFER flag is not set, this parameter specifies the size of the output buffer, in TCHARs. If FORMAT_MESSAGE_ALLOCATE_BUFFER is set, this parameter specifies the minimum number of TCHARs to allocate for an output buffer.

The output buffer cannot be larger than 64K bytes.

Arguments [in, optional]

An array of values that are used as insert values in the formatted message. A %1 in the format string indicates the first value in the Arguments array; a %2 indicates the second argument; and so on.

The interpretation of each value depends on the formatting information associated with the insert in the message definition. The default is to treat each value as a pointer to a null-terminated string.

By default, the Arguments parameter is of type va_list*, which is a language- and implementation-specific data type for describing a variable number of arguments. The state of the va_list argument is undefined upon return from the function. To use the va_list again, destroy the variable argument list pointer using va_end and reinitialize it with va_start.

If you do not have a pointer of type va_list*, then specify the FORMAT_MESSAGE_ARGUMENT_ARRAY flag and pass a pointer to an array of DWORD_PTR values; those values are input to the message formatted as the insert values. Each insert must have a corresponding element in the array.

Return value

If the function succeeds, the return value is the number of TCHARs stored in the output buffer, excluding the terminating null character.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

Remarks

Within the message text, several escape sequences are supported for dynamically formatting the message. These escape sequences and their meanings are shown in the following tables. All escape sequences start with the percent character (%).

Escape sequenceMeaning
%0Terminates a message text line without a trailing new line character. This escape sequence can be used to build up long lines or to terminate the message itself without a trailing new line character. It is useful for prompt messages.
%n!format string!

Identifies an insert. The value of n can be in the range from 1 through 99. The format string (which must be surrounded by exclamation marks) is optional and defaults to !s! if not specified. For more information, see Format Specification Fields.

The format string can include a width and precision specifier for strings and a width specifier for integers. Use an asterisk (*) to specify the width and precision. For example, %1!*.*s! or %1!*u!.

If you do not use the width and precision specifiers, the insert numbers correspond directly to the input arguments. For example, if the source string is "%1 %2 %1" and the input arguments are "Bill" and "Bob", the formatted output string is "Bill Bob Bill".

However, if you use a width and precision specifier, the insert numbers do not correspond directly to the input arguments. For example, the insert numbers for the previous example could change to "%1!*.*s! %4 %5!*s!".

The insert numbers depend on whether you use an arguments array (FORMAT_MESSAGE_ARGUMENT_ARRAY) or a va_list. For an arguments array, the next insert number is n+2 if the previous format string contained one asterisk and is n+3 if two asterisks were specified. For a va_list, the next insert number is n+1 if the previous format string contained one asterisk and is n+2 if two asterisks were specified.

If you want to repeat "Bill", as in the previous example, the arguments must include "Bill" twice. For example, if the source string is "%1!*.*s! %4 %5!*s!", the arguments could be, 4, 2, Bill, Bob, 6, Bill (if using the FORMAT_MESSAGE_ARGUMENT_ARRAY flag). The formatted string would then be "  Bi Bob   Bill".

Repeating insert numbers when the source string contains width and precision specifiers may not yield the intended results. If you replaced %5 with %1, the function would try to print a string at address 6 (likely resulting in an access violation).

Floating-point format specifiers—e, E, f, and g—are not supported. The workaround is to use the StringCchPrintf function to format the floating-point number into a temporary buffer, then use that buffer as the insert string.

Inserts that use the I64 prefix are treated as two 32-bit arguments. They must be used before subsequent arguments are used. Note that it may be easier for you to use StringCchPrintf instead of this prefix.

 

Any other nondigit character following a percent character is formatted in the output message without the percent character. Following are some examples.

Format stringResulting output
%%A single percent sign.
%spaceA single space. This format string can be used to ensure the appropriate number of trailing spaces in a message text line.
%.A single period. This format string can be used to include a single period at the beginning of a line without terminating the message text definition.
%!A single exclamation point. This format string can be used to include an exclamation point immediately after an insert without its being mistaken for the beginning of a format string.
%nA hard line break when the format string occurs at the end of a line. This format string is useful when FormatMessage is supplying regular line breaks so the message fits in a certain width.
%rA hard carriage return without a trailing newline character.
%tA single tab.

 

'MFC' 카테고리의 다른 글

COM9 보다 큰 직렬 포트를 지정  (0) 2016.06.07
Serial Communications  (0) 2016.06.07
Retrieving the Last-Error Code  (0) 2016.06.07
Visual Studio Shortcuts(2013)  (0) 2015.06.25
Message Reflection for Windows Controls  (0) 2015.05.27
Posted by 곰돌이짱
,

Retrieving the Last-Error Code

When many system functions fail, they set the last-error code. If your application needs more details about an error, it can retrieve the last-error code using the GetLastError function and display a description of the error using the FormatMessage function.

The following example includes an error-handling function that prints the error message and terminates the process. The lpszFunction parameter is the name of the function that set the last-error code.

C++
#include <windows.h>
#include <strsafe.h>

void ErrorExit(LPTSTR lpszFunction) 
{ 
    // Retrieve the system error message for the last-error code

    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError(); 

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf,
        0, NULL );

    // Display the error message and exit the process

    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
        (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); 
    StringCchPrintf((LPTSTR)lpDisplayBuf, 
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"), 
        lpszFunction, dw, lpMsgBuf); 
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
    ExitProcess(dw); 
}

void main()
{
    // Generate an error

    if(!GetProcessId(NULL))
        ErrorExit(TEXT("GetProcessId"));
}


 

 


'MFC' 카테고리의 다른 글

Serial Communications  (0) 2016.06.07
FormatMessage function  (0) 2016.06.07
Visual Studio Shortcuts(2013)  (0) 2015.06.25
Message Reflection for Windows Controls  (0) 2015.05.27
OnCommand 메시지 처리기 변경  (0) 2015.05.27
Posted by 곰돌이짱
,

https://support.microsoft.com/ko-kr/kb/176938




regsvr32  "C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\IViewers.Dll"

'MFC > Error' 카테고리의 다른 글

외부 확인 되지 않은 LNK2001" DEFINE_GUID를 사용하여 하는 방법  (0) 2016.06.29
Debug Assertion Failed in occont.cpp Line 926  (0) 2015.11.20
C2143  (0) 2015.06.24
C2504  (0) 2015.05.27
LNK 4070  (0) 2014.09.11
Posted by 곰돌이짱
,

Debug Assertion Failed in occont.cpp Line 926

https://support.microsoft.com/en-us/kb/949107


Action
Development or migration of an MFC application with ActiveX controls in Visual Studio 2005 SP1.
Result
You may get the following error message:
---------------------------





Cause
This problem can occur when the ActiveX controls are not properly registered on the deployment machine.
Resolution
Register ActiveX controls using regsvr32.exe.



'MFC > Error' 카테고리의 다른 글

외부 확인 되지 않은 LNK2001" DEFINE_GUID를 사용하여 하는 방법  (0) 2016.06.29
OLE/COM Object Viewer - DllRegisterServer in IVIEWERS.DLL failed  (0) 2016.04.18
C2143  (0) 2015.06.24
C2504  (0) 2015.05.27
LNK 4070  (0) 2014.09.11
Posted by 곰돌이짱
,