/*++ © 1998 Seagate Software, Inc. All rights reserved. Module Name: MsDatObj.cpp Abstract: Implementation of IDataObject interface for Multi-Select Allows MMC to get a list of Node Types Author: Art Bragg 28-Aug-1997 Revision History: --*/ ///////////////////////////////////////////////////////////////////////////// // // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "msdatobj.h" #define BUMP_SIZE 20 // Declare Snap-in NodeType formats: UINT CMsDataObject::m_cfObjectTypes = RegisterClipboardFormat(CCF_OBJECT_TYPES_IN_MULTI_SELECT); HRESULT CMsDataObject::FinalConstruct( void ) /*++ Routine Description: Called during initial CMsDataObject construction to initialize members. Arguments: none. Return Value: S_OK - Initialized correctly. E_xxxxxxxxxxx - Failure occurred. --*/ { HRESULT hr = S_OK; WsbTraceIn( L"CMsDataObject::FinalConstruct", L"" ); try { m_Count = 0; // Allocate initial array of GUIDs m_pGUIDArray = (GUID *) malloc (BUMP_SIZE * sizeof(GUID)); WsbAffirm ((m_pGUIDArray != NULL), E_OUTOFMEMORY); ZeroMemory (m_pGUIDArray, (BUMP_SIZE * sizeof(GUID))); m_pUnkNodeArray = (IUnknown **) malloc( BUMP_SIZE * sizeof(IUnknown*) ); WsbAffirm ((m_pGUIDArray != NULL), E_OUTOFMEMORY); ZeroMemory (m_pGUIDArray, (BUMP_SIZE * sizeof(IUnknown*))); m_pObjectIdArray = (GUID *) malloc (BUMP_SIZE * sizeof(GUID)); WsbAffirm ((m_pObjectIdArray != NULL), E_OUTOFMEMORY); ZeroMemory (m_pObjectIdArray, (BUMP_SIZE * sizeof(GUID))); m_ArraySize = BUMP_SIZE; WsbAffirmHr (CComObjectRoot::FinalConstruct( )); } WsbCatch (hr); WsbTraceOut( L"CMsDataObject::FinalConstruct", L"hr = <%ls>", WsbHrAsString( hr ) ); return( hr ); } void CMsDataObject::FinalRelease( void ) /*++ Routine Description: Called on final release in order to clean up all members. Arguments: none. Return Value: none. --*/ { WsbTraceIn( L"CMsDataObject::FinalRelease", L"" ); // Clean up array of GUIDs free( m_pGUIDArray ); for( DWORD i = 0; i < m_Count; i++ ) { m_pUnkNodeArray[i]->Release(); } free( m_pUnkNodeArray ); WsbTraceOut( L"CMsDataObject::FinalRelease", L"" ); } // IDataObject STDMETHODIMP CMsDataObject::GetDataHere( LPFORMATETC lpFormatetc, LPSTGMEDIUM /*lpMedium*/ ) /*++ Routine Description: Retrieve information FROM the dataobject and put INTO lpMedium. Arguments: lpFormatetc - Format to retreive. lpMedium - Storage to put information into. Return Value: S_OK - Storage filled in. E_xxxxxxxxxxx - Failure occurred. --*/ { WsbTraceIn( L"CMsDataObject::GetDataHere", L"lpFormatetc->cfFormat = <%ls>", RsClipFormatAsString( lpFormatetc->cfFormat ) ); HRESULT hr = E_NOTIMPL; WsbTraceOut( L"CMsDataObject::GetDataHere", L"hr = <%ls>", WsbHrAsString( hr ) ); return( hr ); } STDMETHODIMP CMsDataObject::SetData( LPFORMATETC lpFormatetc, LPSTGMEDIUM /*lpMedium*/, BOOL /*fRelease*/ ) { WsbTraceIn( L"CMsDataObject::SetData", L"lpFormatetc->cfFormat = <%ls>", RsClipFormatAsString( lpFormatetc->cfFormat ) ); HRESULT hr = E_NOTIMPL; WsbTraceOut( L"CMsDataObject::SetData", L"hr = <%ls>", WsbHrAsString( hr ) ); return( hr ); } /////////////////////////////////////////////////////////////////////// // Note - CMsDataObject does not implement these /////////////////////////////////////////////////////////////////////// STDMETHODIMP CMsDataObject::GetData( LPFORMATETC lpFormatetcIn, LPSTGMEDIUM lpMedium ) { WsbTraceIn( L"CMsDataObject::GetData", L"lpFormatetc->cfFormat = <%ls>", RsClipFormatAsString( lpFormatetcIn->cfFormat ) ); HRESULT hr = S_OK; lpMedium->tymed = TYMED_NULL; lpMedium->hGlobal = NULL; lpMedium->pUnkForRelease = NULL; try { // // Don't need to throw error if not a format we don't understand - // which is currently only CCF_OBJECT_TYPES_IN_MULTI_SELECT // if( lpFormatetcIn->cfFormat == m_cfObjectTypes ) { // // Check to make sure there is data to transfer // WsbAffirm( ( lpFormatetcIn->tymed & TYMED_HGLOBAL ), DV_E_TYMED ); // // m_ppDataObjects m_count // UINT datasize = sizeof(DWORD) + ( sizeof(GUID) * m_Count ); lpMedium->hGlobal = ::GlobalAlloc( GPTR, datasize ); WsbAffirmAlloc( lpMedium->hGlobal ); // // Put the count in the allocated memory // BYTE* pb = reinterpret_cast(lpMedium->hGlobal); *((DWORD*)lpMedium->hGlobal) = m_Count; // // Copy the GUIDs to the allocated memory // if( m_Count > 0 ) { pb += sizeof(DWORD); CopyMemory(pb, m_pGUIDArray, m_Count * sizeof(GUID)); } lpMedium->tymed = TYMED_HGLOBAL; } else { hr = DATA_E_FORMATETC; } } WsbCatch( hr ); WsbTraceOut( L"CMsDataObject::GetData", L"hr = <%ls>", WsbHrAsString( hr ) ); return( hr ); } STDMETHODIMP CMsDataObject::EnumFormatEtc(DWORD /*dwDirection*/, LPENUMFORMATETC* /*ppEnumFormatEtc*/) { WsbTraceIn( L"CMsDataObject::EnumFormatEtc", L"" ); HRESULT hr = E_NOTIMPL; WsbTraceOut( L"CMsDataObject::EnumFormatEtc", L"hr = <%ls>", WsbHrAsString( hr ) ); return( hr ); } HRESULT CMsDataObject::RetrieveMultiSelectData (LPSTGMEDIUM lpMedium) { WsbTraceIn( L"CMsDataObject::RetrieveMultiSelectData", L"" ); HRESULT hr = S_OK; try { WsbAffirm( lpMedium != NULL, E_POINTER); WsbAffirm( lpMedium->tymed == TYMED_HGLOBAL, E_FAIL ); // Create the stream on the hGlobal passed in. When we write to the stream, // it simultaneously writes to the hGlobal the same information. LPSTREAM lpStream; WsbAffirmHr( CreateStreamOnHGlobal(lpMedium->hGlobal, FALSE, &lpStream )); // Write 'len' number of bytes from pBuffer into the stream. When we write // to the stream, it simultaneously writes to the global memory we // associated it with above. ULONG numBytesWritten; // Write the count first WsbAffirmHr( lpStream->Write(&m_Count, sizeof (m_Count), &numBytesWritten )); // Write the GUID array WsbAffirmHr( lpStream->Write(m_pGUIDArray, m_Count * sizeof (GUID), &numBytesWritten )); // Because we told CreateStreamOnHGlobal with 'FALSE', only the stream is released here. // Note - the caller (i.e. snap-in, object) will free the HGLOBAL // at the correct time. This is according to the IDataObject specification. lpStream->Release(); } WsbCatch( hr ); WsbTraceOut( L"CMsDataObject::RetrieveMultiSelectData", L"hr = <%ls>", WsbHrAsString( hr ) ); return hr; } // Data setting method // Note that we keep the node array seperate from the GUID array because // the GetData interface memory copies the GUID array to the stream. STDMETHODIMP CMsDataObject::AddNode (ISakNode *pNode ) { WsbTraceIn( L"CMsDataObject::AddNode", L"pNode = <0x%p>", pNode ); HRESULT hr = S_OK; GUID thisGUID; GUID objectId; GUID * pGUIDArray = 0, * pObjectIdArray = 0; IUnknown ** pUnkNodeArray = 0; try { // // Get the object type GUID // WsbAffirmHr( pNode->GetNodeType( &thisGUID ) ); // // Get the unique ID for the engine object (i.e. FsaResource) // WsbAffirmHr( pNode->GetObjectId( &objectId ) ); // // Reallocate if we need to // if( m_Count >= m_ArraySize ) { // // Allocate new buffer // m_ArraySize += BUMP_SIZE; pGUIDArray = (GUID *) malloc( m_ArraySize * sizeof( GUID ) ); WsbAffirmAlloc( pGUIDArray ); pUnkNodeArray = (IUnknown **) malloc( m_ArraySize * sizeof( IUnknown* ) ); WsbAffirmAlloc( pUnkNodeArray ); pObjectIdArray = (GUID *) malloc( m_ArraySize * sizeof( GUID ) ); WsbAffirmAlloc( pObjectIdArray ); // // copy over old buffer and free // memcpy( pGUIDArray, m_pGUIDArray, m_Count * sizeof( GUID ) ); memcpy( pUnkNodeArray, m_pUnkNodeArray, m_Count * sizeof( IUnknown* ) ); memcpy( pObjectIdArray, m_pObjectIdArray, m_Count * sizeof( GUID ) ); free( m_pGUIDArray ); free( m_pUnkNodeArray ); free( m_pObjectIdArray ); m_pGUIDArray = pGUIDArray; m_pUnkNodeArray = pUnkNodeArray; m_pObjectIdArray = pObjectIdArray; pGUIDArray = 0; pUnkNodeArray = 0; pObjectIdArray = 0; } // // Put the GUID in the array // m_pGUIDArray[ m_Count ] = thisGUID; // // Put the objectId in the array // m_pObjectIdArray[ m_Count ] = objectId; // // Put the unknown pointer (the Cookie) in the array // CComPtr pUnkNode; WsbAffirmHr( RsQueryInterface( pNode, IUnknown, pUnkNode ) ); pUnkNode.CopyTo( &m_pUnkNodeArray[ m_Count ] ); m_Count++; } WsbCatch( hr ); if( pGUIDArray ) free( pGUIDArray ); if( pObjectIdArray ) free( pObjectIdArray ); if( pUnkNodeArray ) free( pUnkNodeArray ); WsbTraceOut( L"CMsDataObject::AddNode", L"hr = <%ls>", WsbHrAsString( hr ) ); return( hr ); } STDMETHODIMP CMsDataObject::GetNodeEnumerator( IEnumUnknown **ppEnum ) { WsbTraceIn( L"CMsDataObject::GetNodeEnumerator", L"ppEnum = <0x%p>", ppEnum ); HRESULT hr = S_OK; CEnumUnknown * pEnum = 0; try { WsbAffirmPointer( ppEnum ); *ppEnum = 0; // // New an ATL enumerator // pEnum = new CEnumUnknown; WsbAffirm( 0 != pEnum, E_OUTOFMEMORY ); // // Initialize it to copy the current node interface pointers // WsbAffirmHr( pEnum->FinalConstruct() ); WsbAffirmHr( pEnum->Init( &m_pUnkNodeArray[0], &m_pUnkNodeArray[m_Count], NULL, AtlFlagCopy ) ); WsbAffirmHr( pEnum->QueryInterface( IID_IEnumUnknown, (void**)ppEnum ) ); } WsbCatchAndDo( hr, if( pEnum ) delete pEnum; ); WsbTraceOut( L"CMsDataObject::GetNodeEnumerator", L"hr = <%ls>, *ppEnum = <%ls>", WsbHrAsString( hr ), WsbPtrToPtrAsString( (void**)ppEnum ) ); return( hr ); } STDMETHODIMP CMsDataObject::GetObjectIdEnumerator( IEnumGUID ** ppEnum ) { WsbTraceIn( L"CMsDataObject::GetObjectIdEnumerator", L"ppEnum = <0x%p>", ppEnum ); HRESULT hr = S_OK; CEnumGUID * pEnum = 0; try { WsbAffirmPointer( ppEnum ); *ppEnum = 0; // // New an ATL enumerator // pEnum = new CEnumGUID; WsbAffirm( 0 != pEnum, E_OUTOFMEMORY ); // // Initialize it to copy the current node interface pointers // WsbAffirmHr( pEnum->FinalConstruct() ); WsbAffirmHr( pEnum->Init( &m_pObjectIdArray[0], &m_pObjectIdArray[m_Count], NULL, AtlFlagCopy ) ); WsbAffirmHr( pEnum->QueryInterface( IID_IEnumGUID, (void**)ppEnum ) ); } WsbCatchAndDo( hr, if( pEnum ) delete pEnum; ); WsbTraceOut( L"CMsDataObject::GetObjectIdEnumerator", L"hr = <%ls>, *ppEnum = <%ls>", WsbHrAsString( hr ), WsbPtrToPtrAsString( (void**)ppEnum ) ); return( hr ); }