Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Delphi, DLLs and FormatMessage

158 views
Skip to first unread message

Chris Krohn

unread,
Sep 1, 2005, 9:45:39 AM9/1/05
to
I'm writing a Windows Service application and am using a DLL with a
MessageTable resource bound to it for the event messages. I am also
trying to get at these messages from within the service so that I can
use the same messages in a debug log. I am using FormatMessage to try
and get these messages, but no matter what I seem to do the
FormatMessage call always fails and GetLastError indicates "The
parameter is incorrect.". The odd thing is that the following code
works fine if I use a system DLL like WinInet.dll but not with my DLL.
Is there something special I must do to my DLL so that it works with
FormatMessage?

Function FormatMessageID(Module: HModule; MsgID: Integer;
MsgData: Array Of Const): String;
Var
Buffer: PChar;
Begin
If FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER or
FORMAT_MESSAGE_FROM_HMODULE, Pointer(Module),
MsgID, 0, @Buffer, 0, Nil) = 0 then
Raise Exception.Create(SysErrorMessage(GetLastError));
Try
Buffer[Lstrlen(Buffer) - 2] := #0;
Result := Buffer;
Finally
LocalFree(Cardinal(Buffer));
End;
End;

The calling code:

Module := LoadLibraryEx(PChar(ModuleName), 0,
LOAD_LIBRARY_AS_DATAFILE);
ShowMessage(FormatMessageID(Module, 1, []));
FreeLibrary(Module);

Where ModuleName is a constant that contains the path and name of my
DLL. My DLL is a simple DPR defined as:

Library BM_Msg;

{$R Messages.res}

Begin
End.

Finally, I used the free XN Resource Editor to create the Message
Table.

Any idea? Anyone?

Chris
---
chrisatkrohndotorg
Cayman Islands

Chris Krohn

unread,
Sep 1, 2005, 10:29:52 AM9/1/05
to
As it turns out, the problem was that the particular message I was testing with contained
place holders (%1, %2, etc) for strings that are supposed to be passed via the Arguments
parameter of FormatMessage (I pass Nil in the example below). When I selected a message
with no place holders the function below works properly. I had assumed that if no strings
were passed, that FormatMessage would place empty values in the place holders but still
work - apparently not. Here is the completed function for those interested: If anyone
can make the building of the argument array more efficient, I'd love to see it.

--------------------------------------------------------------

// Given a DLL/EXE with a message table, a message ID, and an array of strings
// to substitute for place holders in the message, return a formatted message.


Function FormatMessageID(Module: HModule; MsgID: Integer;
MsgData: Array Of Const): String;
Var

I: Integer; // Loop index variable
T: String; // Temp string to hold value from MsgData
Buffer: PChar; // Buffer with returned message
S: Array Of PChar; // Array of args passed to FormatMessage
Begin
// Convert the passed variable array of variants to an array of PChars
SetLength(S, High(MsgData) + 1); // Set the number of PChar strings
For I := 0 To High(MsgData) Do
Begin
// Limitation of code requires only strings be passed. Could expand the
// case statement below to handle all types of variants, but this is not
// needed for our purposes.
If Not (MsgData[I].VType In [vtString, vtAnsiString, vtWideString]) Then
Raise Exception.Create('Only string values allowed for message data.');

// Convert variants to standard strings.
Case MsgData[I].VType Of
vtString: T := MsgData[I].VString^;
vtAnsiString: T := String(MsgData[I].VAnsiString);
vtWideString: T := String(MsgData[I].VWideString);
End;

// Allocate PChar memory for string and copy string to it.
GetMem(S[I], Length(T) + 1);
StrPCopy(S[I], T);
End;

Try
// Call the Win API FormatMessage function. Parameters tell FormatMessage
// to: allocate memory to Buffer for resultant message, get the message
// template from a DLL or EXE specified by Module, and treat the arguments
// array as a standard array of 32 bit pointers. Also pass the module
// handle (typecasted as a Pointer) retrieved by a LoadLibraryEx call, the
// MsgID of the message in the message table, a 0 for the language id which
// causes FormatMessage to search automatically for the best language, the
// address of the buffer which FormatMessage will allocate memory for and
// then return the completed message, the minimum amount of memory that
// FormatMessage should allocate in Buffer (in this case 0), and finally
// the PChar array of argument strings. If successfull, it should return
// the size of the message. If it fails, it returns a size of 0.
If FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER Or
FORMAT_MESSAGE_FROM_HMODULE Or
FORMAT_MESSAGE_ARGUMENT_ARRAY, Pointer(Module),
MsgID, 0, @Buffer, 0, S) = 0 then
Raise Exception.Create(SysErrorMessage(GetLastError)); // Failed, get reason
Try // and raise except.
Buffer[Lstrlen(Buffer) - 2] := #0; // Trim off CR/LF.
Result := Buffer; // Return the results.
Finally
LocalFree(Cardinal(Buffer)); // Free the memory FormatMessage alloc.
End;
Finally
For I := 0 To High(S) Do // Free memory allocated in PChar array.
FreeMem(S[I]);
End;
End;

Chris
---
chrisatkrohndotorg
Cayman Islands

Riki Wiki

unread,
Sep 3, 2005, 4:16:06 AM9/3/05
to
0 new messages