DBALLOC.CPP
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:18k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /***
  2. *dballoc.cpp
  3. *
  4. *  Copyright (C) 1992 - 1997, Microsoft Corporation.  All Rights Reserved.
  5. *
  6. *Purpose:
  7. *  This file contains a debug implementation of the IMalloc interface.
  8. *
  9. *  This implementation is basically a simple wrapping of the C runtime,
  10. *  with additional work to detect memory leakage, and memory overwrite.
  11. *
  12. *  Leakage is detected by tracking each allocation in an address
  13. *  instance table, and then checking to see if the table is empty
  14. *  when the last reference to the allocator is released.
  15. *
  16. *  Memory overwrite is detected by placing a signature at the end
  17. *  of every allocated block, and checking to make sure the signature
  18. *  is unchanged when the block is freed.
  19. *
  20. *  This implementation also has additional param validation code, as
  21. *  well as additional check make sure that instances that are passed
  22. *  to Free() were actually allocated by the corresponding instance
  23. *  of the allocator.
  24. *
  25. *
  26. *  Creating an instance of this debug allocator that uses the default
  27. *  output interface would look like the following,
  28. *
  29. *
  30. *  BOOL init_application_instance()
  31. *  {
  32. *    HRESULT hresult;
  33. *    IMalloc FAR* pmalloc;
  34. *
  35. *    pmalloc = NULL;
  36. *
  37. *    if((hresult = OleStdCreateDbAlloc(0,&pmalloc))!=NOERROR)
  38. *      goto LReturn;
  39. *
  40. *    hresult = OleInitialize(pmalloc);
  41. *
  42. *    // release pmalloc to let OLE hold the only ref to the it. later
  43. *    // when OleUnitialize is called, memory leaks will be reported.
  44. *    if(pmalloc != NULL)
  45. *      pmalloc->Release();
  46. *
  47. *  LReturn:
  48. *
  49. *    return (hresult == NOERROR) ? TRUE : FALSE;
  50. *  }
  51. *
  52. *
  53. *  CONSIDER: could add an option to force error generation, something
  54. *   like DBALLOC_ERRORGEN
  55. *
  56. *  CONSIDER: add support for heap-checking. say for example,
  57. *   DBALLOC_HEAPCHECK would do a heapcheck every free? every 'n'
  58. *   calls to free? ...
  59. *
  60. *
  61. *Implementation Notes:
  62. *
  63. *  The method IMalloc::DidAlloc() is allowed to always return
  64. *  "Dont Know" (-1).  This method is called by Ole, and they take
  65. *  some appropriate action when they get this answer.
  66. *
  67. *****************************************************************************/
  68. // Note: this file is designed to be stand-alone; it includes a
  69. // carefully chosen, minimal set of headers.
  70. //
  71. // For conditional compilation we use the ole2 conventions,
  72. //    _MAC      = mac
  73. //    WIN32     = Win32 (NT really)
  74. //    <nothing> = defaults to Win16
  75. // REVIEW: the following needs to modified to handle _MAC
  76. #define STRICT
  77. #include <windows.h>
  78. #include <ole2.h>
  79. #if defined( __TURBOC__)
  80. #define __STDC__ (1)
  81. #endif
  82. #define WINDLL  1           // make far pointer version of stdargs.h
  83. #include <stdarg.h>
  84. #if defined( __TURBOC__)
  85. #undef __STDC__
  86. #endif
  87. #include <stdio.h>
  88. #include <malloc.h>
  89. #include <string.h>
  90. #include <limits.h>
  91. #include "dballoc.h"
  92. extern "C" DWORD g_dwObjectCount; // since we don't include ole2ui.h
  93. #define DIM(X) (sizeof(X)/sizeof((X)[0]))
  94. #define UNREACHED 0
  95. #if defined(WIN32)
  96. # define MEMCMP(PV1, PV2, CB)   memcmp((PV1), (PV2), (CB))
  97. # define MEMCPY(PV1, PV2, CB)   memcpy((PV1), (PV2), (CB))
  98. # define MEMSET(PV,  VAL, CB)   memset((PV),  (VAL), (CB))
  99. # define MALLOC(CB)     malloc(CB)
  100. # define REALLOC(PV, CB)    realloc((PV), (CB))
  101. # define FREE(PV)       free(PV)
  102. # define HEAPMIN()      _heapmin()
  103. #elif defined(_MAC)
  104. # define MEMCMP(PV1, PV2)   ERROR -- NYI
  105. # define MEMCPY(PV1, PV2, CB)   ERROR -- NYI
  106. # define MEMSET(PV,  VAL, CB)   ERROR -- NYI
  107. # define MALLOC(CB)     ERROR -- NYI
  108. # define REALLOC(PV, CB)    ERROR -- NYI
  109. # define FREE(PV)       ERROR -- NYI
  110. # define HEAPMIN()      ERROR -- NYI
  111. #else
  112. # define MEMCMP(PV1, PV2, CB)   _fmemcmp((PV1), (PV2), (CB))
  113. # define MEMCPY(PV1, PV2, CB)   _fmemcpy((PV1), (PV2), (CB))
  114. # define MEMSET(PV,  VAL, CB)   _fmemset((PV),  (VAL), (CB))
  115. # define MALLOC(CB)     _fmalloc(CB)
  116. # define REALLOC(PV, CB)    _frealloc(PV, CB)
  117. # define FREE(PV)       _ffree(PV)
  118. # define HEAPMIN()      _fheapmin()
  119. #endif
  120. /*************************************************************************
  121. ** DEBUG ASSERTION ROUTINES
  122. *************************************************************************/
  123. #if DBG
  124. #include "assert.h"
  125. #define FnAssert(lpstrExpr, lpstrMsg, lpstrFileName, iLine)     
  126.         (_assert(lpstrMsg ? lpstrMsg : lpstrExpr,               
  127.                  lpstrFileName,                                 
  128.                  iLine), NOERROR)
  129. #endif //DBG
  130. #if defined( __TURBOC__ )
  131. #define classmodel _huge
  132. #else
  133. #define classmodel FAR
  134. #endif
  135. class classmodel CStdDbOutput : public IDbOutput {
  136. public:
  137.    static IDbOutput FAR* Create();
  138.    // IUnknown methods
  139.    STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppv);
  140.    STDMETHOD_(ULONG, AddRef)(void);
  141.    STDMETHOD_(ULONG, Release)(void);
  142.    // IDbOutput methods
  143.    virtual void _cdecl  Printf (char FAR* szFmt, ...);
  144.    STDMETHOD_(void, Assertion)(
  145.      BOOL cond,
  146.      char FAR* szExpr,
  147.      char FAR* szFile,
  148.      UINT uLine,
  149.      char FAR* szMsg);
  150.    void FAR* operator new(size_t cb){
  151.      return MALLOC(cb);
  152.    }
  153.    void operator delete(void FAR* pv){
  154.      FREE(pv);
  155.    }
  156.    CStdDbOutput(){
  157.      g_dwObjectCount++ ;
  158.      m_refs = 0;
  159.    }
  160.    ~CStdDbOutput() { g_dwObjectCount-- ; }
  161. private:
  162.    ULONG m_refs;
  163.    char m_rgch[128]; // buffer for output formatting
  164. };
  165. //---------------------------------------------------------------------
  166. //                implementation of the debug allocator
  167. //---------------------------------------------------------------------
  168. class FAR CAddrNode
  169. {
  170. public:
  171.    void FAR*      m_pv;    // instance
  172.    ULONG      m_cb;    // size of allocation in BYTES
  173.    ULONG          m_nAlloc;    // the allocation pass count
  174.    CAddrNode FAR* m_next;
  175.    void FAR* operator new(size_t cb){
  176.      return MALLOC(cb);
  177.    }
  178.    void operator delete(void FAR* pv){
  179.      FREE(pv);
  180.    }
  181. };
  182. class classmodel CDbAlloc : public IMalloc
  183. {
  184. public:
  185.    static HRESULT Create(
  186.      ULONG options, IDbOutput FAR* pdbout, IMalloc FAR* FAR* ppmalloc);
  187.    // IUnknown methods
  188.    STDMETHOD(QueryInterface)(REFIID riid, void FAR* FAR* ppv);
  189.    STDMETHOD_(ULONG, AddRef)(void);
  190.    STDMETHOD_(ULONG, Release)(void);
  191.    // IMalloc methods
  192.    STDMETHOD_(void FAR*, Alloc)(ULONG cb);
  193.    STDMETHOD_(void FAR*, Realloc)(void FAR* pv, ULONG cb);
  194.    STDMETHOD_(void, Free)(void FAR* pv);
  195.    STDMETHOD_(ULONG, GetSize)(void FAR* pv);
  196.    STDMETHOD_(int, DidAlloc)(void FAR* pv);
  197.    STDMETHOD_(void, HeapMinimize)(void);
  198.    void FAR* operator new(size_t cb){
  199.      return MALLOC(cb);
  200.    }
  201.    void operator delete(void FAR* pv){
  202.      FREE(pv);
  203.    }
  204.    CDbAlloc(){
  205.      m_refs = 1;
  206.      m_pdbout = NULL;
  207.      m_cAllocCalls = 0;
  208.      m_nBreakAtNthAlloc = 0;
  209.      m_nBreakAtAllocSize = 0;
  210.      MEMSET(m_rganode, 0, sizeof(m_rganode));
  211.      g_dwObjectCount++ ;
  212.    }
  213.    ~CDbAlloc() {   g_dwObjectCount-- ; }
  214. private:
  215.    ULONG m_refs;
  216.    ULONG m_cAllocCalls;        // total count of allocation calls
  217.    ULONG m_nBreakAtNthAlloc;   // allocation number to break to debugger
  218.                         //  this value should be set typically in the
  219.                         //  debugger.
  220.    ULONG m_nBreakAtAllocSize;  // allocation size to break to debugger
  221.                         //  this value should be set typically in the
  222.                         //  debugger.
  223.    IDbOutput FAR* m_pdbout;        // output interface
  224.    CAddrNode FAR* m_rganode[64];   // address instance table
  225.    // instance table methods
  226.    BOOL IsEmpty(void);
  227.    void AddInst(void FAR* pv, ULONG nAlloc, ULONG cb);
  228.    void DelInst(void FAR* pv);
  229.    CAddrNode FAR* GetInst(void FAR* pv);
  230.    void DumpInst(CAddrNode FAR* pn);
  231.    void DumpInstTable(void);
  232.    inline UINT HashInst(void FAR* pv) const {
  233.      return ((UINT)((ULONG)pv >> 4)) % DIM(m_rganode);
  234.    }
  235.    // output method(s)
  236.    inline void Assertion(
  237.      BOOL cond,
  238.      char FAR* szExpr,
  239.      char FAR* szFile,
  240.      UINT uLine,
  241.      char FAR* szMsg)
  242.    {
  243.      m_pdbout->Assertion(cond, szExpr, szFile, uLine, szMsg);
  244.    }
  245.    #define ASSERT(X) Assertion(X, #X, __FILE__, __LINE__, NULL)
  246.    #define ASSERTSZ(X, SZ) Assertion(X, #X, __FILE__, __LINE__, SZ)
  247.    static const unsigned char m_rgchSig[4];
  248. };
  249. const unsigned char CDbAlloc::m_rgchSig[] = { 0xDE, 0xAD, 0xBE, 0xEF };
  250. /***
  251. *HRESULT OleStdCreateDbAlloc(ULONG reserved, IMalloc** ppmalloc)
  252. * Purpose:
  253. *  Create an instance of CDbAlloc -- a debug implementation
  254. *  of IMalloc.
  255. *
  256. * Parameters:
  257. *   ULONG reserved              - reserved for future use. must be 0.
  258. *   IMalloc FAR* FAR* ppmalloc  - (OUT) pointer to an IMalloc interface
  259. *                                   of new debug allocator object
  260. * Returns:
  261. *   HRESULT
  262. *       NOERROR         - if no error.
  263. *       E_OUTOFMEMORY   - allocation failed.
  264. *
  265. ***********************************************************************/
  266. STDAPI OleStdCreateDbAlloc(ULONG reserved,IMalloc FAR* FAR* ppmalloc)
  267. {
  268.    return CDbAlloc::Create(reserved, NULL, ppmalloc);
  269. }
  270. HRESULT
  271. CDbAlloc::Create(
  272.    ULONG options,
  273.    IDbOutput FAR* pdbout,
  274.    IMalloc FAR* FAR* ppmalloc)
  275. {
  276.    HRESULT hresult;
  277.    CDbAlloc FAR* pmalloc;
  278.    // default the instance of IDbOutput if the user didn't supply one
  279.    if(pdbout == NULL && ((pdbout = CStdDbOutput::Create()) == NULL)){
  280.      hresult = E_OUTOFMEMORY;
  281.      goto LError0;
  282.    }
  283.    pdbout->AddRef();
  284.    if((pmalloc = new FAR CDbAlloc()) == NULL){
  285.      hresult = E_OUTOFMEMORY;
  286.      goto LError1;
  287.    }
  288.    pmalloc->m_pdbout = pdbout;
  289.    *ppmalloc = pmalloc;
  290.    return NOERROR;
  291. LError1:;
  292.    pdbout->Release();
  293.    pmalloc->Release();
  294. LError0:;
  295.    return hresult;
  296. }
  297. STDMETHODIMP
  298. CDbAlloc::QueryInterface(REFIID riid, void FAR* FAR* ppv)
  299. {
  300.    if(riid == IID_IUnknown || riid == IID_IMalloc){
  301.      *ppv = this;
  302.      AddRef();
  303.      return NOERROR;
  304.    }
  305.    return E_NOINTERFACE;
  306. }
  307. STDMETHODIMP_(ULONG)
  308. CDbAlloc::AddRef()
  309. {
  310.    return ++m_refs;
  311. }
  312. STDMETHODIMP_(ULONG)
  313. CDbAlloc::Release()
  314. {
  315.    if(--m_refs == 0){
  316.      // check for memory leakage
  317.      if(IsEmpty()){
  318.         m_pdbout->Printf("No Memory Leaks.n");
  319.      }else{
  320.         m_pdbout->Printf("Memory Leak Detected,n");
  321.         DumpInstTable();
  322.      }
  323.      m_pdbout->Release();
  324.      delete this;
  325.      return 0;
  326.    }
  327.    return m_refs;
  328. }
  329. STDMETHODIMP_(void FAR*)
  330. CDbAlloc::Alloc(ULONG cb)
  331. {
  332.    size_t size;
  333.    void FAR* pv;
  334.    ++m_cAllocCalls;
  335.    if (m_nBreakAtNthAlloc && m_cAllocCalls == m_nBreakAtNthAlloc) {
  336.       ASSERTSZ(FALSE, "DBALLOC: NthAlloc Break target reachedrn");
  337.    } else if (m_nBreakAtAllocSize && cb == m_nBreakAtAllocSize) {
  338.       ASSERTSZ(FALSE, "DBALLOC: AllocSize Break target reachedrn");
  339.    }
  340.    // REVIEW: need to add support for huge allocations (on win16)
  341.    if((cb + sizeof(m_rgchSig)) > UINT_MAX)
  342.      return NULL;
  343.    size = (size_t)cb;
  344.    if((pv = MALLOC(size + sizeof(m_rgchSig))) == NULL)
  345.      return NULL;
  346.    // set allocated block to some non-zero value
  347.    MEMSET(pv, -1, size);
  348.    // put signature at end of allocated block
  349.    MEMCPY((char FAR*)pv + size, m_rgchSig, sizeof(m_rgchSig));
  350.    AddInst(pv, m_cAllocCalls, size);
  351.    return pv;
  352. }
  353. STDMETHODIMP_(void FAR*)
  354. CDbAlloc::Realloc(void FAR* pv, ULONG cb)
  355. {
  356.    size_t size;
  357.    // REVIEW: need to add support for huge realloc
  358.    if((cb + sizeof(m_rgchSig)) > UINT_MAX)
  359.      return NULL;
  360.    if(pv == NULL){
  361.      return Alloc(cb);
  362.    }
  363.    ++m_cAllocCalls;
  364.    ASSERT(GetInst(pv) != NULL);
  365.    DelInst(pv);
  366.    if(cb == 0){
  367.      Free(pv);
  368.      return NULL;
  369.    }
  370.    size = (size_t)cb;
  371.    if((pv = REALLOC(pv, size + sizeof(m_rgchSig))) == NULL)
  372.      return NULL;
  373.    // put signature at end of allocated block
  374.    MEMCPY((char FAR*)pv + size, m_rgchSig, sizeof(m_rgchSig));
  375.    AddInst(pv, m_cAllocCalls, size);
  376.    return pv;
  377. }
  378. STDMETHODIMP_(void)
  379. CDbAlloc::Free(void FAR* pv)
  380. {
  381.    CAddrNode FAR* pn;
  382. static char szSigMsg[] = "Signature Check Failed";
  383.    if (pv == NULL) return;
  384.    pn = GetInst(pv);
  385.    // check for attempt to free an instance we didnt allocate
  386.    if(pn == NULL){
  387.      ASSERTSZ(FALSE, "pointer freed by wrong allocator");
  388.      return;
  389.    }
  390.    // verify the signature
  391.    if(MEMCMP((char FAR*)pv + pn->m_cb, m_rgchSig, sizeof(m_rgchSig)) != 0){
  392.      m_pdbout->Printf(szSigMsg); m_pdbout->Printf("n");
  393.      DumpInst(GetInst(pv));
  394.      ASSERTSZ(FALSE, szSigMsg);
  395.    }
  396.    // stomp on the contents of the block
  397.    MEMSET(pv, 0xCC, (size_t)pn->m_cb + sizeof(m_rgchSig));
  398.    DelInst(pv);
  399.    FREE(pv);
  400. }
  401. STDMETHODIMP_(ULONG)
  402. CDbAlloc::GetSize(void FAR* pv)
  403. {
  404.    CAddrNode FAR* pn;
  405.    pn = GetInst(pv);
  406.    if (pn == NULL) {
  407.       return (ULONG)-1;
  408.    }
  409.    return pn->m_cb;
  410. }
  411. /***
  412. *PUBLIC HRESULT CDbAlloc::DidAlloc
  413. *Purpose:
  414. *  Answer if the given address belongs to a block allocated by
  415. *  this allocator.
  416. *
  417. *Entry:
  418. *  pv = the instance to lookup
  419. *
  420. *Exit:
  421. *  return value = int
  422. *    1 - did alloc
  423. *    0 - did *not* alloc
  424. *   -1 - dont know (according to the ole2 spec it is always legal
  425. *        for the allocator to answer "dont know")
  426. *
  427. ***********************************************************************/
  428. STDMETHODIMP_(int)
  429. CDbAlloc::DidAlloc(void FAR* pv)
  430. {
  431.    return -1; // answer "I dont know"
  432. }
  433. STDMETHODIMP_(void)
  434. CDbAlloc::HeapMinimize()
  435. {
  436. #ifdef WIN32
  437.    ASSERTSZ (FALSE,"In HeapMinimize () - heapmin not defined in 32bit version.");
  438. #else
  439.    HEAPMIN();
  440. #endif
  441. }
  442. //---------------------------------------------------------------------
  443. //                      instance table methods
  444. //---------------------------------------------------------------------
  445. /***
  446. *PRIVATE CDbAlloc::AddInst
  447. *Purpose:
  448. *  Add the given instance to the address instance table.
  449. *
  450. *Entry:
  451. *  pv = the instance to add
  452. *  nAlloc = the allocation passcount of this instance
  453. *
  454. *Exit:
  455. *  None
  456. *
  457. ***********************************************************************/
  458. void
  459. CDbAlloc::AddInst(void FAR* pv, ULONG nAlloc, ULONG cb)
  460. {
  461.    UINT hash;
  462.    CAddrNode FAR* pn;
  463.    ASSERT(pv != NULL);
  464.    pn = (CAddrNode FAR*)new FAR CAddrNode();
  465.    if (pn == NULL) {
  466.       ASSERT(pn != NULL);
  467.       return;
  468.    }
  469.    pn->m_pv = pv;
  470.    pn->m_cb = cb;
  471.    pn->m_nAlloc = nAlloc;
  472.    hash = HashInst(pv);
  473.    pn->m_next = m_rganode[hash];
  474.    m_rganode[hash] = pn;
  475. }
  476. /***
  477. *UNDONE
  478. *Purpose:
  479. *  Remove the given instance from the address instance table.
  480. *
  481. *Entry:
  482. *  pv = the instance to remove
  483. *
  484. *Exit:
  485. *  None
  486. *
  487. ***********************************************************************/
  488. void
  489. CDbAlloc::DelInst(void FAR* pv)
  490. {
  491.    CAddrNode FAR* FAR* ppn, FAR* pnDead;
  492.    for(ppn = &m_rganode[HashInst(pv)]; *ppn != NULL; ppn = &(*ppn)->m_next){
  493.      if((*ppn)->m_pv == pv){
  494.    pnDead = *ppn;
  495.    *ppn = (*ppn)->m_next;
  496.    delete pnDead;
  497.    // make sure it doesnt somehow appear twice
  498.    ASSERT(GetInst(pv) == NULL);
  499.    return;
  500.      }
  501.    }
  502.    // didnt find the instance
  503.    ASSERT(UNREACHED);
  504. }
  505. CAddrNode FAR*
  506. CDbAlloc::GetInst(void FAR* pv)
  507. {
  508.    CAddrNode FAR* pn;
  509.    for(pn = m_rganode[HashInst(pv)]; pn != NULL; pn = pn->m_next){
  510.      if(pn->m_pv == pv)
  511.       return pn;
  512.    }
  513.    return NULL;
  514. }
  515. void
  516. CDbAlloc::DumpInst(CAddrNode FAR* pn)
  517. {
  518.    if (pn == NULL)
  519.       return;
  520.    m_pdbout->Printf("[0x%lx]  nAlloc=%ld  size=%ldn",
  521.      pn->m_pv, pn->m_nAlloc, GetSize(pn->m_pv));
  522. }
  523. /***
  524. *PRIVATE BOOL IsEmpty
  525. *Purpose:
  526. *  Answer if the address instance table is empty.
  527. *
  528. *Entry:
  529. *  None
  530. *
  531. *Exit:
  532. *  return value = BOOL, TRUE if empty, FALSE otherwise
  533. *
  534. ***********************************************************************/
  535. BOOL
  536. CDbAlloc::IsEmpty()
  537. {
  538.    UINT u;
  539.    for(u = 0; u < DIM(m_rganode); ++u){
  540.      if(m_rganode[u] != NULL)
  541.    return FALSE;
  542.    }
  543.    return TRUE;
  544. }
  545. /***
  546. *PRIVATE CDbAlloc::Dump
  547. *Purpose:
  548. *  Print the current contents of the address instance table,
  549. *
  550. *Entry:
  551. *  None
  552. *
  553. *Exit:
  554. *  None
  555. *
  556. ***********************************************************************/
  557. void
  558. CDbAlloc::DumpInstTable()
  559. {
  560.    UINT u;
  561.    CAddrNode FAR* pn;
  562.    for(u = 0; u < DIM(m_rganode); ++u){
  563.      for(pn = m_rganode[u]; pn != NULL; pn = pn->m_next){
  564.         DumpInst(pn);
  565.      }
  566.    }
  567. }
  568. //---------------------------------------------------------------------
  569. //                implementation of CStdDbOutput
  570. //---------------------------------------------------------------------
  571. IDbOutput FAR*
  572. CStdDbOutput::Create()
  573. {
  574.    return (IDbOutput FAR*)new FAR CStdDbOutput();
  575. }
  576. STDMETHODIMP
  577. CStdDbOutput::QueryInterface(REFIID riid, void FAR* FAR* ppv)
  578. {
  579.    if(riid == IID_IUnknown){
  580.      *ppv = this;
  581.      AddRef();
  582.      return NOERROR;
  583.    }
  584.    return E_NOINTERFACE;
  585. }
  586. STDMETHODIMP_(ULONG)
  587. CStdDbOutput::AddRef()
  588. {
  589.    return ++m_refs;
  590. }
  591. STDMETHODIMP_(ULONG)
  592. CStdDbOutput::Release()
  593. {
  594.    if(--m_refs == 0){
  595.      delete this;
  596.      return 0;
  597.    }
  598.    return m_refs;
  599. }
  600. void _cdecl
  601. CStdDbOutput::Printf(char FAR* lpszFmt, ...)
  602. {
  603.    va_list args;
  604.    char szBuf[256];
  605. #if defined( OBSOLETE )
  606.    char *pn, FAR* pf;
  607. static char rgchFmtBuf[128];
  608. static char rgchOutputBuf[128];
  609.    // copy the 'far' format string to a near buffer so we can use
  610.    // a medium model vsprintf, which only supports near data pointers.
  611.    //
  612.    pn = rgchFmtBuf, pf=szFmt;
  613.    while(*pf != '')
  614.      *pn++ = *pf++;
  615.    *pn = '';
  616. #endif
  617.    va_start(args, lpszFmt);
  618. //    wvsprintf(rgchOutputBuf, rgchFmtBuf, args);
  619.    wvsprintf(szBuf, lpszFmt, args);
  620.    OutputDebugString(szBuf);
  621. }
  622. STDMETHODIMP_(void)
  623. CStdDbOutput::Assertion(
  624.    BOOL cond,
  625.    char FAR* szExpr,
  626.    char FAR* szFile,
  627.    UINT uLine,
  628.    char FAR* szMsg)
  629. {
  630.    if(cond)
  631.      return;
  632. #ifdef _DEBUG
  633.    // following is from compobj.dll (ole2)
  634.    FnAssert(szExpr, szMsg, szFile, uLine);
  635. #else
  636.    // REVIEW: should be able to do something better that this...
  637.    DebugBreak();
  638. #endif
  639. }