///<Summary>This constructor takes a pointer to an IDataObject obtained from a /// IDropTarget Interface's DragEnter method. /// If the pointer points to a .Net DataObject (which only happens within the same app), /// convert it and call ProcessNetDataObject /// Otherwise, it from another app, possibly Win Explorer, so /// check it for the required formats and build m_DragList. /// Any error just quits, leaving m_IsValid as False ... Caller must check ///</Summary> public CProcDataObject(IntPtr pDataObj) //Assumed to be a pointer to an IDataObject { //save it off -- DragWrapper class won't use it // CDragWrapper class may, but should, in this version, use the original m_DataObject = pDataObj; bool HadError = false; //used for various error conditions try { IDO = Marshal.GetTypedObjectForIUnknown(pDataObj, typeof(ShellDll.IDataObject)) as ShellDll.IDataObject; } catch (Exception) { // Debug.WriteLine("Exception Thrown in CMyDataObject -Getting COM interface: " & vbCrLf & ex.ToString) HadError = true; } //If it is really a .Net IDataObject, then treat it as such if (HadError) { try { NetIDO = Marshal.GetTypedObjectForIUnknown(pDataObj, typeof(System.Windows.Forms.IDataObject)) as System.Windows.Forms.IDataObject; IsNet = true; } catch { IsNet = false; } } if (IsNet) { //Any error in ProcessNetDataObject will leave m_IsValid as False -- our only Error Indicator ProcessNetDataObject(NetIDO); } else //IDataObject not from Net, Do it the hard way { if (HadError) { return; //can do no more } ProcessCOMIDataObject(IDO); //It either worked or not. m_IsValid is set accordingly, so we are done } }
///<Summary>Given an IDataObject from some non-net source, Build the /// m_DragList ArrayList. /// Also ensure that the IDataObject has "Shell IDList Array" formatted data /// If not, build it in m_StreamCIDA, if so, copy to m_StreamCIDA ///If dealing with all FileSystem Items, /// </Summary> private void ProcessCOMIDataObject(ShellDll.IDataObject IDO) { //Don't even look for an ArrayList. If there, we don't know how to access //Therefore, we need either a "FileDrop" or a "Shell IDList Array" to //extract the info for m_DragList and to ensure that the IDataObject //actually has a "Shell IDList Array" int HR; //general use response variable ShellDll.FORMATETC fmtEtc; ShellDll.STGMEDIUM stg; //ensure m_StreamCIDA is nothing - will test for this later m_StreamCIDA = null; //First check for "Shell IDList Array" -- preferred in this case (and most others) int cf = ShellDll.RegisterClipboardFormat("Shell IDList Array"); if (cf != 0) { fmtEtc.cfFormat = (ShellDll.CF)cf; fmtEtc.lindex = -1; fmtEtc.dwAspect = ShellDll.DVASPECT.CONTENT; fmtEtc.ptd = IntPtr.Zero; fmtEtc.Tymd = ShellDll.TYMED.HGLOBAL; stg.hGlobal = IntPtr.Zero; stg.pUnkForRelease = IntPtr.Zero; stg.tymed = (int)ShellDll.TYMED.HGLOBAL; HR = IDO.GetData(ref fmtEtc, ref stg); if (HR == 0) { IntPtr cidaptr = Marshal.ReadIntPtr(stg.hGlobal); m_StreamCIDA = MakeStreamFromCIDA(cidaptr); MakeDragListFromCIDA(); ShellDll.ReleaseStgMedium(ref stg); //done with this } else { try { Marshal.ThrowExceptionForHR(HR); } catch (Exception) { } } } //Check for "FileDrop" (CF.HDROP) if we have to if (m_Draglist.Count < 1) //skip this if already have list { fmtEtc.cfFormat = ShellDll.CF.HDROP; fmtEtc.lindex = -1; fmtEtc.dwAspect = ShellDll.DVASPECT.CONTENT; fmtEtc.ptd = IntPtr.Zero; fmtEtc.Tymd = ShellDll.TYMED.HGLOBAL; stg.hGlobal = IntPtr.Zero; stg.pUnkForRelease = IntPtr.Zero; stg.tymed = 0; HR = IDO.GetData(ref fmtEtc, ref stg); if (HR == 0) //we have an HDROP and stg.hGlobal points to the info { IntPtr pHdrop = Marshal.ReadIntPtr(stg.hGlobal); int nrItems = ShellDll.DragQueryFile(pHdrop, -1, null, 0); int i; for (i = 0; i <= nrItems - 1; i++) { int plen = ShellDll.DragQueryFile(pHdrop, i, null, 0); StringBuilder SB = new StringBuilder(plen + 1); int flen = ShellDll.DragQueryFile(pHdrop, i, SB, plen + 1); Debug.WriteLine("Fetched from HDROP: " + SB.ToString()); try //if GetCShitem returns Nothing(it's failure marker) then catch it { m_Draglist.Add(ExpTreeLib.CShItem.GetCShItem(SB.ToString())); } catch (Exception ex) // in this case, just skip it { Debug.WriteLine("Error: CMyDataObject.ProcessComIDataObject - Adding via HDROP" + "\r\n" + "\t" + ex.Message); } } ShellDll.ReleaseStgMedium(ref stg); //done with this stg //Else // Marshal.ThrowExceptionForHR(HR) } } //Have done what we can to get m_DragList -- exit on failure if (m_Draglist.Count < 1) //Can't do any more -- Quit { return; //leaving m_IsValid as False } //May not have Shell IDList Array in IDataObject. If not, give it one if (m_StreamCIDA == null) //IDO does not yet have "Shell IDList Array" Data { m_StreamCIDA = MakeShellIDArray(m_Draglist); if (m_StreamCIDA == null) { return; //failed to make it } //Now put the CIDA into the original IDataObject fmtEtc.cfFormat = (ShellDll.CF)cf; //registered at routine entry fmtEtc.lindex = -1; fmtEtc.dwAspect = ShellDll.DVASPECT.CONTENT; fmtEtc.ptd = IntPtr.Zero; fmtEtc.Tymd = ShellDll.TYMED.HGLOBAL; //note the hGlobal item in stg is a pointer to a pointer->Data IntPtr m_hg = Marshal.AllocHGlobal(Convert.ToInt32(m_StreamCIDA.Length)); Marshal.Copy(m_StreamCIDA.ToArray(), 0, m_hg, (int)m_StreamCIDA.Length); IntPtr hg = Marshal.AllocHGlobal(IntPtr.Size); Marshal.WriteIntPtr(hg, 0, m_hg); stg.tymed = (int)ShellDll.TYMED.HGLOBAL; stg.hGlobal = hg; stg.pUnkForRelease = IntPtr.Zero; HR = IDO.SetData(ref fmtEtc, ref stg, true); //callee responsible for releasing stg if (HR == 0) { m_IsValid = true; } else //failed -- we have to release stg -- and leave m_IsValid as False { ShellDll.ReleaseStgMedium(ref stg); return; //m_isvalid stays False } } m_IsValid = true; //already had a Shell IDList Array, so all is OK }
//Assumed to be a pointer to an IDataObject ///<Summary>This constructor takes a pointer to an IDataObject obtained from a /// IDropTarget Interface's DragEnter method. /// If the pointer points to a .Net DataObject (which only happens within the same app), /// convert it and call ProcessNetDataObject /// Otherwise, it from another app, possibly Win Explorer, so /// check it for the required formats and build m_DragList. /// Any error just quits, leaving m_IsValid as False ... Caller must check ///</Summary> public CProcDataObject(IntPtr pDataObj) { //save it off -- DragWrapper class won't use it // CDragWrapper class may, but should, in this version, use the original m_DataObject = pDataObj; bool HadError = false; //used for various error conditions try { IDO = Marshal.GetTypedObjectForIUnknown(pDataObj, typeof(ShellDll.IDataObject)) as ShellDll.IDataObject; } catch (Exception) { // Debug.WriteLine("Exception Thrown in CMyDataObject -Getting COM interface: " & vbCrLf & ex.ToString) HadError = true; } //If it is really a .Net IDataObject, then treat it as such if (HadError) { try { NetIDO = Marshal.GetTypedObjectForIUnknown(pDataObj, typeof(System.Windows.Forms.IDataObject)) as System.Windows.Forms.IDataObject; IsNet = true; } catch { IsNet = false; } } if (IsNet) { //Any error in ProcessNetDataObject will leave m_IsValid as False -- our only Error Indicator ProcessNetDataObject(NetIDO); } else //IDataObject not from Net, Do it the hard way { if (HadError) { return; //can do no more } ProcessCOMIDataObject(IDO); //It either worked or not. m_IsValid is set accordingly, so we are done } }