2

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?

Class Skeleton
  • 2,913
  • 6
  • 31
  • 51
404
  • 8,022
  • 2
  • 27
  • 47
  • 2
    Well, you're right, it is **never** correct to use a cast. It only stops the compiler from telling you that you are doing it wrong. Why you think you need it is undiscoverable if you don't post your code. – Hans Passant Jun 13 '14 at 21:58
  • 3
    You'll need to show a bit of your code. How exactly are you declaring `errortext`. How is its memory allocated? Are you using `FORMAT_MESSAGE_ALLOCATE_BUFFER`? – zdan Jun 13 '14 at 21:59
  • 2
    @HansPassant Isn't this an example of a situation where a cast **is the only solution**? That's why I was thinking it's a good question, it is not about debugging, but about understanding why a cast is needed, IMO. – alain Jun 14 '14 at 11:36
  • No, char[] and wchar_t[] are not structurally compatible, the elements have a different size. Casting anyway either produces Chinese or a single character C string and high odds for buffer overflow, YMMV. It requires a conversion, like MultiByteToWideString(). – Hans Passant Jun 14 '14 at 11:45
  • But the cast is to `TCHAR*`, because the function only takes `TCHAR*`. There are 2 different usages of the function, one with `TCHAR*`, the other with `TCHAR**`, see my answer. – alain Jun 14 '14 at 11:55

1 Answers1

5

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.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
alain
  • 11,939
  • 2
  • 31
  • 51
  • Thanks for your explanation. I don't understand how `&buf` became a `TCHAR**` rather than remaining a `TCHAR*`. The function allocates a buffer and the starting address of the buffer is assigned to `&buf`, right? This would mean that `buf` would remain a regular pointer to the buffer. I'm sure I'm missing something here. Am I casting to convert a `TCHAR**` to a `TCHAR*/LPTSTR` (if so I don't understand how `TCHAR**` came to be), or converting the buffer itself from some default type to `LPTSTR`? – 404 Jun 14 '14 at 20:44
  • 2
    @user3733070 Yes, `buf` is just a pointer to the text, in both cases. In 1), the pointer is passed directly to give the function access to the text, but in 2), the function needs access to the pointer, so you pass the address of the pointer by using `&buf`. `buf` is `TCHAR*` and `&buf` is `TCHAR**`. The function is declared as taking `TCHAR*`, so the `TCHAR**` is cast back to `TCHAR*`. – alain Jun 15 '14 at 09:55