eurotrash eurotrash - 2 months ago 8
C Question

Why do I need to cast the lpBuffer (LPTSTR) parameter in FormatMessage?

In Windows' FormatMessage() function, the parameter:

_Out_ LPTSTR lpBuffer


Is doing my head in. Following along from Hart's Windows System Programming book, I'm declaring an
LPTSTR
pointer to be used as the
lpBuffer
(e.g.
LPTSTR errortext;
), and then calling the
FormatMessage()
function.

The correct way to pass in this parameter is:
(LPTSTR)&errorText


This works fine. But I don't understand why I need to write
(LPTSTR)
. I understand that's typecasting and I read about it but it doesn't make sense to me, because I'm not changing the variable type or anything, I declared it as an
LPTSTR
and I'm passing its memory address to the function, the function expects an
LPTSTR
and I passed it an
LPTSTR
, so why do I need to put
(LPTSTR)
as part of the
lpBuffer
parameter?

Answer

The parameter lpBuffer of FormatMessage() is documented as follows:

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.

So there are 2 different usages of FormatMessage(),

1) Provide your own buffer

const DWORD bufsize = ....;
TCHAR buf[bufsize];
FormatMessage(.... buf, bufsize, ....); // buf is passed as a TCHAR*

2) FormatMessage allocates a buffer for you

const DWORD bufsize = ....;
TCHAR* buf = 0;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | ....,
    .... (LPTSTR)&buf, bufsize, ....); // &buf is a TCHAR** so type-cast needed!
....
LocalFree(buf);

In #1, you have to pass the address of the first TCHAR in your buffer, and the function simply fills it the buffer.

In #2, the function needs to tell you where it allocates a new buffer, so you have to tell it where to place that address. You have to pass the address of a pointer variable that receives the address.

In short:

  • #1 needs a TCHAR* to an existing buffer
  • #2 needs a TCHAR** that receives a new buffer

That is why the lpBuffer parameter has to be type-casted when using #2.

Comments