Writing a Message Handler

In addition to the Iunknown interface, which must be implemented by all COM objects, message handlers must implement the ITranslate interface in an in-process COM object. The following table shows the ITranslate methods.

Method
Description
ProcessMSG(LPMSGINFO) This method must accept a pointer to a MSGINFO structure from the router. It must return one of the following values:

•TRANS_S_STOP, if the handler completes successfully.

•TRANS_E_STOP, if the handler fails.

GetLastError(pszBuffer, dwSize) This method takes a pointer to a buffer into which to copy the last error message, and a DWORD giving the maximum length of the string that can be copied into the buffer. The length includes one byte for the null terminator. This method returns S_OK.
GetInfo(LPMODULE_INFO) This method takes a pointer to a MODULE_INFO structure, which it fills with handler data. It returns an HRESULT.

The following code example shows the ITranslate interface members of a message handler that writes a printable copy of the received message to a file. The file name to which it writes the message is the first string of the message.

#include <windows.h>
#include <string.h>
#include <TCHAR.H>

#include <wis.h>

#include "main.h"
#include "trans.h"
#include "resource.h"

STDMETHODIMP CTrans::GetLastError (LPWSTR pszError, DWORD dwSize) 
{
    if (pszError && dwSize)  {
        --dwSize;
        if (m_dwLastError >= TRANS_LAST_ERRCODE)  {
            m_dwLastError = TRANS_LAST_ERRCODE;
        }
    }    
    wcsncpy(pszError, TRANS_ERROR_STRINGS[m_dwLastError], dwSize);
    pszError[dwSize] = 0;
    return (S_OK);
}

STDMETHODIMP CTrans::GetInfo(LPMODULE_INFO lpInfo)
{   
    int i;
    // Read the resources into the buffers.
    if ((i = LoadStringW(g_hInst, IDS_TYPE, m_szType, MAX_INFOSTRING)))
    {
        m_szType[i] = 0;
        if ((i = LoadStringW(g_hInst, IDS_FRIENDLY_NAME,
                             m_szFriendlyName, MAX_INFOSTRING)))
        {
            m_szFriendlyName[i] = 0;
            if ((i = LoadStringW(g_hInst, IDS_DISCRIPTION,
                                 m_szDiscription, MAX_DISSTRING)))
            {
                m_szDescription[i] = 0;
                if ((i = LoadStringW(g_hInst, IDS_VERSION,
                                     m_szVersion, MAX_DISSTRING)))
                {
                    m_szVersion[i] = 0;
                    if ((i = LoadStringW(g_hInst, IDS_MANUFACTURER,
                                     m_szManufacturer, MAX_DISSTRING)))
                    {
                       m_szManufacturer[i] = 0;
                       *lpInfo = m_ti;
                       return(S_OK);
                    }
                }
            }
        }
    }
    return(E_FAIL);
}


STDMETHODIMP
CTrans::ProcessMSG ( LPMSGINFO pMSG )
{
    WCHAR   szDummy[] = L"\\testmsgs.txt";
    BOOL bOperationComplete = FALSE;
    HRESULT hr =  TRANS_E_CONTINUE;
    HANDLE htmp;

    // Validate the file name.
    if (pMSG->pszFileName == NULL || (lstrlenW(pMSG->pszFileName) == 0))
    {    // The method should have received a valid file name.
        m_dwLastError = TRANS_INVALID_IN_FILENAME;
        return(TRANS_E_STOP);
    }
    // Ensure that the input and output file handles are NULL.
    if (  m_hInput != INVALID_HANDLE_VALUE
       || m_hOutput != INVALID_HANDLE_VALUE)
    {
        m_dwLastError = TRANS_FILES_ALREADY_OPEN;
        return(hr);
    }

    // Open the input file.
    htmp = CreateFileW(pMSG->pszFileName, GENERIC_READ, FILE_SHARE_READ,
                        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                        NULL ); 
    if (htmp == INVALID_HANDLE_VALUE)
    {
        // The input file does not exist or cannot be opened.
        m_dwLastError = TRANS_CANT_OPEN_IN_FILE;
        return(hr);
    }
    else
    {
        m_hInput = htmp;
    }

    // Open the output file.
    htmp = CreateFileW(szDummy, GENERIC_WRITE, FILE_SHARE_READ,
                        NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
                        NULL ); 
    if (htmp == INVALID_HANDLE_VALUE)
    {
        // The output file cannot be opened or created.
        // Close the input file.
        CloseHandle(m_hInput);
        m_hInput = INVALID_HANDLE_VALUE;
        m_dwLastError = TRANS_CANT_OPEN_OUT_FILE;
        return(hr);
    }
    else
    {
        m_hOutput = htmp;
    }
    
    
    // Both the input and output files are ready.
    if (pMSG->pRL->Dir == DIR_RECEIVE)
    {
        // Perform Untranslation on the message.
        // TODO -- Replace this If statement with your call.
        if (Untranslate(pMSG))
        {
            bOperationComplete = TRUE;
        }
    }
    else // The other valid value is DIR_TRANSMIT.
    {
        // Perform translation on the message.
        // TODO -- Replace this If statement with your call.
        if (Translate(pMSG))
        {
            bOperationComplete = TRUE;
        }
    }

    // Close the files.
    CloseHandle(m_hInput);
    CloseHandle(m_hOutput);
    m_hInput = INVALID_HANDLE_VALUE;
    m_hOutput = INVALID_HANDLE_VALUE;
    if (bOperationComplete)  {
        hr = TRANS_S_STOP;
    }
    return (hr);
}


