904 lines
24 KiB
C
904 lines
24 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
display.c
|
||
|
||
Abstract:
|
||
|
||
This file contains functions that handle the displaying of messages.
|
||
|
||
Currently a message box is used to display messages. A message queueing
|
||
scheme has been setup so that the messenger worker threads can
|
||
call a function with a pointer to a message buffer. That message will
|
||
get copied into the queue so that the worker thread can go on gathering
|
||
more messages. When the display thread will be doing one of the following:
|
||
1) Displaying a message - waiting for the user to press "ok".
|
||
2) Sleeping - waiting for an event that will _tell it to _read the
|
||
message queue.
|
||
|
||
When the display thread completes displaying a message, it will check
|
||
the queue for the next message to display. If there are no further
|
||
messages, it will go to sleep until a message comes in.
|
||
|
||
Author:
|
||
|
||
Dan Lafferty (danl) 24-Feb-1992
|
||
|
||
Environment:
|
||
|
||
User Mode -Win32
|
||
|
||
Notes:
|
||
|
||
|
||
Revision History:
|
||
|
||
04-Nov-1992 danl
|
||
MsgDisplayThread: Handle Extended Characters. This was done by
|
||
translating the Oem-style characters in the message to the unicode
|
||
equivalent, and then calling the Unicode version of the MessageBox Api.
|
||
It will still call the Ansi version of the MessageBox if the
|
||
string cannot be translated for some reason.
|
||
|
||
26-Oct-1992 danl
|
||
MsgDisplayQueueAdd: Added Beep when message is added to queue.
|
||
Fixed bug where "if (status = TRUE)" caused the GlobalMsgDisplayEvent
|
||
to always be set.
|
||
|
||
24-Feb-1992 danl
|
||
created
|
||
|
||
--*/
|
||
|
||
//
|
||
// INCLUDES
|
||
//
|
||
#include "msrv.h"
|
||
#include <msgdbg.h> // STATIC and MSG_LOG
|
||
#include <string.h> // memcpy
|
||
#include <winuser.h> // MessageBox
|
||
#include "msgdata.h" // GlobalMsgDisplayEvent
|
||
|
||
//
|
||
// DEFINES
|
||
//
|
||
|
||
#define MAX_QUEUE_SIZE 25
|
||
#define WAIT_FOREVER 0xffffffff
|
||
|
||
//
|
||
// Queue Entry Structure
|
||
//
|
||
typedef struct _QUEUE_ENTRY {
|
||
struct _QUEUE_ENTRY *Next;
|
||
ULONG SessionId;
|
||
SYSTEMTIME BigTime;
|
||
CHAR Message[1];
|
||
}QUEUE_ENTRY, *LPQUEUE_ENTRY;
|
||
|
||
//
|
||
// GLOBALS
|
||
//
|
||
|
||
//
|
||
// This critical section serializes access to all the other globals.
|
||
//
|
||
CRITICAL_SECTION MsgDisplayCriticalSection;
|
||
|
||
//
|
||
// Used to wakeup the display thread if it was put to sleep due to
|
||
// not having a user desktop to display the message on.
|
||
//
|
||
HANDLE hGlobalDisplayEvent;
|
||
|
||
//
|
||
// These are the Display Queue pointers & counts.
|
||
//
|
||
LPQUEUE_ENTRY GlobalMsgQueueHead;
|
||
LPQUEUE_ENTRY GlobalMsgQueueTail;
|
||
DWORD GlobalMsgQueueCount;
|
||
|
||
BOOL fGlobalInitialized;
|
||
|
||
//
|
||
// This indicates whether there is a display thread already available that
|
||
// can service requests. If this is false, it means a new thread will
|
||
// need to be created.
|
||
//
|
||
HANDLE GlobalDisplayThread;
|
||
|
||
//
|
||
// Function Prototypes
|
||
//
|
||
|
||
|
||
BOOL
|
||
MsgDisplayQueueRead(
|
||
OUT LPQUEUE_ENTRY *pQueueEntry
|
||
);
|
||
|
||
DWORD
|
||
MsgDisplayThread(
|
||
LPVOID parm
|
||
);
|
||
|
||
VOID
|
||
MsgMakeNewFormattedMsg(
|
||
LPWSTR *ppHead,
|
||
LPWSTR *ppTime,
|
||
LPWSTR *ppBody,
|
||
SYSTEMTIME BigTime
|
||
);
|
||
|
||
|
||
BOOL
|
||
MsgDisplayQueueAdd(
|
||
IN LPSTR pMsgBuffer,
|
||
IN DWORD MsgSize,
|
||
IN ULONG SessionId,
|
||
IN SYSTEMTIME BigTime
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function adds a Message to the display queue. If the queue is
|
||
full, the message is rejected.
|
||
|
||
Arguments:
|
||
|
||
pMsgBuffer - This is a pointer to the buffer where the message is
|
||
stored. The message must be in the form of a pre-formatted
|
||
(with message header) NUL-terminated string of ansi characters.
|
||
|
||
MsgSize - Indicates the size (in bytes) of the message in the
|
||
message buffer, including the NUL terminator.
|
||
|
||
BigTime - This is a SYSTEMTIME that indicates the time the message was
|
||
received.
|
||
|
||
Return Value:
|
||
|
||
TRUE - The message was successfully stored in the queue.
|
||
|
||
FALSE - The message was rejected. Either the queue was full, or
|
||
there was not enough memory to store the message in the queue.
|
||
|
||
|
||
--*/
|
||
{
|
||
LPQUEUE_ENTRY pQueueEntry;
|
||
BOOL status;
|
||
DWORD threadId;
|
||
|
||
MSG_LOG(TRACE,"Adding a message to the display queue\n",0);
|
||
|
||
// ***************************
|
||
// **** LOCK QUEUE ACCESS ****
|
||
// ***************************
|
||
EnterCriticalSection(&MsgDisplayCriticalSection);
|
||
|
||
//
|
||
// Is there room for the message in the queue?
|
||
//
|
||
|
||
if (GlobalMsgQueueCount >= MAX_QUEUE_SIZE) {
|
||
MSG_LOG(TRACE,"DisplayQueueAdd: Max Queue Size Exceeded\n",0);
|
||
status = FALSE;
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Allocate memory for the message in the queue.
|
||
//
|
||
pQueueEntry = (LPQUEUE_ENTRY)LocalAlloc(LMEM_FIXED, MsgSize + sizeof(QUEUE_ENTRY));
|
||
|
||
if (pQueueEntry == NULL) {
|
||
MSG_LOG(ERROR,"DisplayQueueAdd: Unable to allocate memory\n",0);
|
||
status = FALSE;
|
||
goto CleanExit;
|
||
}
|
||
|
||
//
|
||
// Copy the message into the queue entry.
|
||
//
|
||
pQueueEntry->Next = NULL;
|
||
memcpy(pQueueEntry->Message, pMsgBuffer, MsgSize);
|
||
pQueueEntry->BigTime = BigTime;
|
||
pQueueEntry->SessionId = SessionId;
|
||
|
||
//
|
||
// Update the queue management pointer.
|
||
//
|
||
|
||
if (GlobalMsgQueueCount == 0) {
|
||
//
|
||
// There are no entries in the queue. So make the head
|
||
// and the tail equal.
|
||
//
|
||
GlobalMsgQueueTail = pQueueEntry;
|
||
GlobalMsgQueueHead = pQueueEntry;
|
||
}
|
||
else {
|
||
//
|
||
// Create the new Queue Tail and have the old tail's next pointer
|
||
// point to the new tail.
|
||
//
|
||
GlobalMsgQueueTail->Next = pQueueEntry;
|
||
GlobalMsgQueueTail = pQueueEntry;
|
||
}
|
||
GlobalMsgQueueCount++;
|
||
status = TRUE;
|
||
|
||
//
|
||
// If a display thread doesn't exist, then create one.
|
||
//
|
||
if (GlobalDisplayThread == NULL) {
|
||
|
||
//
|
||
// No use to create the event in Hydra case, since the thread will never go asleep.
|
||
//
|
||
if (!g_IsTerminalServer)
|
||
{
|
||
|
||
|
||
hGlobalDisplayEvent = CreateEvent( NULL,
|
||
FALSE, // auto-reset
|
||
FALSE, // init to non-signaled
|
||
NULL );
|
||
|
||
}
|
||
|
||
GlobalDisplayThread = CreateThread (
|
||
NULL, // Thread Attributes
|
||
0, // StackSize -- process default
|
||
MsgDisplayThread, // lpStartAddress
|
||
(PVOID)NULL, // lpParameter
|
||
0L, // Creation Flags
|
||
&threadId); // lpThreadId
|
||
|
||
if (GlobalDisplayThread == (HANDLE) NULL) {
|
||
//
|
||
// If we couldn't create the display thread, then we can't do
|
||
// much about it. Might as well leave the entry in the queue.
|
||
// Perhaps we can display it the next time around.
|
||
//
|
||
MSG_LOG(ERROR,"MsgDisplayQueueAdd:CreateThread FAILURE %ld\n",
|
||
GetLastError());
|
||
|
||
if (hGlobalDisplayEvent != NULL) {
|
||
CloseHandle(hGlobalDisplayEvent);
|
||
hGlobalDisplayEvent = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
CleanExit:
|
||
|
||
// *****************************
|
||
// **** UNLOCK QUEUE ACCESS ****
|
||
// *****************************
|
||
LeaveCriticalSection(&MsgDisplayCriticalSection);
|
||
|
||
//
|
||
// If we actually put something in the queue, then beep.
|
||
//
|
||
if (status == TRUE) {
|
||
if (g_IsTerminalServer)
|
||
{
|
||
MsgArrivalBeep( SessionId );
|
||
}
|
||
else
|
||
{
|
||
MessageBeep(MB_OK);
|
||
}
|
||
}
|
||
return(status);
|
||
}
|
||
|
||
|
||
VOID
|
||
MsgDisplayThreadWakeup()
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called at shutdown, or for API requests. It causes
|
||
the display thread to wake up and read the queue again.
|
||
|
||
If the display thread cannot display the message because the MessageBox
|
||
call fails, then we assume it is because the user desktop is not avaiable
|
||
because the screensaver is on, or because the workstation is locked.
|
||
In this case, the display thread hangs around waiting for this
|
||
Event to get signalled. Winlogon calls one of the API entry points
|
||
in order to stimulate the display thread into action again.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
// ***************************
|
||
// **** LOCK QUEUE ACCESS ****
|
||
// ***************************
|
||
EnterCriticalSection(&MsgDisplayCriticalSection);
|
||
|
||
if ( hGlobalDisplayEvent != (HANDLE)NULL ) {
|
||
SetEvent( hGlobalDisplayEvent );
|
||
}
|
||
// *****************************
|
||
// **** UNLOCK QUEUE ACCESS ****
|
||
// *****************************
|
||
LeaveCriticalSection(&MsgDisplayCriticalSection);
|
||
}
|
||
|
||
|
||
DWORD
|
||
MsgDisplayInit(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function initializes everything having to do with the displaying
|
||
of messages. It does the following:
|
||
|
||
Initializes the Locks on global data
|
||
Creates event for display thread to wait on.
|
||
Starts the display thread that will read the msg queue.
|
||
|
||
Arguments:
|
||
|
||
NONE
|
||
|
||
Return Value:
|
||
|
||
Always TRUE.
|
||
|
||
--*/
|
||
{
|
||
DWORD dwError = NO_ERROR;
|
||
NTSTATUS status;
|
||
|
||
MSG_LOG(TRACE,"Initializing the Message Display Code\n",0);
|
||
|
||
//
|
||
// Initialize the Critical Section that protects access to global data.
|
||
//
|
||
status = MsgInitCriticalSection(&MsgDisplayCriticalSection);
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
fGlobalInitialized = TRUE;
|
||
}
|
||
else
|
||
{
|
||
MSG_LOG1(ERROR,
|
||
"MsgDisplayInit: MsgInitCriticalSection failed %#x\n",
|
||
status);
|
||
|
||
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
GlobalMsgQueueHead = NULL;
|
||
GlobalMsgQueueTail = NULL;
|
||
GlobalMsgQueueCount = 0;
|
||
GlobalDisplayThread = NULL;
|
||
|
||
return dwError;
|
||
}
|
||
|
||
|
||
VOID
|
||
MsgDisplayEnd(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function makes sure the Display Thread has completed its work,
|
||
and free's up all of its resources.
|
||
|
||
*** IMPORTANT ***
|
||
NOTE: This function should only be called when it is no longer possible
|
||
for the MsgDisplayQueueAdd function to get called.
|
||
|
||
Arguments:
|
||
|
||
NONE.
|
||
|
||
Return Value:
|
||
|
||
NONE.
|
||
|
||
--*/
|
||
{
|
||
LPQUEUE_ENTRY freeEntry;
|
||
|
||
if (!fGlobalInitialized) {
|
||
return;
|
||
}
|
||
|
||
// ***************************
|
||
// **** LOCK QUEUE ACCESS ****
|
||
// ***************************
|
||
EnterCriticalSection(&MsgDisplayCriticalSection);
|
||
|
||
if (GlobalDisplayThread != NULL) {
|
||
TerminateThread(GlobalDisplayThread,0);
|
||
CloseHandle( GlobalDisplayThread );
|
||
}
|
||
|
||
//
|
||
// To make sure a new thread won't be created...
|
||
//
|
||
GlobalDisplayThread = INVALID_HANDLE_VALUE;
|
||
|
||
//
|
||
// Free memory in the queue
|
||
//
|
||
while(GlobalMsgQueueCount > 0) {
|
||
|
||
freeEntry = GlobalMsgQueueHead;
|
||
GlobalMsgQueueHead = GlobalMsgQueueHead->Next;
|
||
LocalFree(freeEntry);
|
||
GlobalMsgQueueCount--;
|
||
|
||
}
|
||
if (hGlobalDisplayEvent != NULL) {
|
||
CloseHandle(hGlobalDisplayEvent);
|
||
hGlobalDisplayEvent = NULL;
|
||
}
|
||
|
||
fGlobalInitialized = FALSE;
|
||
|
||
// *****************************
|
||
// **** UNLOCK QUEUE ACCESS ****
|
||
// *****************************
|
||
LeaveCriticalSection(&MsgDisplayCriticalSection);
|
||
|
||
DeleteCriticalSection(&MsgDisplayCriticalSection);
|
||
|
||
MSG_LOG(TRACE,"The Display has free'd resources and is terminating\n",0);
|
||
}
|
||
|
||
|
||
BOOL
|
||
MsgDisplayQueueRead(
|
||
OUT LPQUEUE_ENTRY *pQueueEntry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Pulls a display entry out of the display queue.
|
||
|
||
Arguments:
|
||
|
||
pQueueEntry - This is a pointer to a location where a pointer to the
|
||
queue entry structure can be placed.
|
||
|
||
Return Value:
|
||
|
||
TRUE - If an entry was found.
|
||
FALSE- If an entry wasn't found.
|
||
|
||
Note on LOCKS:
|
||
|
||
The caller MUST hold the MsgDisplayCriticalSection Lock prior to calling
|
||
this function!!!
|
||
|
||
|
||
--*/
|
||
{
|
||
BOOL status;
|
||
|
||
//
|
||
// If there is data in the queue, then get the pointer to the queue
|
||
// entry from the queue head. Then decrement the queue count and
|
||
// set the queue head to the next entry (which could be zero if there
|
||
// are no more).
|
||
//
|
||
if (GlobalMsgQueueCount != 0) {
|
||
*pQueueEntry = GlobalMsgQueueHead;
|
||
GlobalMsgQueueCount--;
|
||
GlobalMsgQueueHead = (*pQueueEntry)->Next;
|
||
status = TRUE;
|
||
MSG_LOG(TRACE,"A message was read from the display queue\n",0);
|
||
}
|
||
else{
|
||
status = FALSE;
|
||
*pQueueEntry = NULL;
|
||
}
|
||
|
||
return(status);
|
||
|
||
}
|
||
|
||
|
||
DWORD
|
||
MsgDisplayThread(
|
||
LPVOID parm
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
Note:
|
||
|
||
This worker thread expects that the critical section guarding the
|
||
global queue data is already initialized.
|
||
|
||
|
||
--*/
|
||
{
|
||
LPQUEUE_ENTRY pQueueEntry;
|
||
INT displayStatus;
|
||
DWORD msgrState;
|
||
UNICODE_STRING unicodeString;
|
||
OEM_STRING oemString;
|
||
NTSTATUS ntStatus;
|
||
USHORT unicodeLength;
|
||
LPWSTR pHead; // pointer to header portion of message
|
||
LPSTR pHeadAnsi; // pointer to header of message pulled from queue
|
||
LPWSTR pTime; // pointer to time portion of message
|
||
LPWSTR pBody; // pointer to body of message (just after time)
|
||
SYSTEMTIME BigTime;
|
||
ULONG SessionId; // SessionId of the recipient (found in QUEUE_ENTRY)
|
||
|
||
BOOL MsgToRead = TRUE; // tells us whether or not to sleep.
|
||
|
||
|
||
UNREFERENCED_PARAMETER(parm);
|
||
|
||
pHead = NULL;
|
||
pQueueEntry = NULL;
|
||
|
||
do {
|
||
|
||
//
|
||
// If we are not currently working on displaying a message,
|
||
// then get a new message from the queue.
|
||
//
|
||
if (pHead == NULL)
|
||
{
|
||
// ***************************
|
||
// **** LOCK QUEUE ACCESS ****
|
||
// ***************************
|
||
EnterCriticalSection(&MsgDisplayCriticalSection);
|
||
|
||
if (!MsgDisplayQueueRead(&pQueueEntry))
|
||
{
|
||
//
|
||
// No display entries in the queue. We can leave.
|
||
//
|
||
MsgToRead = FALSE;
|
||
|
||
CloseHandle(GlobalDisplayThread);
|
||
GlobalDisplayThread = NULL;
|
||
|
||
if (hGlobalDisplayEvent != NULL)
|
||
{
|
||
CloseHandle(hGlobalDisplayEvent);
|
||
hGlobalDisplayEvent = NULL;
|
||
}
|
||
|
||
// *****************************
|
||
// **** UNLOCK QUEUE ACCESS ****
|
||
// *****************************
|
||
LeaveCriticalSection(&MsgDisplayCriticalSection);
|
||
//
|
||
// From this point on, we can't access any global
|
||
// variables.
|
||
//
|
||
}
|
||
else
|
||
{
|
||
// *****************************
|
||
// **** UNLOCK QUEUE ACCESS ****
|
||
// *****************************
|
||
LeaveCriticalSection(&MsgDisplayCriticalSection);
|
||
|
||
//
|
||
// Process the entry.
|
||
//
|
||
BigTime = pQueueEntry->BigTime;
|
||
SessionId = pQueueEntry->SessionId;
|
||
|
||
//
|
||
// Here we trash the pQueueEntry structure by pointing to the
|
||
// beginning and copying the message data starting at the
|
||
// first address. This is because MsgMakeNewFormattedMsg
|
||
// expects the message to begin at a address that can be
|
||
// released with LocalFree();
|
||
//
|
||
pHeadAnsi = (LPSTR) pQueueEntry;
|
||
strcpy(pHeadAnsi, pQueueEntry->Message);
|
||
|
||
//
|
||
// Convert the data from the OEM character set to the
|
||
// Unicode character set.
|
||
//
|
||
|
||
RtlInitAnsiString(&oemString, pHeadAnsi);
|
||
|
||
unicodeLength = oemString.Length * sizeof(WCHAR);
|
||
|
||
unicodeString.Buffer = (LPWSTR) LocalAlloc(LMEM_ZEROINIT,
|
||
unicodeLength + sizeof(WCHAR));
|
||
|
||
if (unicodeString.Buffer == NULL)
|
||
{
|
||
//
|
||
// Couldn't allocate for unicode buffer. Therefore we will
|
||
// display the message with the Ansi version of the
|
||
// message box API.
|
||
//
|
||
|
||
LocalFree(pHeadAnsi);
|
||
pHeadAnsi = NULL;
|
||
}
|
||
else
|
||
{
|
||
unicodeString.Length = unicodeLength;
|
||
unicodeString.MaximumLength = unicodeLength + sizeof(WCHAR);
|
||
|
||
ntStatus = RtlOemStringToUnicodeString(
|
||
&unicodeString, // Destination
|
||
&oemString, // Source
|
||
FALSE); // Don't allocate the destination.
|
||
|
||
LocalFree(pHeadAnsi);
|
||
pHeadAnsi = NULL;
|
||
|
||
if (!NT_SUCCESS(ntStatus))
|
||
{
|
||
MSG_LOG(ERROR,
|
||
"MsgDisplayThread:RtlOemStringToUnicodeString Failed rc=%X\n",
|
||
ntStatus);
|
||
|
||
LocalFree(unicodeString.Buffer);
|
||
unicodeString.Buffer = NULL;
|
||
}
|
||
else
|
||
{
|
||
pHead = unicodeString.Buffer;
|
||
pTime = wcsstr(pHead, GlobalTimePlaceHolderUnicode);
|
||
|
||
if (pTime != NULL)
|
||
{
|
||
pBody = pTime + wcslen(GlobalTimePlaceHolderUnicode);
|
||
}
|
||
else
|
||
{
|
||
pTime = pBody = pHead;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (pHead != NULL)
|
||
{
|
||
MsgMakeNewFormattedMsg(&pHead, &pTime, &pBody, BigTime);
|
||
|
||
//
|
||
// Display the data in the QueueEntry
|
||
//
|
||
|
||
MSG_LOG(TRACE, "Calling MessageBox\n",0);
|
||
|
||
if (g_IsTerminalServer)
|
||
{
|
||
displayStatus = DisplayMessage(pHead,
|
||
GlobalMessageBoxTitle,
|
||
SessionId);
|
||
|
||
//
|
||
// In Hydra case do not care about the error, since DisplayMessageW returns FALSE
|
||
// only if the user cannot be found on any Winstation. No use to try again in that case !
|
||
//
|
||
// So free up the data in the QueueEntry in any case
|
||
//
|
||
LocalFree(pHead);
|
||
pHead = NULL;
|
||
}
|
||
else
|
||
{
|
||
displayStatus = MessageBox(NULL,
|
||
pHead,
|
||
GlobalMessageBoxTitle,
|
||
MB_OK | MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION |
|
||
MB_SETFOREGROUND | MB_DEFAULT_DESKTOP_ONLY);
|
||
|
||
if (displayStatus == 0)
|
||
{
|
||
//
|
||
// MessageBoxW can fail in case the current desktop is not the application desktop
|
||
// So wait and try again later (Winlogon will "tickle" messenger at desktop switching)
|
||
//
|
||
MSG_LOG1(TRACE,"MessageBox (unicode) Call failed %d\n",GetLastError());
|
||
WaitForSingleObject( hGlobalDisplayEvent, INFINITE );
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Free up the data in the QueueEntry
|
||
//
|
||
LocalFree(pHead);
|
||
pHead = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
msgrState = GetMsgrState();
|
||
}
|
||
while(MsgToRead && (msgrState != STOPPING) && (msgrState != STOPPED));
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
VOID
|
||
MsgMakeNewFormattedMsg(
|
||
LPWSTR *ppHead,
|
||
LPWSTR *ppTime,
|
||
LPWSTR *ppBody,
|
||
SYSTEMTIME BigTime
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns a buffer containing an entire message that
|
||
consists of a single string of ansi (actually oem) characters.
|
||
Pointers to various areas (time and body) within this buffer are
|
||
also returned.
|
||
|
||
MEMORY MANAGEMENT NOTE:
|
||
*ppHead is expected to point to the top of the buffer. If the
|
||
message is reformatted, then this buffer will have been freed,
|
||
and a new buffer will have been allocated. It is expected that
|
||
the caller allocates the original buffer passed in, and that
|
||
the caller will free it when it is no longer needed.
|
||
|
||
Arguments:
|
||
|
||
ppHead - Pointer to location that contains the pointer to the message
|
||
buffer.
|
||
|
||
ppTime - Pointer to location that contains the pointer to the time portion
|
||
of the message buffer.
|
||
|
||
ppBody - Pointer to location that immediately follows the time string.
|
||
|
||
Return Value:
|
||
|
||
none. If this fails to allocate memory for the formatted message, then
|
||
the unformatted message should be displayed.
|
||
|
||
--*/
|
||
{
|
||
WCHAR TimeBuf[TIME_BUF_SIZE + 1];
|
||
DWORD BufSize;
|
||
LPWSTR pTemp;
|
||
DWORD numChars;
|
||
LPWSTR pOldHead;
|
||
|
||
|
||
//
|
||
// Create a properly formatted time string.
|
||
//
|
||
|
||
BufSize = GetDateFormat(LOCALE_SYSTEM_DEFAULT,
|
||
0, // flags
|
||
&BigTime, // date message was received
|
||
NULL, // use default format
|
||
TimeBuf, // buffer
|
||
sizeof(TimeBuf) / sizeof(WCHAR)); // size (in characters)
|
||
|
||
if (BufSize != 0)
|
||
{
|
||
//
|
||
// Return value includes the trailing NUL
|
||
//
|
||
TimeBuf[BufSize - 1] = ' ';
|
||
|
||
BufSize += GetTimeFormat(LOCALE_SYSTEM_DEFAULT,
|
||
0, // flags
|
||
&BigTime, // time message was received
|
||
NULL, // use default format
|
||
TimeBuf + BufSize, // buffer
|
||
sizeof(TimeBuf) / sizeof(WCHAR) - BufSize);
|
||
|
||
ASSERT(wcslen(TimeBuf) == (BufSize - 1));
|
||
}
|
||
|
||
if (BufSize == 0)
|
||
{
|
||
//
|
||
// Something went wrong
|
||
//
|
||
MSG_LOG1(ERROR,
|
||
"MsgMakeNewFormattedMsg: Date/time formatting failed %d\n",
|
||
GetLastError());
|
||
|
||
TimeBuf[0] = L'\0';
|
||
}
|
||
|
||
if (wcsncmp(TimeBuf, *ppTime, BufSize - 1) == 0)
|
||
{
|
||
//
|
||
// If the newly formatted time string is the same as the existing
|
||
// time string, there is nothing to do so we just return.
|
||
//
|
||
MSG_LOG0(TRACE,
|
||
"MsgMakeNewFormattedMsg: Time Format has not changed - no update.\n");
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Allocate a new message buffer
|
||
//
|
||
|
||
BufSize--;
|
||
BufSize += wcslen(*ppHead) + sizeof(WCHAR) - (DWORD) (*ppBody - *ppTime);
|
||
|
||
pTemp = LocalAlloc(LMEM_ZEROINIT, BufSize * sizeof(WCHAR));
|
||
|
||
if (pTemp == NULL)
|
||
{
|
||
MSG_LOG0(ERROR,"MsgMakeNewFormattedMsg: LocalAlloc failed\n");
|
||
return;
|
||
}
|
||
|
||
pOldHead = *ppHead;
|
||
|
||
//
|
||
// Copy the header of the message.
|
||
//
|
||
numChars = (DWORD) (*ppTime - *ppHead);
|
||
wcsncpy(pTemp, *ppHead, numChars);
|
||
*ppHead = pTemp;
|
||
|
||
//
|
||
// Copy the time string
|
||
//
|
||
*ppTime = *ppHead + numChars;
|
||
wcscpy(*ppTime, TimeBuf);
|
||
|
||
//
|
||
// Copy the Body of the message
|
||
//
|
||
pTemp = *ppBody;
|
||
*ppBody = *ppTime + wcslen(*ppTime);
|
||
wcscpy(*ppBody, pTemp);
|
||
|
||
LocalFree(pOldHead);
|
||
return;
|
||
}
|