{"id":14333,"date":"2010-04-14T07:00:00","date_gmt":"2010-04-14T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2010\/04\/14\/when-you-create-an-object-with-constraints-you-have-to-make-sure-everybody-who-uses-the-object-understands-those-constraints\/"},"modified":"2010-04-14T07:00:00","modified_gmt":"2010-04-14T07:00:00","slug":"when-you-create-an-object-with-constraints-you-have-to-make-sure-everybody-who-uses-the-object-understands-those-constraints","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20100414-00\/?p=14333","title":{"rendered":"When you create an object with constraints, you have to make sure everybody who uses the object understands those constraints"},"content":{"rendered":"<p>\nHere&#8217;s a question that came from a customer.\nThis particular example involves managed code,\nbut don&#8217;t let that distract you from the point of the exercise.\n<\/p>\n<blockquote CLASS=\"q\">\n<p>\nI am trying to create a <code>FileStream<\/code>\nobject using the constructor that takes an <code>IntPtr<\/code> as input.\nIn my .cs file,\nI create the native file handle using <code>CreateFile<\/code>,\nas shown below.\n<\/p>\n<pre>\n[DllImport(\"kernel32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\ninternal static extern IntPtr CreateFile(string lpFileName,\n    int dwDesiredAccess, FileShare dwShareMode,\n    IntPtr securityAttrs, FileMode dwCreationDisposition,\n    UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile);\nIntPtr ptr1 = Win32Native.CreateFile(FileName, 0x40000000,\n         System.IO.FileShare.Read | System.IO.FileShare.Write,\n         Win32Native.NULL,\n         System.IO.FileMode.Create,\n         0xa0000000, \/\/ FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH\n         Win32Native.NULL);\n<\/pre>\n<p>\nThen I create the <code>File&shy;Stream<\/code> object as so:\n<\/p>\n<pre>\nFileStream fs = new FileStream(ptr1, FileAccess.Write, true, 1, false);\n<\/pre>\n<p>\nThe <code>fs<\/code> gets created fine. But when I try to do:\n<\/p>\n<pre>\nfs.Write(msg, 0, msg.Length);\nfs.Flush();\n<\/pre>\n<p>\nit fails with the error\n&#8220;IO operation will not work.\nMost likely the file will become too long\nor the handle was not opened to support synchronous IO operations.&#8221;\n<\/p>\n<pre>\nint hr = System.Runtime.InteropServices.Marshal.GetHRForException(e)\n<\/pre>\n<p>\nGives <code>hr<\/code> as <code>COR_E_IO<\/code> (<code>0x80131620<\/code>).\n<\/p>\n<p>\nThe stack trace is as below.\n<\/p>\n<pre>\nSystem.IO.IOException: IO operation will not work. Most likely\n    the file will become too long or the handle was not opened\n    to support synchronous IO operations.\nat System.IO.FileStream.WriteCore(Byte[] buffer, Int32 offset, Int32 count)\nat System.IO.FileStream.FlushWrite()\nat System.IO.FileStream.Flush()\nat PInvoke.Program.Main(String[] args)\n<\/pre>\n<p>\nCan somebody point out what might be going wrong?\n<\/p>\n<\/blockquote>\n<p>\n(For those who would prefer to cover their ears and hum when\nthe topic of managed code arises,\nchange <code>FileStream<\/code> to\n<code>fdopen<\/code>.)\n<\/p>\n<p>\nThe comment on the line\n<\/p>\n<pre>\n         0xa0000000, \/\/ FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH\n<\/pre>\n<p>\nwas provided by the customer, and that&#8217;s the key to the problem.\nIt was right there in the comment, but the customer didn&#8217;t understand\nthe consequences.\n<\/p>\n<p>\nAs the documentation for <code>Create&shy;File<\/code> notes,\nthe <code>FILE_FLAG_NO_BUFFERING<\/code>\nflag requires that all I\/O operations on the file handle\nbe in multiples of the sector size, and that the I\/O buffers also\nbe aligned on addresses which are multiples of the sector size.\n<\/p>\n<p>\nSince you created the file handle with very specific\nrules for usage, you have to make sure that everybody who\nuses it actually follows those rules.\nOn the other hand,\nthe <code>File&shy;Stream<\/code> object doesn&#8217;t know about these rules.\nIt just figures you gave it a handle that it can issue normal\nsynchronous <code>Read&shy;File<\/code> and <code>Write&shy;File<\/code>\ncalls on.\nIt doesn&#8217;t know that you gave it a handle that requires\nspecial treatment.\nAnd then the attempt to write to the handle with a plain\n<code>Write&shy;File<\/code> fails both because the number\nof bytes is not a multiple of the sector size and because\nthe I\/O buffer is not sector-aligned, and you get the\nI\/O exception.\n<\/p>\n<p>\nThe solution to this problem depends on what you are trying to accomplish.\nWhy are you passing the\n<code>FILE_FLAG_NO_BUFFERING |\nFILE_FLAG_WRITE_THROUGH<\/code> flags?\nAre you doing this just because you overheard in the hallway that it&#8217;s\nfaster?\nWell, yes it may be faster under the right circumstances,\nbut in exchange for the increased performance, you also have to follow\na much stricter set of rules.\nAnd in the absence of documentation to the contrary,\nyou can&#8217;t assume that a chunk of code actually adheres to your\nvery special rules.\n<\/p>\n<p>\nLike <i>What if two people did this?<\/i>,\nthis is an illustration of\nanother principle that many people forget to consider\nwhen working with objects they didn&#8217;t write:\n<i>When you write your own code, do you do this?<\/i>\nIt&#8217;s sort of like the Golden Rule of programming.\n<\/p>\n<p>\nSuppose you have a function which accepts a file handle\nand whose job is to write some data do that file handle.\nDo you write your function so that it performs all its\nI\/O in multiples of the sector size from buffers which are\naligned in memory in multiples of the sector size,\non the off chance that somebody gave you a handle that was\nopened with the <code>FILE_FLAG_NO_BUFFERING<\/code> flag?\nWell, no, you don&#8217;t.\nYou just call <code>Write&shy;File<\/code> to write to it,\nand if you want to write 28 bytes, you write 28 bytes.\nEven if you perform internal buffering and your buffer size\nhappens to be a multiple of the sector size by accident,\nyou still don&#8217;t align your I\/O buffer to the sector size;\nand when it&#8217;s time to flush the final partially-written buffer,\nyou have a not-sector-multiple write at the very end anyway.\n<\/p>\n<p>\nIf you don&#8217;t handle this case in your code,\nwhy would you expect others to handle it in their code?\n<\/p>\n<p>\nWe&#8217;ve seen this principle before,\nsuch as when we looked at\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2008\/11\/28\/9148951.aspx\">\nwhether the <code>Process.Refresh<\/code> method\nrefreshes an arbitrary application&#8217;s windows<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here&#8217;s a question that came from a customer. This particular example involves managed code, but don&#8217;t let that distract you from the point of the exercise. I am trying to create a FileStream object using the constructor that takes an IntPtr as input. In my .cs file, I create the native file handle using CreateFile, [&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-14333","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Here&#8217;s a question that came from a customer. This particular example involves managed code, but don&#8217;t let that distract you from the point of the exercise. I am trying to create a FileStream object using the constructor that takes an IntPtr as input. In my .cs file, I create the native file handle using CreateFile, [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/14333","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=14333"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/14333\/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=14333"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=14333"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=14333"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}