Figure 1   TraceSrv.idl

/*----------------------------------------------------------------------
John Robbins
Microsoft Systems Journal, December 1997 - BugSlayer Column!
----------------------------------------------------------------------*/
import "oaidl.idl" ;
import "ocidl.idl" ;

// Forward declarations.
interface ITrace ;
dispinterface _TraceEvent ;

[
    uuid ( 76AA99F4-0228-11D1-B293-00AA004206E4 ) ,
    helpstring ( "ITrace Interface" ) ,
    // Mark this as a dual interface.
    dual ,
    // Mark this interface as compatible with OLE automation.
    oleautomation
]
interface ITrace : IDispatch
{

    [
        id ( 0x00000001 ) ,
        helpstring ( "Tracing output method" )
    ]
    // The simple method that everyone will call.
    HRESULT Trace ( [in] BSTR bstrText ) ;

    // For advanced users, or cool wrappers.
    HRESULT FullTrace ( [in] BSTR       bstrText ,
                        [in] long       dwPID     ) ;

    ////////////////////////////////////////////////////////////////////
    // ShowTimeStamps property.
    ////////////////////////////////////////////////////////////////////
    [
        propget     ,
        id ( 2 )    ,
        helpstring("If set, puts the time the trace message was
 received by the server on the front of the message.")
    ]
    HRESULT ShowTimeStamps ( [out , retval ] VARIANT_BOOL *pVal ) ;

    [
        propput     ,
        id ( 2 )    ,
        helpstring("If set, puts the time the trace message was
 received by the server on the front of the message.")
    ]
    HRESULT ShowTimeStamps ( [in] VARIANT_BOOL newVal ) ;

    ////////////////////////////////////////////////////////////////////
    // ShowTraceAsODS property
    ////////////////////////////////////////////////////////////////////
    [
        propget     ,
        id ( 3 )    ,
        helpstring("Sends the Trace string to kernel debuggers on
 the server system.")
    ]
    HRESULT ShowTraceAsODS ( [out , retval ] VARIANT_BOOL *pVal ) ;

    [
        propput     ,
        id ( 3 )    ,
        helpstring("Sends the Trace string to kernel debuggers on
 the server system.")
    ]
    HRESULT ShowTraceAsODS ( [in] VARIANT_BOOL newVal ) ;

    ////////////////////////////////////////////////////////////////////
    // ShowItemNumber property
    ////////////////////////////////////////////////////////////////////
    [
        propget     ,
        id ( 4 )    ,
        helpstring("If set, puts the trace message count on the
 front of the message.")
    ]
    HRESULT ShowItemNumber ( [out , retval ] VARIANT_BOOL *pVal ) ;

    [
        propput     ,
        id ( 4 )    ,
        helpstring("If set, puts the trace message count on the
 front of the message.")
    ]
    HRESULT ShowItemNumber ( [in] VARIANT_BOOL newVal ) ;

    ////////////////////////////////////////////////////////////////////
    // ShowPID property
    ////////////////////////////////////////////////////////////////////
    [
        propget     ,
        id ( 5 )    ,
        helpstring("If set, and the PID is passed in, will show
 the PID in the message.")
    ]
    HRESULT ShowPID ( [out , retval ] VARIANT_BOOL *pVal ) ;

    [
        propput     ,
        id ( 5 )    ,
        helpstring("If set, and the PID is passed in, will show
 the PID in the message.")
    ]
    HRESULT ShowPID ( [in] VARIANT_BOOL newVal ) ;

    ////////////////////////////////////////////////////////////////////
    // AddCRLF property
    ////////////////////////////////////////////////////////////////////
    [
        propget     ,
        id ( 6 )    ,
        helpstring("If set, adds a CR/LF to the message if needed.")
    ]
    HRESULT AddCRLF ( [out , retval ] VARIANT_BOOL *pVal ) ;

    [
        propput     ,
        id ( 6 )    ,
        helpstring("If set, adds a CR/LF to the message if needed.")
    ]
    HRESULT AddCRLF ( [in] VARIANT_BOOL newVal ) ;

} ;

[
    object  ,
    uuid ( D4C22CC0-0620-11D1-B29A-00aa004206e4 ) ,
    helpstring ( "ITraceEvent Interface" ),
    // Mark as hidden for user browsers.
    //hidden
]
interface ITraceEvent : IDispatch
{
    HRESULT TraceEvent ( BSTR bstrText ) ;

    HRESULT ChangeShowTimeStamps ( VARIANT_BOOL bNewVal ) ;

    HRESULT ChangeShowTraceAsODS ( VARIANT_BOOL bNewVal ) ;

    HRESULT ChangeShowItemNumber ( VARIANT_BOOL bNewVal ) ;

    HRESULT ChangeShowPID ( VARIANT_BOOL bNewVal ) ;

    HRESULT ChangeAddCRLF ( VARIANT_BOOL bNewVal ) ;

} ;

[
    uuid ( C1262E40-0186-11D1-B292-00AA004206E4 ) ,
    version ( 1.0 ) ,
    helpstring ( "TraceSrv 1.0 Type Library" )
]
library TRACESRVLib
{
    // Import the specified standard libraries into this library.
    importlib ( "stdole32.tlb" ) ;
    importlib ( "stdole2.tlb" ) ;

    [
        uuid ( 480F47B3-02F0-11D1-B296-00AA004206E4 ) ,
        helpstring ( "TraceSrv notification events for trace viewers" ),
        // Mark as hidden for user browsers.
        //hidden
    ]
    dispinterface DITraceEvent
    {
        properties  :
        methods     :

            [
                id ( 0x00000001 ) ,
                helpstring ( "Tells the viewer that an ODS string is here" ),
            ]
            void TraceEvent ( [in] BSTR bstrText ) ;

            [
                id ( 0x00000002 ) ,
                helpstring ( "Tells the viewer the state of the ShowTimeStamps
 property" ),
            ]
            void ChangeShowTimeStamps ( [in] VARIANT_BOOL bNewVal ) ;

            [
                id ( 0x00000003 ) ,
                helpstring ( "Tells the viewer the state of the ShowTraceAsODS
 property" ),
            ]
            void ChangeShowTraceAsODS ( [in] VARIANT_BOOL bNewVal ) ;

            [
                id ( 0x00000004 ) ,
                helpstring ( "Tells the viewer the state of the ShowItemNumber
 property" ),
            ]
            void ChangeShowItemNumber ( [in] VARIANT_BOOL bNewVal ) ;

            [
                id ( 0x00000005 ) ,
                helpstring ( "Tells the viewer the state of the ShowPID 
property" ),
            ]
            void ChangeShowPID ( [in] VARIANT_BOOL bNewVal ) ;

            [
                id ( 0x00000006 ) ,
                helpstring ( "Tells the viewer the state of the AddCRLF 
property" ),
            ]
            void ChangeAddCRLF ( [in] VARIANT_BOOL bNewVal ) ;

    } ;

    ////////////////////////////////////////////////////////////////////
    // Trace coclass supported interface attributes.
    ////////////////////////////////////////////////////////////////////
    [
        uuid ( 76AA99F5-0228-11D1-B293-00AA004206E4 ) ,
        helpstring ( "Trace Class" )
    ]
    coclass Trace
    {
        // The default input interface.
        [default] interface ITrace  ;
        // The event interface.
        interface ITraceEvent       ;
        // The dispinterface needed to trigger events in VB.
        [default , source] dispinterface DITraceEvent ;
    } ;
} ;


Figure 2   TraceSrv Interface and Handlers

Trace.h


 /*----------------------------------------------------------------------
 John Robbins Microsoft Systems Journal, December 1997 - Bugslayer Column!
 ----------------------------------------------------------------------*/
 #ifndef __TRACE_H_
 #define __TRACE_H_
 
 #include "resource.h"
 
 // The IDispatch proxy classes for the events.
 #include "CorrectProxyTraceSrv.h"
 
 // The fast BSTR class.
 #include "FastBSTR.h"
 
 // The following are the formatting sizes.  Since I arbitrarily chose to
 //  wrap the item numbers at 100,000, so the number format was known in
 //  advance, you can change those constants if you would like.  You will
 //  need to change the handling code in TraceFull as well.
 // Item Number - 6 (5 numbers + 1 space).
 const int k_SIZE_ITEMNUMBER = 6 ;
 // Time        - 9 (hh:mm:ss + 1 space)
 //  BUG BUG
 //  Double check that Chinese and Thai formats are really this size!
 //  I think they might have characters on the front of the time but,
 //  I'm not sure if the TIME_NOTIMEMARKER shuts them off.
 const int k_SIZE_TIME       = 9 ;
 // PID         - 7 ([XXXX] + 1 space)
 const int k_SIZE_PID        = 7 ;
 // CR/LF       - 2
 const int k_SIZE_CRLF       = 2 ;
 // TOTAL...
 const int k_SIZE_FULLFORMAT = k_SIZE_ITEMNUMBER + k_SIZE_TIME +
                               k_SIZE_PID + k_SIZE_CRLF        ;
 // The byte count is expressed in Unicode characters since everything
 //  sent to this class is in wide characters.
 const int k_SIZE_FULLFORMATBYTES = k_SIZE_FULLFORMAT * sizeof ( wchar_t )   ;
 
 // The minimum trace buffer size.
 const int k_MIN_TRACE_BUFF_SIZE = 2048 ;
 
 // The default values for the trace settings.  These are used in both
 //  the constructor and in the INI reading code.  Note that -1 is TRUE
 //  for VARIANT_BOOLS!
 const int k_DEFVAL_SHOWTIMESTAMPS  = 0     ;
 const int k_DEFVAL_SHOWTRACEASODS  = -1    ;
 const int k_DEFVAL_SHOWITEMNUMBER  = 0     ;
 const int k_DEFVAL_SHOWPID         = 0     ;
 const int k_DEFVAL_ADDCRLF         = 0     ;
 
 // The constant values for the INI where settings are stored.  These
 //  do not need to be internationalized.
 const LPCTSTR k_STR_INIFILE         = _T ( "TraceSrv.ini"   ) ;
 const LPCTSTR k_STR_SECTION         = _T ( "Settings"       ) ;
 const LPCTSTR k_STR_SHOWTIMESTAMPS  = _T ( "ShowTimeStamps" ) ;
 const LPCTSTR k_STR_SHOWTRACEASODS  = _T ( "ShowTraceAsODS" ) ;
 const LPCTSTR k_STR_SHOWITEMNUMBER  = _T ( "ShowItemNumber" ) ;
 const LPCTSTR k_STR_SHOWPID         = _T ( "ShowPID"        ) ;
 const LPCTSTR k_STR_ADDCRLF         = _T ( "AddCRLF"        ) ;
 
 // Since there is no WritePrivateProfileInt, these are the strings that
 //  represent the integer values for all the settings.
 const LPCTSTR k_STR_VBTRUE  = _T ( "-1" ) ;
 const LPCTSTR k_STR_VBFALSE = _T ( "0"  ) ;
 
 class ATL_NO_VTABLE CTrace :
         // The standard COM classes.
         public CComObjectRootEx<CComMultiThreadModel>   ,
         public CComCoClass<CTrace , &CLSID_Trace>       ,
         public ISupportErrorInfo                        ,
         public IDispatchImpl<ITrace , &IID_ITrace , &LIBID_TRACESRVLib> ,
 
         // This class holds ITraceEvent connection points.
         public IConnectionPointContainerImpl<CTrace>    ,
         // The actual event classes.
         public CProxyDITraceEvent<CTrace>               ,
         // Allows TraceSrv to be scriptable.
         public IObjectSafetyImpl<CTrace>
 {
 public  :
 
     CTrace( )
     {
         m_pUnkMarshaler     = NULL                    ;
         m_vbShowTimeStamps  = k_DEFVAL_SHOWTIMESTAMPS ;
         m_vbShowTraceAsODS  = k_DEFVAL_SHOWTRACEASODS ;
         m_vbShowItemNumber  = k_DEFVAL_SHOWITEMNUMBER ;
         m_vbShowPID         = k_DEFVAL_SHOWPID        ;
         m_vbAddCRLF         = k_DEFVAL_ADDCRLF        ;
         m_dwCurrCount       = 0                       ;
 
         LoadSettings ( ) ;
     }
 
     ~CTrace ( void )
     {
         SaveSettings ( ) ;
     }
 
 DECLARE_REGISTRY_RESOURCEID(IDR_TRACE)
 DECLARE_GET_CONTROLLING_UNKNOWN()
 
 // Wow, it only took me an entire day to figure out that this was the
 //  magic macro that forced a single instance!
 DECLARE_CLASSFACTORY_SINGLETON(CTrace)
 
 BEGIN_COM_MAP(CTrace)
     COM_INTERFACE_ENTRY(ITrace)
     COM_INTERFACE_ENTRY(IDispatch)
     COM_INTERFACE_ENTRY(ISupportErrorInfo)
     COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
     COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.p)
     COM_INTERFACE_ENTRY_IMPL(IObjectSafety)
 END_COM_MAP()
 
 BEGIN_CONNECTION_POINT_MAP(CTrace)
     CONNECTION_POINT_ENTRY(DIID_DITraceEvent)
 END_CONNECTION_POINT_MAP()
 
     // The memory debugging code.  See the Oct installment of Bugslayer!
     DECLARE_MEMDEBUG ( CTrace ) ;
 
     HRESULT FinalConstruct ( )
     {
         return ( CoCreateFreeThreadedMarshaler( GetControllingUnknown(),
                                                 &m_pUnkMarshaler.p   ));
     }
 
     void FinalRelease ( )
     {
         m_pUnkMarshaler.Release ( ) ;
     }
 
     CComPtr<IUnknown> m_pUnkMarshaler;
 
 // ISupportsErrorInfo
     STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);
 
 // ITrace
 public      :
     STDMETHOD(Trace) ( BSTR bstrText ) ;
     STDMETHOD(FullTrace) ( BSTR bstrText , long dwPID ) ;
 
     STDMETHOD(get_ShowTimeStamps) ( VARIANT_BOOL * pVal   ) ;
     STDMETHOD(put_ShowTimeStamps) ( VARIANT_BOOL   newVal ) ;
 
     STDMETHOD(get_ShowTraceAsODS) ( VARIANT_BOOL * pVal   ) ;
     STDMETHOD(put_ShowTraceAsODS) ( VARIANT_BOOL   newVal ) ;
 
     STDMETHOD(get_ShowItemNumber) ( VARIANT_BOOL * pVal   ) ;
     STDMETHOD(put_ShowItemNumber) ( VARIANT_BOOL   newVal ) ;
 
     STDMETHOD(get_ShowPID) ( VARIANT_BOOL * pVal   ) ;
     STDMETHOD(put_ShowPID) ( VARIANT_BOOL   newVal ) ;
 
     STDMETHOD(get_AddCRLF) ( VARIANT_BOOL * pVal   ) ;
     STDMETHOD(put_AddCRLF) ( VARIANT_BOOL   newVal ) ;
 
 private     :
 
     void LoadSettings ( void ) ;
     void SaveSettings ( void ) ;
 
     // The real property values.
     VARIANT_BOOL m_vbShowTimeStamps  ;
     VARIANT_BOOL m_vbShowTraceAsODS  ;
     VARIANT_BOOL m_vbShowItemNumber  ;
     VARIANT_BOOL m_vbShowPID         ;
     VARIANT_BOOL m_vbAddCRLF         ;
 
     // The running trace count.
     DWORD m_dwCurrCount     ;
 
     // The fast BSTR class that holds the string buffer built up.
     CFastBSTR   m_cOutput   ;
 
     // The function that does all the actual work!  If this function
     //  returns S_OK, then the m_cOutput variable has the string to
     //  use as the parameter to Fire_TraceEvent.
     HRESULT ProcessTrace ( BSTR bstrText , long dwPID ) ;
 } ;
 
 #endif  //__TRACE_H_

Trace.cpp


 /*----------------------------------------------------------------------
 John Robbins Microsoft Systems Journal, December 1997 - Bugslayer Column!
 ----------------------------------------------------------------------*/
 #include "stdafx.h"
 #include "TraceSrv.h"
 #include "Trace.h"
 
 IMPLEMENT_MEMDEBUG ( CTrace ) ;
 
 // The memory dumping and validating code for this class.
 #ifdef _DEBUG
 void CTrace :: ClassDumper ( const void * pData )
 {
     CTrace * pClass = (CTrace*)pData ;
     _RPT1 ( _CRT_WARN, " CTrace instance : 0x%08X\n" , pClass ) ;
 }
 void CTrace :: ClassValidator ( const void * , const void *  )
 {
     // The validator is not needed for this class.
 }
 #endif
 
 STDMETHODIMP CTrace :: InterfaceSupportsErrorInfo ( REFIID riid )
 {
     ASSERT ( this ) ;
 
     static const IID* arr[] =
     {
         &IID_ITrace,
     } ;
 
     for ( int i = 0 ; i < (sizeof(arr)/sizeof(arr[0])) ; i++ )
     {
         if ( InlineIsEqualGUID ( *arr[ i ] , riid ) )
         {
             return ( S_OK ) ;
         }
     }
     return ( S_FALSE ) ;
 }
 
 STDMETHODIMP CTrace :: Trace ( BSTR bstrText )
 {
     ASSERT ( this ) ;
     return ( FullTrace ( bstrText , 0 ) ) ;
 }
 
 STDMETHODIMP CTrace :: FullTrace ( BSTR bstrText, long dwPID     )
 {
     ASSERT ( this ) ;
     return ( ProcessTrace ( bstrText , dwPID ) ) ;
 }
 
 STDMETHODIMP CTrace :: get_ShowTimeStamps ( VARIANT_BOOL * pVal )
 {
     ASSERT ( this ) ;
     *pVal = m_vbShowTimeStamps ;
     return ( S_OK ) ;
 }
 
 STDMETHODIMP CTrace :: put_ShowTimeStamps ( VARIANT_BOOL newVal )
 {
     ASSERT ( this ) ;
     m_vbShowTimeStamps = newVal ;
     Fire_ChangeShowTimeStamps ( newVal ) ;
     return ( S_OK ) ;
 }
 
 STDMETHODIMP CTrace :: get_ShowTraceAsODS ( VARIANT_BOOL * pVal )
 {
     ASSERT ( this ) ;
     *pVal = (VARIANT_BOOL)m_vbShowTraceAsODS ;
     return ( S_OK ) ;
 }
 
 STDMETHODIMP CTrace :: put_ShowTraceAsODS ( VARIANT_BOOL newVal )
 {
     ASSERT ( this ) ;
     m_vbShowTraceAsODS = newVal ;
     Fire_ChangeShowTraceAsODS ( newVal ) ;
     return ( S_OK ) ;
 }
 
 STDMETHODIMP CTrace :: get_ShowItemNumber ( VARIANT_BOOL * pVal )
 {
     ASSERT ( this ) ;
     *pVal = (VARIANT_BOOL)m_vbShowItemNumber ;
     return ( S_OK ) ;
 }
 
 STDMETHODIMP CTrace :: put_ShowItemNumber ( VARIANT_BOOL newVal )
 {
     ASSERT ( this ) ;
     m_vbShowItemNumber = newVal ;
     Fire_ChangeShowItemNumber ( newVal ) ;
     return ( S_OK ) ;
 }
 
 STDMETHODIMP CTrace :: get_ShowPID ( VARIANT_BOOL * pVal )
 {
     ASSERT ( this ) ;
     *pVal = (VARIANT_BOOL)m_vbShowPID ;
     return ( S_OK ) ;
 }
 
 STDMETHODIMP CTrace :: put_ShowPID ( VARIANT_BOOL newVal )
 {
     ASSERT ( this ) ;
     m_vbShowPID = newVal ;
     Fire_ChangeShowPID ( newVal ) ;
     return ( S_OK ) ;
 }
 
 STDMETHODIMP CTrace :: get_AddCRLF ( VARIANT_BOOL * pVal )
 {
     ASSERT ( this ) ;
     *pVal = (VARIANT_BOOL)m_vbAddCRLF ;
     return ( S_OK ) ;
 }
 
 STDMETHODIMP CTrace :: put_AddCRLF ( VARIANT_BOOL newVal )
 {
     ASSERT ( this ) ;
     m_vbAddCRLF = newVal ;
     Fire_ChangeAddCRLF ( newVal ) ;
     return ( S_OK ) ;
 }
 
 HRESULT CTrace :: ProcessTrace ( BSTR bstrText , long dwPID )
 {
     // Always double check everything.  Trust nothing.
     ASSERT ( this ) ;
     ASSERT ( NULL != bstrText ) ;
 
     // The length of the input string.  This is read after the pointer
     //  is validated and for everyone else to use.
     int iInputLen = 0 ;
 
     if ( NULL == bstrText )
     {
         return ( Error ( IDS_NULLSTRINGPASSED, GUID_NULL, E_INVALIDARG ) ) ;
     }
 
     // We have some form of pointer in bstrText. Make sure what it holds
     //  is valid.
     ASSERT ( FALSE == IsBadReadPtr ( bstrText , sizeof ( BSTR ) ) ) ;
     ASSERT ( L'\0' != *bstrText ) ;
 
     if ( ( TRUE == IsBadReadPtr ( bstrText , sizeof ( BSTR ) ) ) ||
          ( L'\0' == *bstrText ) )
     {
         return ( Error ( IDS_INVALIDSTRING, GUID_NULL, E_INVALIDARG ) ) ;
     }
 
     // Get the input length now that the pointer is validated.
     iInputLen = lstrlenW ( bstrText ) ;
 
     // Could this string blow the buffer?
     UINT uiSize = ( iInputLen * sizeof ( OLECHAR) ) + k_SIZE_FULLFORMATBYTES  ;
 
     // If this is the first time through, then m_lBuffSize is zero so
     //  this serves as the initial allocation point.
     if ( uiSize >= m_cOutput.BufferSize ( ) )
     {
         // Delete the existing buffer and allocate a bigger one.
         m_cOutput.Free ( ) ;
 
         UINT uiAllocSize = uiSize * 2 ;
 
         // Make sure we get a minimum buffer size.
         if ( k_MIN_TRACE_BUFF_SIZE > uiAllocSize )
         {
             uiAllocSize = k_MIN_TRACE_BUFF_SIZE ;
         }
         OLECHAR * pTemp = m_cOutput.Allocate ( uiAllocSize ) ;
         ASSERT ( NULL != pTemp ) ;
         if ( NULL == pTemp )
         {
             return ( Error ( IDS_OUTOFMEMORY, GUID_NULL, E_OUTOFMEMORY ) ) ;
         }
     }
 
     // Now start the real work, everything checked out.
     // Increment the total.
     m_dwCurrCount++ ;
 
     // Is it time to wrap?
     if ( 100000 == m_dwCurrCount )
     {
         m_dwCurrCount = 0 ;
     }
 
     // Have the marker pointer start at the beginning of the output buffer.
     OLECHAR * pCurr = m_cOutput.GetDataBuffer ( ) ;
 
     if ( -1 == m_vbShowItemNumber )
     {
         pCurr += wsprintfW ( pCurr , L"%05d " , m_dwCurrCount ) ;
     }
 
     if ( -1 == m_vbShowTimeStamps )
     {
         // Show the timestamp based on the user's local (here at the
         //  server, not at the client!).  It is forced to just be the
         //  24 hour military time.
         int iLen = GetTimeFormatW ( LOCALE_USER_DEFAULT,
             LOCALE_NOUSEROVERRIDE | TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER,
             NULL, NULL, pCurr, 
             m_cOutput.BufferSize ( ) - k_SIZE_FULLFORMATBYTES   ) ;
         // BUG BUG. I need more error checking here.
         ASSERT ( 0 != iLen ) ;
 
         // Move the pointer along but remember to account for the
         //  null character counted item.
         pCurr += ( iLen - 1 ) ;
 
         // GetTimeFormat does not stick in the extra space on the end
         //  so do it now.
         *pCurr = L' ' ;
         pCurr++ ;
     }
 
     if ( -1 == m_vbShowPID )
     {
         pCurr += wsprintfW ( pCurr , L"[%04X] " , dwPID ) ;
     }
 
     // Now put the actual message in and copy the NULL terminator as well.
     lstrcpynW ( pCurr , bstrText , iInputLen + 1 ) ;
 
     // Move pCurr to point at the NULL terminator.
     pCurr += iInputLen ;
 
     // Finally check if the string needs CRLFs.
     if ( -1 == m_vbAddCRLF )
     {
         if ( ( L'\x0D' != *(pCurr - 2) ) ||
              ( L'\x0A' != *(pCurr - 1) )   )
         {
             *(pCurr) = L'\x0D' ;
             *(pCurr + 1) = L'\x0A' ;
             pCurr += 2 ;
             *pCurr = L'\0' ;
         }
     }
 
     // Is it supposed to get shot to a kernel debugger?
     if ( -1 == m_vbShowTraceAsODS )
     {
         OutputDebugStringW ( (BSTR)m_cOutput ) ;
     }
 
     // Calculate the actual string length for the string.
     m_cOutput.GetStringByteLength ( ) ;
 
     // Finally, let any viewers know.
     Fire_TraceEvent ( m_cOutput ) ;
 
     return ( S_OK ) ;
 }
 
 void CTrace :: LoadSettings ( void )
 {
     ASSERT ( this ) ;
 
     m_vbShowTimeStamps =
         (VARIANT_BOOL)GetPrivateProfileInt ( k_STR_SECTION, k_STR_SHOWTIMESTAMPS,
                                              k_DEFVAL_SHOWTIMESTAMPS,
                                              k_STR_INIFILE);
     m_vbShowTraceAsODS =
         (VARIANT_BOOL)GetPrivateProfileInt ( k_STR_SECTION, k_STR_SHOWTRACEASODS,
                                              k_DEFVAL_SHOWTRACEASODS,
                                              k_STR_INIFILE);
     m_vbShowItemNumber =
         (VARIANT_BOOL)GetPrivateProfileInt ( k_STR_SECTION, k_STR_SHOWITEMNUMBER,
                                              k_DEFVAL_SHOWITEMNUMBER,
                                              k_STR_INIFILE);
     m_vbShowPID =
         (VARIANT_BOOL)GetPrivateProfileInt ( k_STR_SECTION, k_STR_SHOWPID,
                                              k_DEFVAL_SHOWPID, k_STR_INIFILE);
     m_vbAddCRLF = 
         (VARIANT_BOOL)GetPrivateProfileInt ( k_STR_SECTION, k_STR_ADDCRLF,
                                              k_DEFVAL_ADDCRLF, k_STR_INIFILE);
 }
 
 void CTrace :: SaveSettings ( void )
 {
     ASSERT ( this ) ;
 
     WritePrivateProfileString ( k_STR_SECTION, k_STR_SHOWTIMESTAMPS,
         ( m_vbShowTimeStamps == -1 ) ? k_STR_VBTRUE : k_STR_VBFALSE,
         k_STR_INIFILE);
     WritePrivateProfileString ( k_STR_SECTION, k_STR_SHOWTRACEASODS,
         ( m_vbShowTraceAsODS == -1 ) ? k_STR_VBTRUE : k_STR_VBFALSE,
         k_STR_INIFILE);
     WritePrivateProfileString ( k_STR_SECTION, k_STR_SHOWITEMNUMBER,
         ( m_vbShowItemNumber == -1 ) ? k_STR_VBTRUE : k_STR_VBFALSE,
         k_STR_INIFILE);
     WritePrivateProfileString ( k_STR_SECTION, k_STR_SHOWPID,
         ( m_vbShowPID == -1 ) ? k_STR_VBTRUE : k_STR_VBFALSE,
         k_STR_INIFILE);
     WritePrivateProfileString ( k_STR_SECTION, k_STR_ADDCRLF,
         ( m_vbAddCRLF == -1 ) ? k_STR_VBTRUE : k_STR_VBFALSE,
         k_STR_INIFILE);
 }


Figure 3   FastBSTR.h


 /*----------------------------------------------------------------------
 John Robbins
 Microsoft Systems Journal, December 1997 - Bugslayer Column!
 ----------------------------------------------------------------------*/
 #ifndef _FASTBSTR_
 #define _FASTBSTR_
 
 class CFastBSTR
 {
 
 ////////////////////////////////////////////////////////////////////////
 // Public construction and destruction.
 ////////////////////////////////////////////////////////////////////////
 public      :
 
     // The memory debugging code.  See the Oct installment of
     //  Bugslayer!
     DECLARE_MEMDEBUG ( CFastBSTR ) ;
 
     CFastBSTR ( void )
     {
         m_szData        = NULL ;
         m_szWholeBuff   = NULL ;
         m_pdwStringSize = NULL ;
         m_uiBuffSize    = 0    ;
     }
 
     ~CFastBSTR ( void )
     {
         Free ( ) ;
     }
 
 ////////////////////////////////////////////////////////////////////////
 // Public allocation and deallocation functions.
 ////////////////////////////////////////////////////////////////////////
 public      :
 
     OLECHAR * Allocate ( UINT uiSize )
     {
         ASSERT ( 0 != uiSize ) ;
         if ( 0 == uiSize )
         {
             return ( NULL ) ;
         }
         // Free up any existing.
         if ( NULL != m_szWholeBuff )
         {
             delete [] m_szWholeBuff ;
         }
         m_szWholeBuff = new DWORD[ uiSize ] ;
         memset ( m_szWholeBuff , NULL , uiSize ) ;
 
         m_pdwStringSize = (LPDWORD)m_szWholeBuff ;
         *m_pdwStringSize = 0 ;
 
         m_szData = (OLECHAR*)(m_pdwStringSize + 1) ;
         m_uiBuffSize = uiSize ;
         return ( m_szData ) ;
     }
 
     void Free ( )
     {
         if ( NULL == m_szWholeBuff )
         {
             return ;
         }
         delete [] m_szWholeBuff ;
         m_szData        = NULL ;
         m_szWholeBuff   = NULL ;
         m_pdwStringSize = NULL ;
         m_uiBuffSize    = 0    ;
     }
 
 ////////////////////////////////////////////////////////////////////////
 // Public length functions.
 ////////////////////////////////////////////////////////////////////////
 public      :
 
     // Returns the length of the whole data buffer currently allocated.
     UINT BufferSize ( void ) const
     {
         return ( m_uiBuffSize ) ;
     }
 
     // Returns the byte length of the current string.  Call this before
     //  passing the data for this string to a function that needs a
     //  BSTR.  If the parameter is not zero, then that value will be
     //  used for the string size.  If this parameter is zero, then
     //  this returns the length of the string data.  (This means that
     //  the length will be up to the first NULL character.)
     UINT GetStringByteLength ( UINT uiLen = 0 )
     {
         ASSERT ( m_pdwStringSize != NULL ) ;
         if ( NULL == m_pdwStringSize )
         {
             return ( 0 ) ;
         }
         if ( 0 == uiLen )
         {
             uiLen = lstrlenW ( m_szData ) * sizeof ( OLECHAR ) ;
         }
         *m_pdwStringSize = uiLen ;
         return ( uiLen ) ;
     }
 
 ////////////////////////////////////////////////////////////////////////
 // Public data access members and operators.
 ////////////////////////////////////////////////////////////////////////
 public      :
 
     OLECHAR * GetDataBuffer ( ) const
     {
         return ( m_szData ) ;
     }
 
     operator BSTR ( ) const
     {
         return ( m_szData ) ;
     }
 
 
 ////////////////////////////////////////////////////////////////////////
 // Private data members.
 ////////////////////////////////////////////////////////////////////////
 private     :
     // The whole actual buffer.
     LPDWORD     m_szWholeBuff   ;
     // The string size portion of the buffer.
     LPDWORD     m_pdwStringSize ;
     // The data portion of the buffer.
     OLECHAR *   m_szData        ;
     // The buffer size.
     DWORD       m_uiBuffSize    ;
 
 } ;
 
 
 #endif      // _FASTBSTR_


Figure 4   DCOM Properties Settings

In the Default Properties Page in DCOMCNFG
Enable Distributed COM on this Computer
Checked
Default Authentication Level
Connect
Default Impersonation Level
Identify
In the Default Security Page in DCOMCNFG
Default Access Permissions
Everyone
Allow Access
INTERACTIVE
Allow Access
NETWORK
Allow Access
SYSTEM
Allow Access
Default Launch Permissions
Administrators
Allow Launch
Everyone
Allow Launch
INTERACTIVE
Allow Launch
NETWORK
Allow Launch
SYSTEM
Allow Launch
Default Configuration Permissions
Administrators
Full Control
CREATOR OWNER
Full Control
Everyone
Read
INTERACTIVE
Special Access
SYSTEM
Full Control