{"id":783,"date":"2014-06-09T07:00:00","date_gmt":"2014-06-09T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2014\/06\/09\/improving-the-performance-of-cf_hdrop-by-providing-file-attribute-information\/"},"modified":"2014-06-09T07:00:00","modified_gmt":"2014-06-09T07:00:00","slug":"improving-the-performance-of-cf_hdrop-by-providing-file-attribute-information","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20140609-00\/?p=783","title":{"rendered":"Improving the performance of CF_HDROP by providing file attribute information"},"content":{"rendered":"<p>\nThe <code>CF_HDROP<\/code> clipboard format is still quite popular,\ndespite its limitation of being limited to files.\nYou can&#8217;t use it to represent\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2008\/03\/18\/8080183.aspx\">\nvirtual content<\/a>,\nfor example.\n<\/p>\n<p>\nFor all of you still using\n<code>CF_HDROP<\/code>,\nyou can improve the performance of drag\/drop operations by\nadding a little more information to your data object.\n<\/p>\n<p>\nObserve that the\n<code>CF_HDROP<\/code>\nclipboard format is just a list of paths.\nSome drop targets care about whether the paths refer to directories\nor to files,\nand since <code>CF_HDROP<\/code> does not provide this information,\nthe drop targets are forced to access the disk to get the answer.\n(This can be expensive for network locations.)\n<\/p>\n<p>\nTo help this case, you can add a\n<code>CFSTR_FILE_ATTRIBUTES_ARRAY<\/code> to your\ndata object.\nThis contains the file attribute information for the items in your\n<code>CF_HDROP<\/code>,\nthereby saving the drop target the cost of having to go find them.\n<\/p>\n<p>\nTake our\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2008\/03\/11\/8080077.aspx\">\ntiny drag-drop sample<\/a>\nand make the following changes:\n<\/p>\n<pre>\nclass CTinyDataObject : public IDataObject\n{\n  ...\n  enum {\n    <font COLOR=\"red\">\/\/ <strike>DATA_TEXT,<\/strike><\/font>\n    <font COLOR=\"blue\">DATA_HDROP,<\/font>\n    DATA_NUM,\n    DATA_INVALID = -1,\n  };\n  ...\n};\nCTinyDataObject::CTinyDataObject() : m_cRef(1)\n{\n  SetFORMATETC(&amp;m_rgfe[DATA_<font COLOR=\"blue\">HDROP<\/font>], CF_<font COLOR=\"blue\">HDROP<\/font>);\n}\n<font COLOR=\"blue\">struct STATICDROPFILES\n{\n DROPFILES df;\n TCHAR szFile[ARRAYSIZE(TEXT(\"C:\\\\Something.txt\\0\"))];\n} const c_hdrop = {\n  {\n    FIELD_OFFSET(STATICDROPFILES, szFile),\n    { 0, 0 },\n    FALSE,\n    sizeof(TCHAR) == sizeof(WCHAR), \/\/ fUnicode\n  },\n  TEXT(\"C:\\\\Something.txt\\0\"),\n};<\/font>\nHRESULT CTinyDataObject::GetData(FORMATETC *pfe, STGMEDIUM *pmed)\n{\n  ZeroMemory(pmed, sizeof(*pmed));\n  switch (GetDataIndex(pfe)) {\n  case <font COLOR=\"blue\">DATA_HDROP<\/font>:\n    pmed-&gt;tymed = TYMED_HGLOBAL;\n    return CreateHGlobalFromBlob(&amp;<font COLOR=\"blue\">&amp;c_hdrop, sizeof(c_hdrop),<\/font>\n                              GMEM_MOVEABLE, &amp;pmed-&gt;hGlobal);\n  }\n  return DV_E_FORMATETC;\n}\n<\/pre>\n<p>\nOkay, let&#8217;s look at what we did here.\n<\/p>\n<p>\nFirst, we make our data object report a\n<code>CF_HDROP<\/code>.\nWe then declare a static <code>DROP&shy;FILES<\/code> structure\nwhich we use for all of our drag-drop operations.\n(Of course, in real life, you would generate it dynamically,\nbut this is just a Little Program.)\n<\/p>\n<p>\nThat&#8217;s our basic program that drags a file.\n<\/p>\n<p>\nNote that\n<\/p>\n<blockquote CLASS=\"q\"><p>\nyou are much better off letting the shell create the data object,\n<\/p><\/blockquote>\n<p>\nsince that data object will contain much richer information\n(and this entire article would not be needed).\nHere&#8217;s a sample program which\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2004\/12\/06\/275659.aspx\">\nuses the <code>Get&shy;UI&shy;Object&shy;Of&shy;File<\/code> function\nto do this in just a few lines<\/a>.\nIt&#8217;s much shorter than having to cook up this\n<code>CTiny&shy;Data&shy;Object<\/code> class.\nI&#8217;m doing it this way on the assumption that your program\nis deeply invested in the less flexible <code>CF_HDROP<\/code>\nformat,\nso changing from <code>CF_HDROP<\/code> to some other format\nwould be impractical.\n<\/p>\n<p>\nOkay, so that&#8217;s the program we&#8217;re starting from.\nLet&#8217;s add support for precomputed attributes.\n<\/p>\n<pre>\nclass CTinyDataObject : public IDataObject\n{\n  ...\n  enum {\n    DATA_HDROP,\n    <font COLOR=\"blue\">DATA_ATTRIBUTES,<\/font>\n    DATA_NUM,\n    DATA_INVALID = -1,\n  };\n  ...\n};\nCTinyDataObject::CTinyDataObject() : m_cRef(1)\n{\n  SetFORMATETC(&amp;m_rgfe[DATA_HDROP], CF_HDROP);\n  <font COLOR=\"blue\">SetFORMATETC(&amp;m_rgfe[DATA_ATTRIBUTES],\n               RegisterClipboardFormat(CFSTR_FILE_ATTRIBUTES_ARRAY));<\/font>\n}\n<font COLOR=\"blue\">FILE_ATTRIBUTES_ARRAY c_attr = {\n 1, \/\/ cItems\n FILE_ATTRIBUTE_ARCHIVE, \/\/ OR of attributes\n FILE_ATTRIBUTE_ARCHIVE, \/\/ AND of attributes\n { FILE_ATTRIBUTE_ARCHIVE }, \/\/ the file attributes\n};<\/font>\nHRESULT CTinyDataObject::GetData(FORMATETC *pfe, STGMEDIUM *pmed)\n{\n  ZeroMemory(pmed, sizeof(*pmed));\n  switch (GetDataIndex(pfe)) {\n  case DATA_HDROP:\n    pmed-&gt;tymed = TYMED_HGLOBAL;\n    return CreateHGlobalFromBlob(&amp;amp;c_hdrop, sizeof(c_hdrop),\n                              GMEM_MOVEABLE, &amp;pmed-&gt;hGlobal);\n  <font COLOR=\"blue\">case DATA_ATTRIBUTES:\n    pmed-&gt;tymed = TYMED_HGLOBAL;\n    return CreateHGlobalFromBlob(&amp;c_attr1, sizeof(c_attr1),\n                              GMEM_MOVEABLE, &amp;pmed-&gt;hGlobal);<\/font>\n  }\n  return DV_E_FORMATETC;\n}\n<\/pre>\n<p>\nOkay, let&#8217;s look at what we did here.\n<\/p>\n<p>\nWe added a new data format,\n<code>CFSTR_FILE_ATTRIBUTES_ARRAY<\/code>,\nand we created a static copy of the\n<code>FILE_ATTRIBUTES_ARRAY<\/code>\nvariable-length structure that contains the attributes of our\none file.\nOf course, in a real program, you would generate the structure\ndynamically.\nNote that I use a sneaky trick here:\nSince the\n<code>FILE_ATTRIBUTES_ARRAY<\/code>\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2004\/08\/26\/220873.aspx\">\nends with an array of length 1<\/a>,\nand I happen to need exactly one item,\nI can just declare the structure as-is and fill in the one slot.\n(If I had more than one item, then I would have needed more typing.)\n<\/p>\n<p>\nTo make things easier for the consumers of the\n<code>FILE_ATTRIBUTES_ARRAY<\/code>,\nthe structure also asks you to report the logical OR and logical AND\nof all the file attributes.\nThis is to allow quick answers to questions like\n&#8220;Is everything in this <code>CF_DROP<\/code> a file?&#8221;\nor\n&#8220;Is anything in this <code>CF_DROP<\/code> write-protected?&#8221;\nSince we have only one file, the calculation of these OR and AND\nvalues is nearly trivial.\n<\/p>\n<p>\nOkay, so there isn&#8217;t much benefit to adding file attributes\nto a drag of a single file from the local hard drive,\nsince the local hard drive is pretty fast,\nand the file attributes may very well be cached.\nBut if you&#8217;ve placed thousands of files from a network drive\nonto the clipboard,\nthis shortcut can save a lot of time.\n(That was in fact the customer problem that inspired this Little Program.)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The CF_HDROP clipboard format is still quite popular, despite its limitation of being limited to files. You can&#8217;t use it to represent virtual content, for example. For all of you still using CF_HDROP, you can improve the performance of drag\/drop operations by adding a little more information to your data object. Observe that the CF_HDROP [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-783","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The CF_HDROP clipboard format is still quite popular, despite its limitation of being limited to files. You can&#8217;t use it to represent virtual content, for example. For all of you still using CF_HDROP, you can improve the performance of drag\/drop operations by adding a little more information to your data object. Observe that the CF_HDROP [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/783","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=783"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/783\/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=783"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=783"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=783"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}