//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1998 - 1999 // // File: dataobj_.cpp // //-------------------------------------------------------------------------- #include /////////////////////////////////////////////////////////////////////////////// // Sample code to show how to Create DataObjects // Minimal error checking for clarity /////////////////////////////////////////////////////////////////////////////// // Snap-in NodeType in both GUID format and string format // Note - Typically there is a node type for each different object, sample // only uses one node type. const wchar_t* CCF_DNS_SNAPIN_INTERNAL = L"DNS_SNAPIN_INTERNAL"; CLIPFORMAT CDataObject::m_cfNodeType = (CLIPFORMAT)RegisterClipboardFormat(CCF_NODETYPE); CLIPFORMAT CDataObject::m_cfNodeTypeString = (CLIPFORMAT)RegisterClipboardFormat(CCF_SZNODETYPE); CLIPFORMAT CDataObject::m_cfDisplayName = (CLIPFORMAT)RegisterClipboardFormat(CCF_DISPLAY_NAME); CLIPFORMAT CDataObject::m_cfCoClass = (CLIPFORMAT)RegisterClipboardFormat(CCF_SNAPIN_CLASSID); CLIPFORMAT CDataObject::m_cfColumnID = (CLIPFORMAT)RegisterClipboardFormat(CCF_COLUMN_SET_ID); CLIPFORMAT CDataObject::m_cfInternal = (CLIPFORMAT)RegisterClipboardFormat(CCF_DNS_SNAPIN_INTERNAL); CLIPFORMAT CDataObject::m_cfMultiSel = (CLIPFORMAT)RegisterClipboardFormat(CCF_MULTI_SELECT_SNAPINS); CLIPFORMAT CDataObject::m_cfMultiObjTypes = (CLIPFORMAT)RegisterClipboardFormat(CCF_OBJECT_TYPES_IN_MULTI_SELECT); #ifdef _DEBUG_REFCOUNT unsigned int CDataObject::m_nOustandingObjects = 0; #endif // _DEBUG_REFCOUNT ///////////////////////////////////////////////////////////////////////////// // CInternalFormatCracker HRESULT CInternalFormatCracker::Extract(LPDATAOBJECT lpDataObject) { if (DOBJ_CUSTOMOCX == lpDataObject || DOBJ_CUSTOMWEB == lpDataObject || DOBJ_NULL == lpDataObject) { return DV_E_CLIPFORMAT; } if (m_pInternal != NULL) _Free(); SMMCDataObjects * pDO = NULL; STGMEDIUM stgmedium = { TYMED_HGLOBAL, NULL }; FORMATETC formatetc = { CDataObject::m_cfInternal, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; FORMATETC formatetc2 = { CDataObject::m_cfMultiSel, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; HRESULT hr = lpDataObject->GetData(&formatetc2, &stgmedium); if (FAILED(hr)) { hr = lpDataObject->GetDataHere(&formatetc, &stgmedium); if (FAILED(hr)) return hr; m_pInternal = reinterpret_cast(stgmedium.hGlobal); } else { pDO = reinterpret_cast(stgmedium.hGlobal); for (UINT i = 0; i < pDO->count; i++) { hr = pDO->lpDataObject[i]->GetDataHere(&formatetc, &stgmedium); if (FAILED(hr)) break; m_pInternal = reinterpret_cast(stgmedium.hGlobal); if (m_pInternal != NULL) break; } } return hr; } void CInternalFormatCracker::GetCookieList(CNodeList& list) { for (DWORD dwCount = 0; dwCount < m_pInternal->m_cookie_count; dwCount++) { list.AddTail(m_pInternal->m_p_cookies[dwCount]); } } ///////////////////////////////////////////////////////////////////////////// // CDataObject implementations STDMETHODIMP CDataObject::GetDataHere(LPFORMATETC lpFormatetc, LPSTGMEDIUM lpMedium) { HRESULT hr = DV_E_CLIPFORMAT; AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Based on the CLIPFORMAT write data to the stream const CLIPFORMAT cf = lpFormatetc->cfFormat; if(cf == m_cfNodeType) { hr = CreateNodeTypeData(lpMedium); } else if(cf == m_cfNodeTypeString) { hr = CreateNodeTypeStringData(lpMedium); } else if (cf == m_cfDisplayName) { hr = CreateDisplayName(lpMedium); } else if (cf == m_cfCoClass) { hr = CreateCoClassID(lpMedium); } else if (cf == m_cfInternal) { hr = CreateInternal(lpMedium); } else if (cf == m_cfMultiObjTypes) { hr = CreateMultiSelectObject(lpMedium); } else { // if not successful, maybe there is a node specific clipboard format, // so ask the node itself to provide CTreeNode* pNode = GetTreeNodeFromCookie(); ASSERT(pNode != NULL); if (pNode != NULL) { hr = pNode->GetDataHere(cf, lpMedium, this); } } return hr; } // Note - Sample does not implement these STDMETHODIMP CDataObject::GetData(LPFORMATETC lpFormatetcIn, LPSTGMEDIUM lpMedium) { HRESULT hr = DV_E_CLIPFORMAT; AFX_MANAGE_STATE(AfxGetStaticModuleState()); // Based on the CLIPFORMAT write data to the stream const CLIPFORMAT cf = lpFormatetcIn->cfFormat; if (cf == m_cfColumnID) { hr = CreateColumnID(lpMedium); } else if (cf == m_cfMultiObjTypes) { hr = CreateMultiSelectObject(lpMedium); } else { // if not successful, maybe there is a node specific clipboard format, // so ask the node itself to provide CTreeNode* pNode = GetTreeNodeFromCookie(); if (pNode != NULL) { hr = pNode->GetData(cf, lpMedium, this); } } return hr; } STDMETHODIMP CDataObject::EnumFormatEtc(DWORD, LPENUMFORMATETC*) { return E_NOTIMPL; } ///////////////////////////////////////////////////////////////////////////// // CDataObject creation members HRESULT CDataObject::Create(const void* pBuffer, size_t len, LPSTGMEDIUM lpMedium) { HRESULT hr = DV_E_TYMED; // Do some simple validation if (pBuffer == NULL || lpMedium == NULL) return E_POINTER; // Make sure the type medium is HGLOBAL if (lpMedium->tymed == TYMED_HGLOBAL) { // Create the stream on the hGlobal passed in LPSTREAM lpStream; hr = CreateStreamOnHGlobal(lpMedium->hGlobal, FALSE, &lpStream); if (SUCCEEDED(hr)) { // Write to the stream the number of bytes ULONG bytesToWrite = static_cast(min(len, ::GlobalSize(lpMedium->hGlobal))); unsigned long written; hr = lpStream->Write(pBuffer, bytesToWrite, &written); // 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(); } } return hr; } HRESULT CDataObject::CreateColumnID(LPSTGMEDIUM lpMedium) { CTreeNode* pTreeNode = GetTreeNodeFromCookie(); if (pTreeNode == NULL) { return E_FAIL; } ASSERT(pTreeNode->IsContainer()); CContainerNode* pContainerNode = (CContainerNode*)pTreeNode; // build the column id LPCWSTR lpszColumnID = pContainerNode->GetColumnID(); // We are assuming the column ID is NULL terminated. Since this is usually // hardcoded and there is no good way to verify that it is NULL terminated // this usage should be fine. size_t iLen = wcslen(lpszColumnID); iLen += 1; // Include space for null. // allocate enough memory for the struct and the string for the column id size_t arraySizeInBytes = sizeof(SColumnSetID) + (iLen * sizeof(WCHAR)); SColumnSetID* pColumnID = (SColumnSetID*)malloc(arraySizeInBytes); if (pColumnID != NULL) { memset(pColumnID, 0, arraySizeInBytes); pColumnID->cBytes = static_cast(iLen * sizeof(WCHAR)); // NOTICE-2002/04/18-artm Part of fix for ntraid#ntbug9-540061. // Unlike wcscpy(), StringCchCopy() will ensure that the destination // buffer is null terminated and report an error code if there was // a truncation (won't overrun the destination buffer). // // Since we needed to use strsafe.h elsewhere in this file, I decided // to replace these dangerous wcscpy() uses that were deprecated by // strsafe.h. HRESULT err; err = StringCchCopyW( reinterpret_cast(pColumnID->id), // destination string iLen, // size of destination string (including null) lpszColumnID); // source string if (FAILED(err)) { ASSERT(false); // This should never happen. free(pColumnID); return err; } // copy the column id to global memory size_t cb = sizeof(SColumnSetID) + (iLen * sizeof(WCHAR)); lpMedium->tymed = TYMED_HGLOBAL; lpMedium->hGlobal = ::GlobalAlloc(GMEM_SHARE|GMEM_MOVEABLE, cb); if (lpMedium->hGlobal == NULL) return STG_E_MEDIUMFULL; BYTE* pb = reinterpret_cast(::GlobalLock(lpMedium->hGlobal)); if (pb) { // REVIEWED-2002/03/0-JeffJon-The count of bytes is equal to the number // of bytes allocated memcpy(pb, pColumnID, cb); ::GlobalUnlock(lpMedium->hGlobal); } free(pColumnID); } return S_OK; } HRESULT CDataObject::CreateNodeTypeData(LPSTGMEDIUM lpMedium) { // Create the node type object in GUID format // First ask the related node, if failed, get the default GUID // from the root node CTreeNode* pNode = GetTreeNodeFromCookie(); if (pNode == NULL) { return E_FAIL; } const GUID* pNodeType = pNode->GetNodeType(); if (pNodeType == NULL) { pNodeType = GetDataFromComponentDataObject()->GetNodeType(); } HRESULT hr = Create(pNodeType, sizeof(GUID), lpMedium); return hr; } HRESULT CDataObject::CreateNodeTypeStringData(LPSTGMEDIUM lpMedium) { // Create the node type object in GUID string format OLECHAR szNodeType[128] = {0}; // First ask the related node, if failed, get the default GUID // from the root node CTreeNode* pNode = GetTreeNodeFromCookie(); if (pNode == NULL) { return E_FAIL; } const GUID* pNodeType = pNode->GetNodeType(); if (pNodeType == NULL) { pNodeType = GetDataFromComponentDataObject()->GetNodeType(); } ::StringFromGUID2(*pNodeType,szNodeType,128); return Create(szNodeType, BYTE_MEM_LEN_W(szNodeType), lpMedium); } HRESULT CDataObject::CreateDisplayName(LPSTGMEDIUM lpMedium) { // This is the display named used in the scope pane and snap-in manager // We get it from the root node. CString szDispName; szDispName = GetDataFromComponentDataObject()->GetDisplayName(); return Create(szDispName, (szDispName.GetLength()+1) * sizeof(wchar_t), lpMedium); } HRESULT CDataObject::CreateCoClassID(LPSTGMEDIUM lpMedium) { // TODO ASSERT(m_pUnkComponentData != NULL); IPersistStream* pIPersistStream = NULL; HRESULT hr = m_pUnkComponentData->QueryInterface(IID_IPersistStream, (void**)&pIPersistStream); if (FAILED(hr)) return hr; ASSERT(pIPersistStream != NULL); // Create the CoClass information CLSID clsid; VERIFY(SUCCEEDED(pIPersistStream->GetClassID(&clsid))); hr = Create(reinterpret_cast(&clsid), sizeof(CLSID), lpMedium); ASSERT(SUCCEEDED(hr)); pIPersistStream->Release(); return hr; } HRESULT CDataObject::CreateInternal(LPSTGMEDIUM lpMedium) { HRESULT hr = S_OK; INTERNAL * pInt = NULL; void * pBuf = NULL; UINT size = sizeof(INTERNAL); size += sizeof(CTreeNode*) * (m_internal.m_cookie_count); pBuf = GlobalAlloc (GPTR, size); if (pBuf != NULL) { pInt = (INTERNAL *) pBuf; lpMedium->hGlobal = pBuf; // copy the data pInt->m_type = m_internal.m_type; pInt->m_cookie_count = m_internal.m_cookie_count; pInt->m_p_cookies = (CTreeNode**) ((BYTE *)pInt + sizeof(INTERNAL)); // REVIEWED-2002/03/08-JeffJon-The number of bytes being copied // will fit in the supplied buffer memcpy (pInt->m_p_cookies, m_internal.m_p_cookies, sizeof(CTreeNode*) * (m_internal.m_cookie_count)); hr = Create(pBuf, size, lpMedium); } else { hr = E_OUTOFMEMORY; } return hr; } //+---------------------------------------------------------------------------- // // Method: CDSDataObject::CreateMultiSelectObject // // Synopsis: this is to create the list of types selected // //----------------------------------------------------------------------------- HRESULT CDataObject::CreateMultiSelectObject(LPSTGMEDIUM lpMedium) { CTreeNode** cookieArray = NULL; cookieArray = (CTreeNode**) GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, m_internal.m_cookie_count*sizeof(CTreeNode*)); if (!cookieArray) { return E_OUTOFMEMORY; } for (UINT k=0; kGetNodeType()); GUID Guid2 = *(cookieArray[j]->GetNodeType()); if (IsEqualGUID (Guid1, Guid2)) { bDuplicateArr[index] = TRUE; break; //repeated GUID } } if (!bDuplicateArr[index]) { cCount++; } } UINT size = sizeof(SMMCObjectTypes) + (cCount) * sizeof(GUID); void * pTmp = ::GlobalAlloc(GPTR, size); if (!pTmp) { if (cookieArray) { GlobalFree (cookieArray); } if (bDuplicateArr) { GlobalFree (bDuplicateArr); } return E_OUTOFMEMORY; } SMMCObjectTypes* pdata = reinterpret_cast(pTmp); pdata->count = cCount; UINT i = 0; for (index=0; indexguid[i++] = *(cookieArray[index]->GetNodeType()); } } ASSERT(i == cCount); lpMedium->hGlobal = pTmp; GlobalFree (cookieArray); GlobalFree (bDuplicateArr); return S_OK; } CRootData* CDataObject::GetDataFromComponentDataObject() { CComponentDataObject* pObject = reinterpret_cast(m_pUnkComponentData); CRootData* pRootData = pObject->GetRootData(); ASSERT(pRootData != NULL); return pRootData; } CTreeNode* CDataObject::GetTreeNodeFromCookie() { CComponentDataObject* pObject = reinterpret_cast(m_pUnkComponentData); CTreeNode* pNode = NULL; if (m_internal.m_cookie_count > 0) { pNode = m_internal.m_p_cookies[0]; if (pNode == NULL) { return pObject->GetRootData(); } } return pNode; } void CDataObject::AddCookie(CTreeNode* cookie) { const UINT MEM_CHUNK_SIZE = 10; void * pTMP = NULL; if ((m_internal.m_cookie_count) % MEM_CHUNK_SIZE == 0) { if (m_internal.m_p_cookies != NULL) { pTMP = realloc (m_internal.m_p_cookies, (m_internal.m_cookie_count + MEM_CHUNK_SIZE) * sizeof (CTreeNode*)); } else { pTMP = malloc (MEM_CHUNK_SIZE * sizeof (CTreeNode*)); } if (pTMP == NULL) { TRACE(_T("CDataObject::AddCookie - malloc/realloc failed..")); ASSERT (pTMP != NULL); if (m_internal.m_p_cookies) { free(m_internal.m_p_cookies); m_internal.m_p_cookies = 0; m_internal.m_cookie_count = 0; return; } } else { m_internal.m_p_cookies = (CTreeNode**)pTMP; } } m_internal.m_p_cookies[m_internal.m_cookie_count] = cookie; m_internal.m_cookie_count++; }