{"id":37133,"date":"2004-12-06T07:00:00","date_gmt":"2004-12-06T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2004\/12\/06\/dragging-a-shell-object-part-1-getting-the-idataobject\/"},"modified":"2022-08-02T07:03:39","modified_gmt":"2022-08-02T14:03:39","slug":"20041206-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20041206-00\/?p=37133","title":{"rendered":"Dragging a shell object, part 1: Getting the IDataObject"},"content":{"rendered":"<p>The shell gives you the <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/objidl\/nn-objidl-idataobject\"> <code>IDataObject<\/code><\/a>; all you have to do is drag it around. (This is the first of a five-part series.)<\/p>\n<p>Start with <a title=\"The scratch program\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20030723-00\/?p=43073\"> the scratch program<\/a>, and add <a title=\"How to host an IContextMenu, part 1 - Initial foray\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040920-00\/?p=37823\"> the function <code>GetUIObjectOfFile<\/code> from an earlier article<\/a>. Also, change the calls to <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/objbase\/nf-objbase-coinitialize\"> <code>CoInitialize<\/code><\/a> and <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/combaseapi\/nf-combaseapi-couninitialize\"> <code>CoUninitialize<\/code><\/a> to <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/ole2\/nf-ole2-oleinitialize\"> <code>OleInitialize<\/code><\/a> and <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/ole2\/nf-ole2-oleuninitialize\"> <code>OleUninitialize<\/code><\/a>, respectively, since we&#8217;re now going to be using full-on OLE and not just COM.<\/p>\n<p>In order to initiate a drag\/drop operation, we need a drop source:<\/p>\n<pre>class CDropSource : public IDropSource\r\n{\r\npublic:\r\n  \/\/ *** IUnknown ***\r\n  STDMETHODIMP QueryInterface(REFIID riid, void **ppv);\r\n  STDMETHODIMP_(ULONG) AddRef();\r\n  STDMETHODIMP_(ULONG) Release();\r\n\r\n  \/\/ *** IDropSource ***\r\n  STDMETHODIMP QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState);\r\n  STDMETHODIMP GiveFeedback(DWORD dwEffect);\r\n\r\n  CDropSource() : m_cRef(1) { }\r\nprivate:\r\n  ULONG m_cRef;\r\n};\r\n\r\nHRESULT CDropSource::QueryInterface(REFIID riid, void **ppv)\r\n{\r\n  IUnknown *punk = NULL;\r\n  if (riid == IID_IUnknown) {\r\n    punk = static_cast&lt;IUnknown*&gt;(this);\r\n  } else if (riid == IID_IDropSource) {\r\n    punk = static_cast&lt;IDropSource*&gt;(this);\r\n  }\r\n\r\n  *ppv = punk;\r\n  if (punk) {\r\n    punk-&gt;AddRef();\r\n    return S_OK;\r\n  } else {\r\n    return E_NOINTERFACE;\r\n  }\r\n}\r\n\r\nULONG CDropSource::AddRef()\r\n{\r\n  return ++m_cRef;\r\n}\r\n\r\nULONG CDropSource::Release()\r\n{\r\n  ULONG cRef = --m_cRef;\r\n  if (cRef == 0) delete this;\r\n  return cRef;\r\n}\r\n\r\nHRESULT CDropSource::QueryContinueDrag(\r\n          BOOL fEscapePressed, DWORD grfKeyState)\r\n{\r\n  if (fEscapePressed) return DRAGDROP_S_CANCEL;\r\n\r\n  \/\/ [Update: missing paren repaired, 7 Dec]\r\n  if (!(grfKeyState &amp; (MK_LBUTTON | MK_RBUTTON)))\r\n    return DRAGDROP_S_DROP;\r\n\r\n  return S_OK;\r\n}\r\n\r\nHRESULT CDropSource::GiveFeedback(DWORD dwEffect)\r\n{\r\n  return DRAGDROP_S_USEDEFAULTCURSORS;\r\n}\r\n<\/pre>\n<p>As you can see, this drop source is extraordinarily boring. Even the interesting methods are uninteresting.<\/p>\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/oleidl\/nf-oleidl-idropsource-querycontinuedrag\"> The <code>IDropSource::QueryContinueDrag<\/code> method<\/a> is pretty much boilerplate. If the Escape key was pressed, then cancel the drag\/drop operation. If the mouse buttons are released, then complete the operation. Otherwise, continue the operation.<\/p>\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/oleidl\/nf-oleidl-idropsource-givefeedback\"> The <code>IDropSource::GiveFeedback<\/code> method<\/a> is even less interesting. It merely returns <code>DRAGDROP_S_USEDEFAULTCURSORS<\/code> to indicate that it wants default drag feedback.<\/p>\n<p>Believe it or not, we now have everything we need to drag a file.<\/p>\n<pre>void OnLButtonDown(HWND hwnd, BOOL fDoubleClick,\r\n                   int x, int y, UINT keyFlags)\r\n{\r\n  IDataObject *pdto;\r\n  \/\/ In a real program of course\r\n  \/\/ you wouldn't use a hard-coded path.\r\n  \/\/ [comment added 11am because apparently some\r\n  \/\/ people thought this wasn't self-evident.]\r\n  if (SUCCEEDED(GetUIObjectOfFile(hwnd,\r\n                    L\"C:\\\\Windows\\\\clock.avi\",\r\n\t\t    IID_IDataObject, (void**)&amp;pdto))) {\r\n    IDropSource *pds = new CDropSource();\r\n    if (pds) {\r\n      DWORD dwEffect;\r\n      DoDragDrop(pdto, pds, DROPEFFECT_COPY | DROPEFFECT_LINK,\r\n                 &amp;dwEffect);\r\n      pds-&gt;Release();\r\n    }\r\n    pdto-&gt;Release();\r\n  }\r\n}\r\n\r\n    HANDLE_MSG(hwnd, WM_LBUTTONDOWN, OnLButtonDown);\r\n<\/pre>\n<p>To drag an object, you need two things, a data object and a drop source. We created our drop source above, and the data object comes from the shell. All that&#8217;s left to do is start the drag\/drop operation by calling <a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/api\/ole2\/nf-ole2-dodragdrop\"> the <code>DoDragDrop<\/code> function<\/a>.<\/p>\n<p>Notice that we specify that the permitted operations are <code>DROPEFFECT_COPY<\/code> and <code>DROPEFFECT_LINK<\/code>. We specifically disallow <code>DROPEFFECT_MOVE<\/code> because this program doesn&#8217;t present a folder-like window; the user has no expectation that the drag\/drop will result in a Move operation.<\/p>\n<p>Next time, adding Move support, just to see how it works.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The shell gives you the IDataObject; all you have to do is drag it around.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-37133","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The shell gives you the IDataObject; all you have to do is drag it around.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/37133","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=37133"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/37133\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=37133"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=37133"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=37133"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}