You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1092 lines
27 KiB
1092 lines
27 KiB
/*
|
|
* U N K O B J . C
|
|
*
|
|
* This is a generic implementation of the IUnknown plus GetLastError)
|
|
* "IMAPIUnknown" part of objects that are derived from IUnknown with
|
|
* GetLastError.
|
|
*
|
|
* This also implements several useful utility functions based on
|
|
* IMAPIUnknown.
|
|
*
|
|
* To use this, you must implement your own init function.
|
|
*
|
|
* Used in:
|
|
* IPROP
|
|
* ITABLE
|
|
*
|
|
*/
|
|
|
|
|
|
#include "_apipch.h"
|
|
|
|
|
|
|
|
/*
|
|
* Per-instance global data for the UNKOBJ Class
|
|
*/
|
|
typedef struct
|
|
{
|
|
int cRef; // reference count for instance data
|
|
HLH hlh; // Single heap used by UNKOBJ_ScCOxxx
|
|
// allocators for all Unkobj's
|
|
CRITICAL_SECTION cs; // critical section for data access
|
|
} UNKOBJCLASSINST, FAR *LPUNKOBJCLASSINST;
|
|
|
|
#if defined (WIN32) && !defined (MAC)
|
|
CRITICAL_SECTION csUnkobjInit;
|
|
extern BOOL fGlobalCSValid;
|
|
#endif
|
|
|
|
// $MAC - Use Mac specific instance global handlers
|
|
|
|
#ifndef MAC
|
|
DefineInstList(UNKOBJ);
|
|
#undef PvGetInstanceGlobals
|
|
#define PvGetInstanceGlobals() PvGetInstanceGlobalsEx(UNKOBJ)
|
|
#undef ScSetInstanceGlobals
|
|
#define ScSetInstanceGlobals(pinst) ScSetInstanceGlobalsEx(pinst, UNKOBJ)
|
|
#else // MAC
|
|
#include <utilmac.h>
|
|
#define PvGetInstanceGlobals() PvGetInstanceGlobalsMac(kInstMAPIU)
|
|
#define PvGetInstanceGlobalsEx(_x) PvGetInstanceGlobalsMac(kInstMAPIU)
|
|
#define ScSetInstanceGlobals(a) ScSetInstanceGlobalsMac(a, kInstMAPIU)
|
|
#define ScSetInstanceGlobalsEx(_pinst, _x) ScSetInstanceGlobalsMac(_pinst, kInstMAPIU)
|
|
#endif // MAC
|
|
|
|
// #pragma SEGMENT(Common)
|
|
|
|
/*============================================================================
|
|
* UNKOBJ (IMAPIUnknown) Class
|
|
*
|
|
* Routines for handling per-process global data for the UNKOBJ Class
|
|
*
|
|
*/
|
|
|
|
/*============================================================================
|
|
*
|
|
* Initializes per-process global data for the UNKOBJ Class
|
|
*
|
|
*/
|
|
IF_WIN32(__inline) SCODE
|
|
ScGetUnkClassInst(LPUNKOBJCLASSINST FAR *ppinst)
|
|
{
|
|
SCODE sc = S_OK;
|
|
LPUNKOBJCLASSINST pinst = NULL;
|
|
|
|
#if defined (WIN32) && !defined (MAC)
|
|
if (fGlobalCSValid)
|
|
EnterCriticalSection(&csUnkobjInit);
|
|
#endif
|
|
|
|
pinst = (LPUNKOBJCLASSINST)PvGetInstanceGlobals();
|
|
|
|
if (pinst)
|
|
{
|
|
EnterCriticalSection(&pinst->cs);
|
|
pinst->cRef++;
|
|
LeaveCriticalSection(&pinst->cs);
|
|
goto ret;
|
|
}
|
|
|
|
|
|
if (!(pinst = (LPUNKOBJCLASSINST) GlobalAllocPtr(GPTR, sizeof(UNKOBJCLASSINST))))
|
|
{
|
|
sc = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto ret;
|
|
}
|
|
|
|
// Initialize the instance structure
|
|
|
|
// DebugTrace( TEXT("Creating UnkObj Inst: %8x"), pinst);
|
|
|
|
InitializeCriticalSection(&pinst->cs);
|
|
pinst->cRef = 1;
|
|
|
|
// (the heap will be created when the first allocation is done) ....
|
|
pinst->hlh = NULL;
|
|
|
|
#ifdef NEVER
|
|
// Create a Heap for the UNKOBJ Class that will be used by
|
|
// all unkobjs in this process.
|
|
//$ NOTE: The heap creation can be removed from here and the
|
|
//$ code to fault the heap in in UNKOBJ_ScCO(Re)Allocate()
|
|
//$ enabled instead - that would *require* users of CreateIProp,
|
|
//$ CreateITable etc not to do LH_SetHeapName().
|
|
|
|
pinst->hlh = LH_Open(0);
|
|
if (!pinst->hlh)
|
|
{
|
|
DebugTrace( TEXT("ScGetUnkClassInst():: Can't create Local Heap\n"));
|
|
sc = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto ret;
|
|
}
|
|
#endif
|
|
|
|
// ... and install the instance globals.
|
|
|
|
if (FAILED(sc = ScSetInstanceGlobals(pinst)))
|
|
{
|
|
DebugTrace( TEXT("ScGetUnkClassInst():: Failed to install instance globals\n"));
|
|
goto ret;
|
|
}
|
|
|
|
ret:
|
|
if (FAILED(sc))
|
|
{
|
|
if (pinst)
|
|
{
|
|
DeleteCriticalSection(&pinst->cs);
|
|
if (pinst->hlh)
|
|
LH_Close(pinst->hlh);
|
|
GlobalFreePtr(pinst);
|
|
pinst = NULL;
|
|
}
|
|
}
|
|
|
|
*ppinst = pinst;
|
|
|
|
#if defined (WIN32) && !defined (MAC)
|
|
if (fGlobalCSValid)
|
|
LeaveCriticalSection(&csUnkobjInit);
|
|
#endif
|
|
|
|
DebugTraceSc(ScInitInstance, sc);
|
|
return sc;
|
|
}
|
|
|
|
/*============================================================================
|
|
*
|
|
* Cleans up per-process global data for the UNKOBJ Class
|
|
*
|
|
*/
|
|
IF_WIN32(__inline) void
|
|
ReleaseUnkClassInst()
|
|
{
|
|
LPUNKOBJCLASSINST pinst = NULL;
|
|
|
|
#if defined (WIN32) && !defined (MAC)
|
|
if (fGlobalCSValid)
|
|
EnterCriticalSection(&csUnkobjInit);
|
|
#endif
|
|
|
|
pinst = (LPUNKOBJCLASSINST)PvGetInstanceGlobals();
|
|
|
|
if (!pinst)
|
|
goto out;
|
|
|
|
EnterCriticalSection(&pinst->cs);
|
|
if (--(pinst->cRef) > 0)
|
|
{
|
|
LeaveCriticalSection(&pinst->cs);
|
|
goto out;
|
|
}
|
|
|
|
// The last Unkobj for this process is going away, hence close
|
|
// our heap.
|
|
|
|
// DebugTrace( TEXT("Deleting UnkObj Inst: %8x"), pinst);
|
|
|
|
if (pinst->hlh)
|
|
{
|
|
// DebugTrace( TEXT("Destroying hlh (%8x) for Inst: %8x"), pinst->hlh, pinst);
|
|
LH_Close(pinst->hlh);
|
|
}
|
|
|
|
pinst->hlh = 0;
|
|
|
|
LeaveCriticalSection(&pinst->cs);
|
|
DeleteCriticalSection(&pinst->cs);
|
|
|
|
GlobalFreePtr(pinst);
|
|
(void)ScSetInstanceGlobals(NULL);
|
|
out:
|
|
|
|
#if defined (WIN32) && !defined (MAC)
|
|
if (fGlobalCSValid)
|
|
LeaveCriticalSection(&csUnkobjInit);
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*============================================================================
|
|
* UNKOBJ (IMAPIUnknown) Class
|
|
*
|
|
* Object methods.
|
|
*/
|
|
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::QueryInterface()
|
|
-
|
|
*/
|
|
|
|
STDMETHODIMP
|
|
UNKOBJ_QueryInterface (LPUNKOBJ lpunkobj,
|
|
REFIID riid,
|
|
LPVOID FAR * lppUnk)
|
|
{
|
|
LPIID FAR * lppiidSupported;
|
|
ULONG ulcIID;
|
|
SCODE sc;
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
/* Validate the object.
|
|
*/
|
|
if (BAD_STANDARD_OBJ( lpunkobj, UNKOBJ_, QueryInterface, lpvtbl))
|
|
{
|
|
DebugTrace( TEXT("UNKOBJ::QueryInterface() - Bad object passed\n") );
|
|
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
|
|
}
|
|
|
|
Validate_IUnknown_QueryInterface(lpunkobj, riid, lppUnk);
|
|
#endif
|
|
|
|
|
|
for ( lppiidSupported = lpunkobj->rgpiidList, ulcIID = lpunkobj->ulcIID
|
|
; ulcIID
|
|
; lppiidSupported++, ulcIID--)
|
|
{
|
|
if (IsEqualGUID(riid, *lppiidSupported))
|
|
{
|
|
/* We support the interface so break out of the search loop.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Return error if the requested interface was not in our list of
|
|
* supported interfaces.
|
|
*/
|
|
if (!ulcIID)
|
|
{
|
|
*lppUnk = NULL; // OLE requires zeroing [out] parameters
|
|
sc = E_NOINTERFACE;
|
|
goto error;
|
|
}
|
|
|
|
|
|
/* We found the requested interface so increment the reference count.
|
|
*/
|
|
UNKOBJ_EnterCriticalSection(lpunkobj);
|
|
lpunkobj->ulcRef++;
|
|
UNKOBJ_LeaveCriticalSection(lpunkobj);
|
|
|
|
*lppUnk = lpunkobj;
|
|
|
|
return hrSuccess;
|
|
|
|
error:
|
|
UNKOBJ_EnterCriticalSection(lpunkobj);
|
|
UNKOBJ_SetLastError(lpunkobj, E_NOINTERFACE, 0);
|
|
UNKOBJ_LeaveCriticalSection(lpunkobj);
|
|
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::AddRef()
|
|
-
|
|
*/
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
UNKOBJ_AddRef( LPUNKOBJ lpunkobj )
|
|
{
|
|
ULONG ulcRef;
|
|
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
if (BAD_STANDARD_OBJ( lpunkobj, UNKOBJ_, AddRef, lpvtbl))
|
|
{
|
|
DebugTrace( TEXT("UNKOBJ::AddRef() - Bad object passed\n") );
|
|
return 42;
|
|
}
|
|
#endif
|
|
|
|
UNKOBJ_EnterCriticalSection(lpunkobj);
|
|
ulcRef = ++lpunkobj->ulcRef;
|
|
UNKOBJ_LeaveCriticalSection(lpunkobj);
|
|
return ulcRef;
|
|
}
|
|
|
|
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::GetLastError()
|
|
-
|
|
* NOTE!
|
|
* An error in GetLastError will NOT cause the objects last error to be
|
|
* set again. This will allow the caller to retry the call.
|
|
*/
|
|
|
|
STDMETHODIMP
|
|
UNKOBJ_GetLastError( LPUNKOBJ lpunkobj,
|
|
HRESULT hrError,
|
|
ULONG ulFlags,
|
|
LPMAPIERROR FAR * lppMAPIError)
|
|
{
|
|
SCODE sc = S_OK;
|
|
HRESULT hrLastError;
|
|
IDS idsLastError;
|
|
LPTSTR lpszMessage = NULL;
|
|
LPMAPIERROR lpMAPIError = NULL;
|
|
|
|
|
|
#if !defined(NO_VALIDATION)
|
|
if (BAD_STANDARD_OBJ( lpunkobj, UNKOBJ_, GetLastError, lpvtbl))
|
|
{
|
|
DebugTrace( TEXT("UNKOBJ::GetLastError() - Bad object passed\n") );
|
|
return ResultFromScode(MAPI_E_INVALID_PARAMETER);
|
|
}
|
|
|
|
Validate_IMAPIProp_GetLastError(lpunkobj, hrError, ulFlags, lppMAPIError);
|
|
#endif
|
|
|
|
/* Verify flags.
|
|
*/
|
|
if (ulFlags & ~(MAPI_UNICODE))
|
|
{
|
|
return ResultFromScode(MAPI_E_UNKNOWN_FLAGS);
|
|
}
|
|
|
|
*lppMAPIError = NULL;
|
|
|
|
/* Get a snapshot of the last error.
|
|
*/
|
|
UNKOBJ_EnterCriticalSection(lpunkobj);
|
|
idsLastError = lpunkobj->idsLastError;
|
|
hrLastError = lpunkobj->hrLastError;
|
|
UNKOBJ_LeaveCriticalSection(lpunkobj);
|
|
|
|
/* If last error doesn't match parameter or there is no
|
|
* provider-context specific error string then just succeed.
|
|
*/
|
|
if ((hrError != hrLastError) || !idsLastError)
|
|
goto out;
|
|
|
|
/* Generate new lpMAPIError
|
|
*/
|
|
sc = UNKOBJ_ScAllocate(lpunkobj,
|
|
sizeof(MAPIERROR),
|
|
&lpMAPIError);
|
|
if (FAILED(sc))
|
|
{
|
|
DebugTrace( TEXT("UNKOBJ::GetLastError() - Unable to allocate memory\n"));
|
|
goto err;
|
|
}
|
|
|
|
FillMemory(lpMAPIError, sizeof(MAPIERROR), 0x00);
|
|
lpMAPIError->ulVersion = MAPI_ERROR_VERSION;
|
|
|
|
/* Load a copy of the error string.
|
|
*/
|
|
if ( FAILED(sc = UNKOBJ_ScSzFromIdsAllocMore(lpunkobj,
|
|
idsLastError,
|
|
ulFlags,
|
|
lpMAPIError,
|
|
cchLastError,
|
|
&lpszMessage)) )
|
|
{
|
|
DebugTrace( TEXT("UNKOBJ::GetLastError() - WARNING: Unable to load error string (SCODE = 0x%08lX). Returning hrSuccess.\n"), sc );
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
lpMAPIError->lpszError = lpszMessage;
|
|
|
|
*lppMAPIError = lpMAPIError;
|
|
|
|
out:
|
|
|
|
DebugTraceSc(UNKOBJ_GetLastError, sc);
|
|
return ResultFromScode(sc);
|
|
|
|
err:
|
|
UNKOBJ_Free( lpunkobj, lpMAPIError );
|
|
|
|
goto out;
|
|
}
|
|
|
|
|
|
/*
|
|
* UNKOBJ utility functions.
|
|
*/
|
|
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::ScAllocate()
|
|
-
|
|
* Utility function to allocate memory using MAPI linked memory.
|
|
*
|
|
*
|
|
* Parameters:
|
|
* lpunkobj in UNKOBJ with instance variable containing
|
|
* allocators.
|
|
* ulcb in Count of bytes to allocate.
|
|
* lplpv out MAPI-Allocated buffer.
|
|
*/
|
|
|
|
STDAPI_(SCODE)
|
|
UNKOBJ_ScAllocate( LPUNKOBJ lpunkobj,
|
|
ULONG ulcb,
|
|
LPVOID FAR * lplpv )
|
|
{
|
|
// parameter validation
|
|
|
|
AssertSz( lpunkobj && !FBadUnknown( (LPUNKNOWN)lpunkobj ), TEXT("lpunkobj fails address check") );
|
|
|
|
AssertSz( lplpv && !IsBadWritePtr( lplpv, sizeof( LPVOID ) ),
|
|
TEXT("lplpv fails address check") );
|
|
|
|
return lpunkobj->pinst->lpfAllocateBuffer(ulcb, lplpv);
|
|
}
|
|
|
|
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::ScAllocateMore()
|
|
-
|
|
* Utility function to allocate more memory using MAPI linked memory.
|
|
* If the link buffer is null, this function just does a MAPI allocate.
|
|
*
|
|
*
|
|
* Parameters:
|
|
* lpunkobj in UNKOBJ with instance variable containing
|
|
* allocators.
|
|
* ulcb in Count of bytes to allocate.
|
|
* lpv in Buffer to link to.
|
|
* lplpv out New buffer
|
|
*/
|
|
|
|
STDAPI_(SCODE)
|
|
UNKOBJ_ScAllocateMore( LPUNKOBJ lpunkobj,
|
|
ULONG ulcb,
|
|
LPVOID lpv,
|
|
LPVOID FAR * lplpv )
|
|
{
|
|
// validate parameters
|
|
|
|
AssertSz( lpunkobj && !FBadUnknown( (LPUNKNOWN)lpunkobj ), TEXT("lpunkobj fails address check") );
|
|
|
|
AssertSz( lplpv && !IsBadWritePtr( lplpv, sizeof( LPVOID ) ),
|
|
TEXT("lplpv fails address check") );
|
|
|
|
return lpv ?
|
|
lpunkobj->pinst->lpfAllocateMore(ulcb, lpv, lplpv) :
|
|
lpunkobj->pinst->lpfAllocateBuffer(ulcb, lplpv) ;
|
|
}
|
|
|
|
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::Free()
|
|
-
|
|
* Utility function to free MAPI linked memory. NULL buffers are ignored.
|
|
*
|
|
*
|
|
* Parameters:
|
|
* lpunkobj in UNKOBJ with instance variable containing
|
|
* allocators.
|
|
* lpv in Buffer to free.
|
|
*/
|
|
|
|
STDAPI_(VOID)
|
|
UNKOBJ_Free( LPUNKOBJ lpunkobj,
|
|
LPVOID lpv )
|
|
{
|
|
// parameter validation
|
|
|
|
AssertSz( lpunkobj && !FBadUnknown( (LPUNKNOWN)lpunkobj ), TEXT("lpunkobj fails address check") );
|
|
|
|
if (lpv)
|
|
{
|
|
if (lpv == lpunkobj)
|
|
lpunkobj->lpvtbl = NULL;
|
|
|
|
(void) lpunkobj->pinst->lpfFreeBuffer(lpv);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::FreeRows()
|
|
-
|
|
* Frees a row set of the form returned from IMAPITable::QueryRows
|
|
* (i.e. where the row set and each individual *prop value array*
|
|
* in that row set are individually allocated with MAPI linked memory.)
|
|
* NULL row sets are ignored.
|
|
*
|
|
*
|
|
* Parameters:
|
|
* lpunkobj in UNKOBJ with instance variable containing
|
|
* allocators.
|
|
* lprows in Row set to free.
|
|
*/
|
|
|
|
STDAPI_(VOID)
|
|
UNKOBJ_FreeRows( LPUNKOBJ lpunkobj,
|
|
LPSRowSet lprows )
|
|
{
|
|
LPSRow lprow;
|
|
|
|
// validate parameters
|
|
|
|
AssertSz( lpunkobj && !FBadUnknown( (LPUNKNOWN)lpunkobj ), TEXT("lpunkobj fails address check") );
|
|
|
|
AssertSz( !lprows || !FBadRowSet( lprows ), TEXT("lprows fails address check") );
|
|
|
|
if ( !lprows )
|
|
return;
|
|
|
|
/* Free each row in the set from last to first. UNKOBJ_Free
|
|
* handles NULL pointers.
|
|
*/
|
|
lprow = lprows->aRow + lprows->cRows;
|
|
while ( lprow-- > lprows->aRow )
|
|
UNKOBJ_Free((LPUNKOBJ) lpunkobj, lprow->lpProps);
|
|
|
|
UNKOBJ_Free(lpunkobj, lprows);
|
|
}
|
|
|
|
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::ScCOAllocate()
|
|
-
|
|
* Utility function to allocate memory using CO memory allocators.
|
|
*
|
|
*
|
|
* Parameters:
|
|
* lpunkobj in UNKOBJ with instance variable containing
|
|
* allocators.
|
|
* ulcb in Count of bytes to allocate.
|
|
* lplpv out Pointer to allocated buffer.
|
|
*/
|
|
|
|
STDAPI_(SCODE)
|
|
UNKOBJ_ScCOAllocate( LPUNKOBJ lpunkobj,
|
|
ULONG ulcb,
|
|
LPVOID FAR * lplpv )
|
|
{
|
|
HLH lhHeap;
|
|
|
|
// validate parameters
|
|
|
|
AssertSz( lpunkobj && !FBadUnknown( (LPUNKNOWN)lpunkobj ), TEXT("lpunkobj fails address check") );
|
|
|
|
AssertSz( lplpv && !IsBadWritePtr( lplpv, sizeof( LPVOID ) ),
|
|
TEXT("lplpv fails address check") );
|
|
|
|
/* If caller _really_ wants a 0 byte allocation, warn
|
|
* them and give them back a NULL pointer so that they
|
|
* can't dereference it, but should be able to free it.
|
|
*/
|
|
if ( ulcb == 0 )
|
|
{
|
|
DebugTrace( TEXT("LH_Alloc() - WARNING: Caller requested 0 bytes; returning NULL\n") );
|
|
*lplpv = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
lhHeap = lpunkobj->lhHeap;
|
|
|
|
// Enable following section when we fault in the Heap - requires changes
|
|
// throughout where CreateIProp/CreateITable calls are
|
|
// done followed by LH_SetHeapName(). The LH_SetHeapName
|
|
// calls have to be used since we may not have a heap
|
|
// at the time. Furthermore, there is only 1 heap, so
|
|
// they are unnecessary anyway.
|
|
|
|
#if 1
|
|
if (!lhHeap)
|
|
{
|
|
LPUNKOBJCLASSINST pinst;
|
|
|
|
// The UNKOBJ heap *probably* does not exist, make sure
|
|
// (to guard against a race) and create it if indeed so.
|
|
|
|
pinst = (LPUNKOBJCLASSINST)PvGetInstanceGlobals();
|
|
Assert(pinst);
|
|
EnterCriticalSection(&pinst->cs);
|
|
if (!pinst->hlh)
|
|
{
|
|
|
|
|
|
lhHeap = LH_Open(0);
|
|
if (!lhHeap)
|
|
{
|
|
DebugTrace( TEXT("UNKOBJ_ScCOAllocate() - Can't create Local Heap"));
|
|
LeaveCriticalSection(&pinst->cs);
|
|
return MAPI_E_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
// DebugTrace( TEXT("Faulting in heap (%8x). UnkObj Inst: %8x"), lhHeap, pinst);
|
|
|
|
// Install the heap handle in the global data
|
|
|
|
pinst->hlh = lhHeap;
|
|
}
|
|
else
|
|
{
|
|
// The rare event that the heap got created by some other
|
|
// object between our UNKOBJ_Init and this (first) allocation ...
|
|
// ... Take it and use it.
|
|
|
|
lhHeap = pinst->hlh;
|
|
}
|
|
|
|
LeaveCriticalSection(&pinst->cs);
|
|
|
|
// Install the heap handle in this object's internal data too
|
|
// so we don't have to access the instance data for subsequent
|
|
// allocations. This does not need to be crit-sectioned on lpunkobj
|
|
// since an overwrite will be with the same heap!.
|
|
|
|
lpunkobj->lhHeap = lhHeap;
|
|
|
|
LH_SetHeapName(lhHeap, TEXT("UNKOBJ Internal Heap"));
|
|
}
|
|
#endif
|
|
|
|
/* Allocate the buffer.
|
|
*/
|
|
*lplpv = LH_Alloc( lhHeap,(UINT) ulcb );
|
|
if (!*lplpv)
|
|
{
|
|
DebugTrace( TEXT("LH_Alloc() - OOM allocating *lppv\n") );
|
|
return MAPI_E_NOT_ENOUGH_MEMORY;
|
|
}
|
|
LH_SetName1(lhHeap, *lplpv, TEXT("UNKOBJ::ScCOAllocate %ld"), *lplpv);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::ScCOReallocate()
|
|
-
|
|
* Utility function to reallocate memory using CO memory allocators.
|
|
*
|
|
*
|
|
* Parameters:
|
|
* lpunkobj in UNKOBJ with instance variable containing
|
|
* allocators.
|
|
* ulcb in Count of bytes to allocate.
|
|
* lplpv in Pointer to buffer to reallocate.
|
|
* out Pointer to reallocated buffer.
|
|
*/
|
|
|
|
STDAPI_(SCODE)
|
|
UNKOBJ_ScCOReallocate( LPUNKOBJ lpunkobj,
|
|
ULONG ulcb,
|
|
LPVOID FAR * lplpv )
|
|
{
|
|
HLH lhHeap;
|
|
SCODE sc = S_OK;
|
|
LPVOID lpv = NULL;
|
|
|
|
// validate parameters
|
|
|
|
AssertSz( lpunkobj && !FBadUnknown( (LPUNKNOWN)lpunkobj ), TEXT("lpunkobj fails address check") );
|
|
|
|
AssertSz( lplpv && !IsBadWritePtr( lplpv, sizeof( LPVOID ) ),
|
|
TEXT("lplpv fails address check") );
|
|
|
|
lhHeap = lpunkobj->lhHeap;
|
|
|
|
// Enable following section when we fault in the Heap - requires changes
|
|
// throughout where CreateIProp/CreateITable calls are
|
|
// done followed by LH_SetHeapName(). The LH_SetHeapName
|
|
// calls have to be used since we may not have a heap
|
|
// at the time. Furthermore, there is only 1 heap, so
|
|
// they are unnecessary anyway.
|
|
|
|
#if 1
|
|
if (!lhHeap)
|
|
{
|
|
LPUNKOBJCLASSINST pinst;
|
|
|
|
// The UNKOBJ heap *probably* does not exist, make sure
|
|
// (to guard against a race) and create it if indeed so.
|
|
|
|
pinst = (LPUNKOBJCLASSINST)PvGetInstanceGlobals();
|
|
Assert(pinst);
|
|
EnterCriticalSection(&pinst->cs);
|
|
if (!pinst->hlh)
|
|
{
|
|
lhHeap = LH_Open(0);
|
|
if (!lhHeap)
|
|
{
|
|
DebugTrace( TEXT("UNKOBJ_ScCOReallocate() - Can't create Local Heap"));
|
|
LeaveCriticalSection(&pinst->cs);
|
|
return MAPI_E_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
// DebugTrace( TEXT("Faulting in heap (%8x). UnkObj Inst: %8x"), lhHeap, pinst);
|
|
|
|
// Install the heap handle in the global data
|
|
|
|
pinst->hlh = lhHeap;
|
|
}
|
|
else
|
|
{
|
|
// The rare event that the heap got created by some other
|
|
// object between our UNKOBJ_Init and this (first) allocation ...
|
|
// ... Take it and use it.
|
|
|
|
lhHeap = pinst->hlh;
|
|
}
|
|
|
|
LeaveCriticalSection(&pinst->cs);
|
|
|
|
// Install the heap handle in this object's internal data too
|
|
// so we don't have to access the instance data for subsequent
|
|
// allocations. This does not need to be crit-sectioned on lpunkobj
|
|
// since an overwrite will be with the same heap!.
|
|
|
|
lpunkobj->lhHeap = lhHeap;
|
|
|
|
LH_SetHeapName(lhHeap, TEXT("UNKOBJ Internal Heap"));
|
|
}
|
|
#endif
|
|
|
|
//$BUG Actually, the CO model is supposed do an Alloc() if
|
|
//$BUG the pointer passed in is NULL, but it currently
|
|
//$BUG doesn't seem to work that way....
|
|
if ( *lplpv == NULL )
|
|
{
|
|
lpv = LH_Alloc(lhHeap, (UINT) ulcb);
|
|
if (lpv)
|
|
{
|
|
*lplpv = lpv;
|
|
LH_SetName1(lhHeap, lpv, TEXT("UNKOBJ::ScCOReallocate %ld"), lpv);
|
|
}
|
|
else
|
|
sc = E_OUTOFMEMORY;
|
|
|
|
goto out;
|
|
}
|
|
|
|
/* Reallocate the buffer.
|
|
*/
|
|
lpv = LH_Realloc(lhHeap, *lplpv, (UINT) ulcb );
|
|
if (!lpv)
|
|
{
|
|
DebugTrace( TEXT("UNKOBJ::ScCOReallocate() - OOM reallocating *lplpv\n") );
|
|
sc = MAPI_E_NOT_ENOUGH_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
LH_SetName1(lhHeap, lpv, TEXT("UNKOBJ::ScCOReallocate %ld"), lpv);
|
|
*lplpv = lpv;
|
|
|
|
out:
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::COFree()
|
|
-
|
|
* Utility function to free memory using CO memory allocators.
|
|
*
|
|
*
|
|
* Parameters:
|
|
* lpunkobj in UNKOBJ with instance variable containing
|
|
* allocators.
|
|
* lpv in Buffer to free.
|
|
*/
|
|
|
|
STDAPI_(VOID)
|
|
UNKOBJ_COFree( LPUNKOBJ lpunkobj,
|
|
LPVOID lpv )
|
|
{
|
|
HLH lhHeap;
|
|
|
|
// validate parameters
|
|
|
|
AssertSz( lpunkobj && !FBadUnknown( (LPUNKNOWN)lpunkobj ), TEXT("lpunkobj fails address check") );
|
|
|
|
lhHeap = lpunkobj->lhHeap;
|
|
|
|
/* Free the buffer.
|
|
*/
|
|
//$??? Don't know if CO properly handles freeing NULL pointers,
|
|
//$??? but I assume it doesn't....
|
|
if ( lpv != NULL )
|
|
LH_Free( lhHeap, lpv );
|
|
}
|
|
|
|
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::ScSzFromIdsAlloc()
|
|
-
|
|
* Utility function load a resource string into a MAPI-allocated buffer.
|
|
*
|
|
*
|
|
* Parameters:
|
|
* lpunkobj in UNKOBJ with instance variable containing
|
|
* allocators.
|
|
* ids in ID of resource string.
|
|
* ulFlags in Flags (UNICODE or ANSI)
|
|
* cchBuf in Max length, in characters, to read.
|
|
* lpszBuf out Pointer to allocated buffer containing string.
|
|
*/
|
|
|
|
STDAPI_(SCODE)
|
|
UNKOBJ_ScSzFromIdsAlloc( LPUNKOBJ lpunkobj,
|
|
IDS ids,
|
|
ULONG ulFlags,
|
|
int cchBuf,
|
|
LPTSTR FAR * lppszBuf )
|
|
{
|
|
SCODE sc;
|
|
ULONG ulStringMax;
|
|
|
|
|
|
// validate parameters
|
|
|
|
AssertSz( lpunkobj && !FBadUnknown( (LPUNKNOWN)lpunkobj ), TEXT("lpunkobj fails address check") );
|
|
|
|
AssertSz( lppszBuf && !IsBadWritePtr( lppszBuf, sizeof( LPVOID ) ),
|
|
TEXT("lppszBuf fails address check") );
|
|
|
|
AssertSz( cchBuf > 0, TEXT("cchBuf can't be less than 1") );
|
|
|
|
ulStringMax = cchBuf
|
|
* ((ulFlags & MAPI_UNICODE) ? sizeof(TCHAR) : sizeof(CHAR));
|
|
if ( FAILED(sc = UNKOBJ_ScAllocate(lpunkobj,
|
|
ulStringMax,
|
|
(LPVOID FAR *) lppszBuf)) )
|
|
{
|
|
DebugTrace( TEXT("UNKOBJ::ScSzFromIdsAlloc() - Error allocating string (SCODE = 0x%08lX)\n"), sc );
|
|
return sc;
|
|
}
|
|
|
|
#if !defined(WIN16) && !defined(MAC)
|
|
if ( ulFlags & MAPI_UNICODE )
|
|
(void) LoadStringW(hinstMapiX,
|
|
(UINT) ids,
|
|
(LPWSTR) *lppszBuf,
|
|
cchBuf);
|
|
else
|
|
#endif
|
|
(void) LoadStringA(hinstMapiX,
|
|
(UINT) ids,
|
|
(LPSTR) *lppszBuf,
|
|
cchBuf);
|
|
return S_OK;
|
|
}
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::ScSzFromIdsAllocMore()
|
|
-
|
|
* Utility function load a resource string into a MAPI-allocated buffer.
|
|
*
|
|
*
|
|
* Parameters:
|
|
* lpunkobj in UNKOBJ with instance variable containing
|
|
* allocators.
|
|
* ids in ID of resource string.
|
|
* ulFlags in Flags (UNICODE or ANSI)
|
|
* lpvBase in Base allocation
|
|
* cchBuf in Max length, in characters, to read.
|
|
* lpszBuf out Pointer to allocated buffer containing string.
|
|
*/
|
|
|
|
STDAPI_(SCODE)
|
|
UNKOBJ_ScSzFromIdsAllocMore( LPUNKOBJ lpunkobj,
|
|
IDS ids,
|
|
ULONG ulFlags,
|
|
LPVOID lpvBase,
|
|
int cchBuf,
|
|
LPTSTR FAR * lppszBuf )
|
|
{
|
|
SCODE sc;
|
|
ULONG ulStringMax;
|
|
|
|
|
|
ulStringMax = cchBuf
|
|
* ((ulFlags & MAPI_UNICODE) ? sizeof(WCHAR) : sizeof(CHAR));
|
|
if ( FAILED(sc = UNKOBJ_ScAllocateMore(lpunkobj,
|
|
ulStringMax,
|
|
lpvBase,
|
|
(LPVOID FAR *) lppszBuf)) )
|
|
{
|
|
DebugTrace( TEXT("UNKOBJ::ScSzFromIdsAllocMore() - Error allocating string (SCODE = 0x%08lX)\n"), sc );
|
|
return sc;
|
|
}
|
|
|
|
#if !defined(WIN16) && !defined(MAC)
|
|
if ( ulFlags & MAPI_UNICODE )
|
|
(void) LoadStringW(hinstMapiX,
|
|
(UINT) ids,
|
|
(LPWSTR) *lppszBuf,
|
|
cchBuf);
|
|
else
|
|
#endif
|
|
(void) LoadStringA(hinstMapiX,
|
|
(UINT) ids,
|
|
(LPSTR) *lppszBuf,
|
|
cchBuf);
|
|
return S_OK;
|
|
}
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::Init()
|
|
-
|
|
* Initialize an object of the UNKOBJ Class
|
|
*
|
|
*
|
|
* Parameters:
|
|
* lpunkobj in UNKOBJ with instance variable containing
|
|
* allocators.
|
|
* lpvtblUnkobj in the object v-table
|
|
* ulcbVtbl in size of the object v-table
|
|
* rgpiidList in list of iid's supported by this object
|
|
* ulcIID in count of iid's in the list above
|
|
* punkinst in pointer to object's private (instance) data
|
|
*/
|
|
|
|
STDAPI_(SCODE)
|
|
UNKOBJ_Init( LPUNKOBJ lpunkobj,
|
|
UNKOBJ_Vtbl FAR * lpvtblUnkobj,
|
|
ULONG ulcbVtbl,
|
|
LPIID FAR * rgpiidList,
|
|
ULONG ulcIID,
|
|
PUNKINST punkinst )
|
|
{
|
|
SCODE sc = S_OK;
|
|
LPUNKOBJCLASSINST pinst = NULL;
|
|
|
|
// Create/Get per process global data for the Unkobj class
|
|
// This gets faulted in the first time UNKOBJ_Init
|
|
// is called by the process, i.e., when the process creates
|
|
// its first Unkobj. Subsequent calls just Addref
|
|
// the instance data. Note that this data is global to all
|
|
// UNKOBJ objects (per process) and differs from the per object
|
|
// data that *each* Unkobj keeps.
|
|
|
|
sc = ScGetUnkClassInst(&pinst);
|
|
if (FAILED(sc))
|
|
{
|
|
DebugTrace( TEXT("UNKOBJ_Init() - Can't create Instance Data"));
|
|
goto ret;
|
|
}
|
|
|
|
Assert(pinst);
|
|
|
|
lpunkobj->lpvtbl = lpvtblUnkobj;
|
|
lpunkobj->ulcbVtbl = ulcbVtbl;
|
|
lpunkobj->ulcRef = 1;
|
|
lpunkobj->rgpiidList= rgpiidList;
|
|
lpunkobj->ulcIID = ulcIID;
|
|
lpunkobj->pinst = punkinst;
|
|
lpunkobj->hrLastError = hrSuccess;
|
|
lpunkobj->idsLastError = 0;
|
|
|
|
InitializeCriticalSection(&lpunkobj->csid);
|
|
|
|
// If we have a heap for this instance, use it;
|
|
// otherwise, wait and it'll get faulted in the first time
|
|
// and allocation is made on this object.
|
|
|
|
lpunkobj->lhHeap = pinst->hlh ? pinst->hlh : NULL;
|
|
|
|
ret:
|
|
return sc;
|
|
}
|
|
|
|
/*============================================================================
|
|
- UNKOBJ::Deinit()
|
|
-
|
|
* Deinitialize an object of the UNKOBJ Class
|
|
*
|
|
*
|
|
* Parameters:
|
|
* lpunkobj in UNKOBJ with instance variable containing
|
|
* allocators.
|
|
*/
|
|
|
|
STDAPI_(VOID)
|
|
UNKOBJ_Deinit( LPUNKOBJ lpunkobj )
|
|
{
|
|
// Cleanup per process global data for the Unkobj class,
|
|
// if necessary. Last one out will end up shutting off
|
|
// the lights.
|
|
|
|
ReleaseUnkClassInst();
|
|
|
|
DeleteCriticalSection(&lpunkobj->csid);
|
|
}
|
|
|
|
#ifdef WIN16
|
|
// Win16 version of inline function. These are no longer inline function because
|
|
// Watcom WCC doesn't support inline. (WPP(C++ compiler) support inline.
|
|
VOID
|
|
UNKOBJ_EnterCriticalSection( LPUNKOBJ lpunkobj )
|
|
{
|
|
EnterCriticalSection(&lpunkobj->csid);
|
|
}
|
|
|
|
VOID
|
|
UNKOBJ_LeaveCriticalSection( LPUNKOBJ lpunkobj )
|
|
{
|
|
LeaveCriticalSection(&lpunkobj->csid);
|
|
}
|
|
|
|
HRESULT
|
|
UNKOBJ_HrSetLastResult( LPUNKOBJ lpunkobj,
|
|
HRESULT hResult,
|
|
IDS idsError )
|
|
{
|
|
UNKOBJ_EnterCriticalSection(lpunkobj);
|
|
lpunkobj->idsLastError = idsError;
|
|
lpunkobj->hrLastError = hResult;
|
|
UNKOBJ_LeaveCriticalSection(lpunkobj);
|
|
|
|
return hResult;
|
|
}
|
|
|
|
HRESULT
|
|
UNKOBJ_HrSetLastError( LPUNKOBJ lpunkobj,
|
|
SCODE sc,
|
|
IDS idsError )
|
|
{
|
|
UNKOBJ_EnterCriticalSection(lpunkobj);
|
|
lpunkobj->idsLastError = idsError;
|
|
lpunkobj->hrLastError = ResultFromScode(sc);
|
|
UNKOBJ_LeaveCriticalSection(lpunkobj);
|
|
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
VOID
|
|
UNKOBJ_SetLastError( LPUNKOBJ lpunkobj,
|
|
SCODE sc,
|
|
IDS idsError )
|
|
{
|
|
lpunkobj->idsLastError = idsError;
|
|
lpunkobj->hrLastError = ResultFromScode(sc);
|
|
}
|
|
|
|
VOID
|
|
UNKOBJ_SetLastErrorSc( LPUNKOBJ lpunkobj,
|
|
SCODE sc )
|
|
{
|
|
lpunkobj->hrLastError = ResultFromScode(sc);
|
|
}
|
|
|
|
VOID
|
|
UNKOBJ_SetLastErrorIds( LPUNKOBJ lpunkobj,
|
|
IDS ids )
|
|
{
|
|
lpunkobj->idsLastError = ids;
|
|
}
|
|
#endif // WIN16
|