//
// Dump the MSGINFO structure and the data into a text file.
BOOL DumpToFile(HANDLE hOutFile, LPTSTR Str, ...)
{
    DWORD dwBytesToWrite, dwBytesWritten;
    va_list val;
    WCHAR byBuf[BUFF_SIZE];
    
    // Create the output string and send it to the
    // debugger's output window, if requested.
    va_start(val,Str);
    wvsprintf(byBuf, Str, val);
    va_end(val);
    dwBytesToWrite = lstrlen(byBuf) * sizeof(WCHAR);
    if (!WriteFile(hOutFile, byBuf, dwBytesToWrite,
                             &dwBytesWritten, NULL)
        || (dwBytesToWrite != dwBytesWritten))
    {
        return FALSE;
    }
    return TRUE;    
}    

BOOL
CTrans::Translate( LPMSGINFO pMSG )
{
    BYTE byBuf[BUFF_SIZE];
    DWORD dwBytesWritten;
    DWORD dwBytesRead;
    SYSTEMTIME stLocalTime;
    BOOL fRetValue = TRUE;

#if defined(DEBUG) && defined(DEBUG_BREAK)
        DebugBreak();
#endif        

    // Append the message to the end of the output file.
    // First, move the file pointer to the end of the output file.
    if (SetFilePointer(m_hOutput, 0, NULL, FILE_END) == 0xFFFFFFFF)  {
        m_dwLastError = TRANS_FILE_SEEK_ERROR;
        return FALSE;
    }
    
    // Write the message separator to the output file.
    GetLocalTime(&stLocalTime);
    fRetValue &= DumpToFile(m_hOutput,
                        L"==0--==--== Msg Log %d/%d/%d %d:%d.%d\r\n",
                        stLocalTime.wYear,
                        stLocalTime.wMonth,
                        stLocalTime.wDay, 
                        stLocalTime.wHour,
                        stLocalTime.wMinute,
                        stLocalTime.wSecond);
    
    // Write out MSGINFO structure members.
    fRetValue &= DumpToFile(m_hOutput, L"Size            =%d\r\n",
                                 pMSG->cbSize);
    fRetValue &= DumpToFile(m_hOutput, L"FileName        =%s\r\n",
                                 pMSG->pszFileName);
    fRetValue &= DumpToFile(m_hOutput, L"ErrFileName     =%s\r\n",
                                 pMSG->pszErrFileName);
    fRetValue &= DumpToFile(m_hOutput, L"ResponseFileName=%s\r\n",
                                 pMSG->pszResponseFileName);
    fRetValue &= DumpToFile(m_hOutput, L"OEMFileName     =%s\r\n",
                                 pMSG->pszOEMFileName);
    fRetValue &= DumpToFile(m_hOutput, L"FolderName      =%s\r\n",
                                 pMSG->pszFolderName);
    fRetValue &= DumpToFile(m_hOutput, L"Source          =%d\r\n",
                                 pMSG->Source);
    fRetValue &= DumpToFile(m_hOutput, L"Device          =%d\r\n",
                                 pMSG->Device);
    fRetValue &= DumpToFile(m_hOutput,
                            L"Systemtime      =%d/%d/%d %d:%d.%d\r\n",
                                  pMSG->DateTime.wYear, 
                                  pMSG->DateTime.wMonth, 
                                  pMSG->DateTime.wDay, 
                                  pMSG->DateTime.wHour, 
                                  pMSG->DateTime.wMinute, 
                                  pMSG->DateTime.wSecond);
    fRetValue &= DumpToFile(m_hOutput, L"MsgType         =%d\r\n",
                                 pMSG->pXtraMsgInfo->MsgType);
    fRetValue &= DumpToFile(m_hOutput, L"MsgPriority     =%d\r\n",
                                 pMSG->pXtraMsgInfo->MsgPriority);
    fRetValue &= DumpToFile(m_hOutput, L"MsgFlags        =0x%X\r\n",
                                 pMSG->pXtraMsgInfo->MsgFlags);
    fRetValue &= DumpToFile(m_hOutput, L"NumParts        =%d\r\n",
                                 pMSG->pXtraMsgInfo->NumParts);
    fRetValue &= DumpToFile(m_hOutput, L"ErrorFlags      =0x%X\r\n",
                                 pMSG->pXtraMsgInfo->wErrorFlags);
    fRetValue &= DumpToFile(m_hOutput, L"MsgSeqNum       =%d\r\n",
                                pMSG->pXtraMsgInfo->wMsgSequenceNumber);
    fRetValue &= DumpToFile(m_hOutput, L"==1-------==\r\n");
    
    while ( ReadFile(m_hInput, byBuf, BUFF_SIZE, &dwBytesRead, NULL)
            && dwBytesRead )
    {
        if (!WriteFile(m_hOutput, byBuf, dwBytesRead,
                         &dwBytesWritten, NULL)
             || (dwBytesRead != dwBytesWritten))
        {
            fRetValue = FALSE;
            break;
        }
    }
    fRetValue &= DumpToFile(m_hOutput, L"\r\n==*-------==\r\n");
    return fRetValue;
}

// Make the untranslate function the same as the translate function.
BOOL CTrans::Untranslate( LPMSGINFO pMSG )
{
    return Translate(pMSG);
}