|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: ido.cpp
//
// Contents: Special data object implementation to optimize drag/drop
//
// Classes: CDragDataObject
//
// Functions: CreateDragDataObject
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 ricksa Created
//
// Notes:
//
//--------------------------------------------------------------------------
#include <le2int.h>
#include <utils.h>
#include <dragopt.h>
#include <clipdata.h>
// Format for name of shared memory
OLECHAR szSharedMemoryTemplate[] = OLESTR("DragDrop%lx");
// Maximum size of string for name of shared memory. This is the size of the
// template plus the maximum number of hex digits in a long.
const int DRAG_SM_NAME_MAX = sizeof(szSharedMemoryTemplate) + sizeof(DWORD) * 2;
// Useful function for getting an enumerator
HRESULT wGetEnumFormatEtc( IDataObject *pDataObj, DWORD dwDirection, IEnumFORMATETC **ppIEnum);
//+-------------------------------------------------------------------------
//
// Class: CDragDataObject
//
// Purpose: Server side data object for drag that creates enumerator
// for shared formats.
//
// Interface: QueryInterface
// AddRef
// Release
// GetData
// GetDataHere
// QueryGetData
// GetCanonicalFormatEtc
// SetData
// EnumFormatEtc
// DAdvise
// DUnadvise
// EnumDAdvise
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Notes: This class only exists for return the enumerator. For
// all other operations it will simply pass the operation on
// to the real data object.
//
//--------------------------------------------------------------------------
class CDragDataObject : public IDataObject, public CPrivAlloc { public: CDragDataObject( void *pvMarshaledDataObject, DWORD dwSmId);
~CDragDataObject(void);
//
// IUnknown
//
STDMETHODIMP QueryInterface( REFIID riid, void **ppvObject);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
//
// IDataObject
//
STDMETHODIMP GetData( FORMATETC *pformatetcIn, STGMEDIUM *pmedium);
STDMETHODIMP GetDataHere( FORMATETC *pformatetc, STGMEDIUM *pmedium);
STDMETHODIMP QueryGetData( FORMATETC *pformatetc);
STDMETHODIMP GetCanonicalFormatEtc( FORMATETC *pformatectIn, FORMATETC *pformatetcOut);
STDMETHODIMP SetData( FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease);
STDMETHODIMP EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc);
STDMETHODIMP DAdvise( FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection);
STDMETHODIMP DUnadvise(DWORD dwConnection);
STDMETHODIMP EnumDAdvise(IEnumSTATDATA **ppenumAdvise);
private:
IDataObject * GetRealDataObjPtr(void); HRESULT GetFormatEtcDataArray(void);
ULONG _cRefs;
void * _pvMarshaledDataObject;
IDataObject * _pIDataObject; FORMATETCDATAARRAY *m_pFormatEtcDataArray;
DWORD _dwSmId; };
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::CDragDataObject
//
// Synopsis: Create server side object for drag
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
//--------------------------------------------------------------------------
CDragDataObject::CDragDataObject(void *pvMarshaledDataObject, DWORD dwSmId) : _cRefs(1), _pvMarshaledDataObject(pvMarshaledDataObject), _dwSmId(dwSmId), _pIDataObject(NULL), m_pFormatEtcDataArray(NULL) { // Header does all the work
}
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::~CDragDataObject
//
// Synopsis: Free any resources connected with this object
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note:
//
//--------------------------------------------------------------------------
CDragDataObject::~CDragDataObject(void) { // Release held pointer since we no longer need it.
if (_pIDataObject) { _pIDataObject->Release(); }
// this memory was allocated in RemPrivDragDrop, getif.cxx
if( _pvMarshaledDataObject ) { PrivMemFree(_pvMarshaledDataObject); }
if (m_pFormatEtcDataArray) {
if (0 == --m_pFormatEtcDataArray->_cRefs) { PrivMemFree(m_pFormatEtcDataArray); m_pFormatEtcDataArray = NULL; }
}
}
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::GetRealDataObjPtr
//
// Synopsis: Get the pointer to the real data object from the client
//
// Returns: NULL - could not unmarshal drag source's data object
// ~NULL - pointer to drag source's data object
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
//--------------------------------------------------------------------------
IDataObject *CDragDataObject::GetRealDataObjPtr(void) { if (_pIDataObject == NULL) { _pIDataObject = UnmarshalDragDataObject(_pvMarshaledDataObject);
LEERROR(!_pIDataObject, "Unable to unmarshal dnd data object"); }
return _pIDataObject; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::GetFormatEtcDataArray (private)
//
// Synopsis: if don't already have shared formats for enumeraor.
//
// Effects:
//
// Arguments: void
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 13-Jun-94 Ricksa author
//
// Notes:
//
//--------------------------------------------------------------------------
HRESULT CDragDataObject::GetFormatEtcDataArray(void) { OLECHAR szSharedMemoryName[DRAG_SM_NAME_MAX]; HANDLE hSharedMemory; FORMATETCDATAARRAY *pFormatEtcDataArray = NULL;
if (m_pFormatEtcDataArray) return NOERROR;
wsprintf(szSharedMemoryName, szSharedMemoryTemplate, _dwSmId);
// Create the shared memory object
hSharedMemory = OpenFileMapping(FILE_MAP_READ, FALSE, szSharedMemoryName); if (hSharedMemory != NULL) { // Map in the shared memory
pFormatEtcDataArray = (FORMATETCDATAARRAY *) MapViewOfFile(hSharedMemory, FILE_MAP_READ, 0, 0, 0);
if (NULL == pFormatEtcDataArray) { CloseHandle(hSharedMemory); hSharedMemory = NULL; } }
if (pFormatEtcDataArray) {
size_t stSize; GetCopiedFormatEtcDataArraySize (pFormatEtcDataArray, &stSize);
m_pFormatEtcDataArray = (FORMATETCDATAARRAY *) PrivMemAlloc(stSize); if (m_pFormatEtcDataArray) { CopyFormatEtcDataArray (m_pFormatEtcDataArray, pFormatEtcDataArray, stSize, FALSE); Assert(1 == m_pFormatEtcDataArray->_cRefs); }
UnmapViewOfFile(pFormatEtcDataArray); CloseHandle(hSharedMemory); }
return m_pFormatEtcDataArray ? NOERROR : E_OUTOFMEMORY; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::QueryInterface
//
// Synopsis: Get new interface
//
// Arguments: [riid] - interface id of requested interface
// [ppvObject] - where to put the new interface pointer
//
// Returns: NOERROR - interface was instantiated
// E_FAIL - could not unmarshal source's data object
// other - some error occurred.
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note:
//
//--------------------------------------------------------------------------
STDMETHODIMP CDragDataObject::QueryInterface( REFIID riid, void **ppvObject) { if(IsEqualIID(riid, IID_IDataObject) || IsEqualIID(riid, IID_IUnknown)) { *ppvObject = this; AddRef(); return NOERROR; }
return (GetRealDataObjPtr() != NULL) ? _pIDataObject->QueryInterface(riid, ppvObject) : E_FAIL; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::AddRef
//
// Synopsis: Create server side object for drag
//
// Returns: Current reference count
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CDragDataObject::AddRef(void) { DDDebugOut((DEB_ITRACE, "ADDREF == %d\n", _cRefs + 1)); return ++_cRefs; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::Release
//
// Synopsis: Decrement reference count to the object
//
// Returns: Current reference count to the object
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CDragDataObject::Release(void) { ULONG cRefs = --_cRefs;
DDDebugOut((DEB_ITRACE, "RELEASE == %d\n", cRefs));
if (cRefs == 0) { delete this; }
return cRefs; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::GetData
//
// Synopsis: Create server side object for drag
//
// Arguments: [pformatetcIn] - format for requested data
// [pmedium] - storage medium
//
// Returns: NOERROR - operation was successful
// Other - operation failed
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note: This just forwards the operation to the source data object
// if possible.
//
//--------------------------------------------------------------------------
STDMETHODIMP CDragDataObject::GetData( FORMATETC *pformatetcIn, STGMEDIUM *pmedium) { return (GetRealDataObjPtr() != NULL) ? _pIDataObject->GetData(pformatetcIn, pmedium) : E_FAIL; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::GetDataHere
//
// Synopsis: Create server side object for drag
//
// Arguments: [pformatetc] - format for requested data
// [pmedium] - storage medium
//
// Returns: NOERROR - operation was successful
// Other - operation failed
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note: This just forwards the operation to the source data object
// if possible.
//
//--------------------------------------------------------------------------
STDMETHODIMP CDragDataObject::GetDataHere( FORMATETC *pformatetc, STGMEDIUM *pmedium) { return (GetRealDataObjPtr() != NULL) ? _pIDataObject->GetDataHere(pformatetc, pmedium) : E_FAIL; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::QueryGetData
//
// Synopsis: Create server side object for drag
//
// Arguments: [pformatetc] - format to verify
//
// Returns: NOERROR - operation was successful
// Other - operation failed
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note: This just forwards the operation to the source data object
// if possible.
//
//--------------------------------------------------------------------------
STDMETHODIMP CDragDataObject::QueryGetData(FORMATETC *pformatetc) { return (GetRealDataObjPtr() != NULL) ? _pIDataObject->QueryGetData(pformatetc) : E_FAIL; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::GetCanonicalFormatEtc
//
// Synopsis: Create server side object for drag
//
// Arguments: [pformatetcIn] - input format
// [pformatetcOut] - output format
//
// Returns: NOERROR - operation was successful
// Other - operation failed
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note: This just forwards the operation to the source data object
// if possible.
//
//--------------------------------------------------------------------------
STDMETHODIMP CDragDataObject::GetCanonicalFormatEtc( FORMATETC *pformatetcIn, FORMATETC *pformatetcOut) { return (GetRealDataObjPtr() != NULL) ? _pIDataObject->GetCanonicalFormatEtc(pformatetcIn, pformatetcOut) : E_FAIL; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::SetData
//
// Synopsis: Create server side object for drag
//
// Arguments: [pformatetc] - format for set
// [pmedium] - medium to use
// [fRelease] - who releases
//
// Returns: NOERROR - operation was successful
// Other - operation failed
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note: This just forwards the operation to the source data object
// if possible.
//
//--------------------------------------------------------------------------
STDMETHODIMP CDragDataObject::SetData( FORMATETC *pformatetc, STGMEDIUM *pmedium, BOOL fRelease) { return (GetRealDataObjPtr() != NULL) ? _pIDataObject->SetData(pformatetc, pmedium, fRelease) : E_FAIL; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::EnumFormatEtc
//
// Synopsis: Create server side object for drag
//
// Arguments: [dwDirection] - direction of formats either set or get
// [ppenumFormatEtc] - where to put enumerator
//
// Returns: NOERROR - operation succeeded.
//
// Algorithm: If format enumerator requested is for a data get, the
// create our private enumerator object otherwise pass
// the request to the real data object.
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note: For the data set direction, we just use the data object of
// the drop source.
//
//--------------------------------------------------------------------------
STDMETHODIMP CDragDataObject::EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC **ppenumFormatEtc) { HRESULT hr;
// Create our enumerator
if (dwDirection == DATADIR_GET) { // In the data get case we use our overridden enumerator.
// This s/b the typical case with Drag and Drop.
*ppenumFormatEtc = NULL; GetFormatEtcDataArray();
if (m_pFormatEtcDataArray) { // enumerator implementation in Clipdata.cpp
*ppenumFormatEtc = new CEnumFormatEtcDataArray(m_pFormatEtcDataArray,0); }
hr = *ppenumFormatEtc ? NOERROR : E_OUTOFMEMORY; } else { // Call through to the real data object because this is the
// set case. In general, this won't happen during Drag and Drop.
hr = (GetRealDataObjPtr() != NULL) ? _pIDataObject->EnumFormatEtc(dwDirection, ppenumFormatEtc) : E_FAIL; }
return hr; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::DAdvise
//
// Synopsis: Create server side object for drag
//
// Arguments: [pformatetc] - format to be advised on
// [advf] - type of advise
// [pAdvSink] - advise to notify
// [pdwConnection] - connection id for advise
//
// Returns: NOERROR - operation was successful
// Other - operation failed
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note: This just forwards the operation to the source data object
// if possible.
//
//--------------------------------------------------------------------------
STDMETHODIMP CDragDataObject::DAdvise( FORMATETC *pformatetc, DWORD advf, IAdviseSink *pAdvSink, DWORD *pdwConnection) { return (GetRealDataObjPtr() != NULL) ? _pIDataObject->DAdvise(pformatetc, advf, pAdvSink, pdwConnection) : E_FAIL; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::DUnadvise
//
// Synopsis: Create server side object for drag
//
// Arguments: [dwConnection] - connection id for advise
//
// Returns: NOERROR - operation was successful
// Other - operation failed
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note: This just forwards the operation to the source data object
// if possible.
//
//--------------------------------------------------------------------------
STDMETHODIMP CDragDataObject::DUnadvise(DWORD dwConnection) { return (GetRealDataObjPtr() != NULL) ? _pIDataObject->DUnadvise(dwConnection) : E_FAIL; }
//+-------------------------------------------------------------------------
//
// Member: CDragDataObject::EnumDAdvise
//
// Synopsis: Create server side object for drag
//
// Arguments: [ppenumAdvise] - where to put the enumerator
//
// Returns: NOERROR - operation was successful
// Other - operation failed
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note: This just forwards the operation to the source data object
// if possible.
//
//--------------------------------------------------------------------------
STDMETHODIMP CDragDataObject::EnumDAdvise(IEnumSTATDATA **ppenumAdvise) { return (GetRealDataObjPtr() != NULL) ? _pIDataObject->EnumDAdvise(ppenumAdvise) : E_FAIL; }
//+-------------------------------------------------------------------------
//
// Member: CreateDragDataObject
//
// Synopsis: Create the server side data object for format enumeration
//
// Arguments: [pvMarshaledDataObject] - marshaled real data object buffer
// [dwSmId] - id for the shared memory
// [ppIDataObject] - output data object.
//
// Returns: NOERROR - could create the object
// E_OUTOFMEMORY - could not create the object
//
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
// Note:
//
//--------------------------------------------------------------------------
HRESULT CreateDragDataObject( void *pvMarshaledDataObject, DWORD dwSmId, IDataObject **ppIDataObject) { CDragDataObject *pDragDataObject = new CDragDataObject(pvMarshaledDataObject, dwSmId);
if (pDragDataObject != NULL) { *ppIDataObject = pDragDataObject; }
// The only thing that can fail here is the memory allocation of
// CDragDataObject thus there are only two error returns.
return (pDragDataObject != NULL) ? NOERROR : E_OUTOFMEMORY; }
//+-------------------------------------------------------------------------
//
// Member: CreateSharedDragFormats
//
// Synopsis: Put the data formats for the data object in shared memory.
//
// Arguments: [pIDataObject] - data object to use for formats.
//
// Returns: NULL - could not create enumerator
// ~NULL - handle to shared memory
//
// Algorithm: First calculate the size of the required memory by enumerating
// the formats. Then allocate the memory and map it into the
// process. Then enumerate the formats again placing them in
// the shared memory. Finally, map the memory out of the
// process and return the handle the file mapping to the
// caller.
//
// History: dd-mmm-yy Author Comment
// 30-Sep-94 Ricksa Created
//
//--------------------------------------------------------------------------
HANDLE CreateSharedDragFormats(IDataObject *pIDataObject) {
// Handle to the shared memory for formats
HANDLE hSharedMemory = NULL;
// Pointer to share memory
FORMATETCDATAARRAY *pFormatEtcDataArray = NULL;
// Size required for the shared memory
DWORD dwSize = 0;
// Count of FORMATETCs contained in the enumerator
DWORD cFormatEtc = 0;
// Buffer for name of shared memory for enumerator
OLECHAR szSharedMemoryName[DRAG_SM_NAME_MAX];
// Work pointer to shared memory for storing FORMATETCs from the enumerator.
FORMATETCDATA *pFormatEtcData;
// Work ptr to shared memory for storing DVTARGETDEVICEs from enumerator.
BYTE *pbDvTarget = NULL;
//
// Calculate the size of the formats
//
// Get the format enumerator
IEnumFORMATETC *penum = NULL; HRESULT hr = wGetEnumFormatEtc(pIDataObject, DATADIR_GET, &penum); FORMATETC FormatEtc;
if( hr != NOERROR ) { // not all apps support enumerators (yahoo). Also, we may
// have run out of memory or encountered some other error.
DDDebugOut((DEB_WARN, "WARNING: Failed to get formatetc enumerator" ", error code (%lx)", hr)); goto exitRtn; }
// Enumerate the data one at a time because this is a local operation
// and it make the code simpler.
while ((hr = penum->Next(1, &FormatEtc, NULL)) == S_OK) { // Bump the entry count
cFormatEtc++;
// Bump the size by the size of another FORMATETC.
dwSize += sizeof(FORMATETCDATA);
// Is there a device target associated with the FORMATETC?
if (FormatEtc.ptd != NULL) { // Bump the size required by the size of the target device
dwSize += FormatEtc.ptd->tdSize;
// Free the target device
CoTaskMemFree(FormatEtc.ptd); } }
// HRESULT s/b S_FALSE at the end of the enumeration.
if (hr != S_FALSE) { goto errRtn; }
// the enumerator may have been empty
if( dwSize == 0 ) { DDDebugOut((DEB_WARN, "WARNING: Empty formatetc enumerator")); goto exitRtn; }
dwSize += sizeof(FORMATETCDATAARRAY); // add space for _cFormats and one extra FORMATETC for FALSE in enumerator.
//
// Create shared memory for the type enumeration
//
// Build name of shared memory - make it unique by using the thread id.
wsprintf(szSharedMemoryName, szSharedMemoryTemplate, GetCurrentThreadId());
// Create the shared memory object
hSharedMemory = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, dwSize, szSharedMemoryName);
// Did the file mapping get created?
if (hSharedMemory == NULL) { goto errRtn; }
// Map in the memory
pFormatEtcDataArray = (FORMATETCDATAARRAY *) MapViewOfFile( hSharedMemory, FILE_MAP_WRITE, 0, // High-order 32 bits of file offset
0, // Low-order 32 bits of file offset
0); // Number of bytes to map; 0 means all.
// Could we map the memory?
if (pFormatEtcDataArray == NULL) { goto errRtn; }
// We can initialize the size of the array now.
pFormatEtcDataArray->_dwSig = 0; pFormatEtcDataArray->_dwSize = dwSize; pFormatEtcDataArray->_cFormats = cFormatEtc; pFormatEtcDataArray->_cRefs = 1; pFormatEtcDataArray->_fIs64BitArray = IS_WIN64;
//
// Copy the formats into the shared memory
//
// Get back to the start of the enumeration
penum->Reset();
// This is the pointer to where we will copy the data from the
// enumeration.
pFormatEtcData = &pFormatEtcDataArray->_FormatEtcData[0];
// put DvTarget past last valid FormatEtc + 1 to handle S_FALSE enumerator case.
pbDvTarget = (BYTE *) (&pFormatEtcDataArray->_FormatEtcData[cFormatEtc + 1]);
// Loop loading the formats into the shared memory.
while (penum->Next(1,&(pFormatEtcData->_FormatEtc), NULL) != S_FALSE) { // Is there a DVTARGETDEVICE?
if (pFormatEtcData->_FormatEtc.ptd != NULL) {
// Copy the device target data
memcpy(pbDvTarget,pFormatEtcData->_FormatEtc.ptd,(pFormatEtcData->_FormatEtc.ptd)->tdSize);
// Free the target device data
CoTaskMemFree(pFormatEtcData->_FormatEtc.ptd);
// NOTE: For this shared memory structure, we override the
// FORMATETC field so that it is that offset to the DVTARGETDEVICE
// from the beginning of the shared memory rather than a direct
// pointer to the structure. This is because we can't guarantee
// the base of shared memory in different processes.
pFormatEtcData->_FormatEtc.ptd = (DVTARGETDEVICE *) (pbDvTarget - (BYTE *) pFormatEtcDataArray);
// Bump pointer of where to copy target to next available
// byte for copy.
pbDvTarget += ((DVTARGETDEVICE *) pbDvTarget)->tdSize; Assert(dwSize >= (DWORD) (pbDvTarget - (BYTE *) pFormatEtcDataArray));
}
// Bug#18669 - if dwAspect was set to NULL the 16 bit dlls would
// set it to content.
if ( (NULL == pFormatEtcData->_FormatEtc.dwAspect) && IsWOWThread() ) { pFormatEtcData->_FormatEtc.dwAspect = DVASPECT_CONTENT; pFormatEtcData->_FormatEtc.lindex = -1; // CorelDraw also puts up a lindex of 0
}
// Bump the pointer in the table of FORMATETCs to the next slot
pFormatEtcData++; }
Assert( dwSize >= (DWORD) ( (BYTE *) pFormatEtcData - (BYTE *) pFormatEtcDataArray)); Assert( dwSize >= (DWORD) ( (BYTE *) pbDvTarget - (BYTE *) pFormatEtcDataArray));
// Successful enumeration always ends with S_FALSE.
if (hr == S_FALSE) { goto exitRtn; }
errRtn:
if (hSharedMemory != NULL) { CloseHandle(hSharedMemory); hSharedMemory = NULL; }
exitRtn:
if( penum ) { // HACK ALERT: Do not release the enumerator if the calling application
// was Interleaf 6.0, otherwise they will fault in the release call.
if (!IsTaskName(L"ILEAF6.EXE")) { penum->Release(); } }
if (pFormatEtcDataArray != NULL) { // Only remote clients will use this memory so we unmap it
// out of our address space.
UnmapViewOfFile(pFormatEtcDataArray); }
return hSharedMemory; }
|