DeviceIoControl with FSCTL_SET_ZERO_DATA returns ERROR_INVALID_PARAMETER

1

I am trying to use DeviceIoControl with FSCTL_SET_ZERO_DATA control code in C#.

I created the file C:\tmp\test.txt and inside it I put the text "aaaa".

I ran the code and I received error number: 87
According to MSDN: "The parameter is incorrect."

enter image description here

I supposed that the problem is because I am sending input buffer (parameter number 3 in DeviceIoControl) without any data.

The DeviceIoControl returns false.
How can I insert valid parameters to DeviceIoControl with FSCTL_SET_ZERO_DATA which will make it return true?

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program2
    {
        [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
 IntPtr lpInBuffer, uint nInBufferSize,
 IntPtr lpOutBuffer, uint nOutBufferSize,
 out uint lpBytesReturned, IntPtr lpOverlapped);

        [Flags]
        public enum EMethod : uint
        {
            Buffered = 0,
            InDirect = 1,
            OutDirect = 2,
            Neither = 3
        }

        [Flags]
        public enum EFileDevice : uint
        {
            FileSystem = 0x00000009
        }

        [Flags]
        public enum EIOControlCode : uint
        {
            FsctlSetZeroData = (EFileDevice.FileSystem << 16) | (50 << 2) | EMethod.Buffered | (FileAccess.Write << 14),
        }

        [StructLayout(LayoutKind.Sequential)]
        struct FILE_ZERO_DATA_INFORMATION
        {
            public FILE_ZERO_DATA_INFORMATION(long offset, long count)
            {
                FileOffset = offset;
                BeyondFinalZero = offset + count;
            }

            public long FileOffset;
            public long BeyondFinalZero;
        }

        [DllImport("kernel32.dll")]
        public static extern uint GetLastError();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr CreateFile(
        [MarshalAs(UnmanagedType.LPTStr)] string filename,
        [MarshalAs(UnmanagedType.U4)] FileAccess access,
        [MarshalAs(UnmanagedType.U4)] FileShare share,
        IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
        [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
        uint flagsAndAttributes,
        IntPtr templateFile);

        static void Main(string[] args)
        {
            IntPtr handle = CreateFile(@"C:\tmp\test.txt", FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, (int)FileAttributes.Normal, IntPtr.Zero);

            long length = 2;
            FILE_ZERO_DATA_INFORMATION data = new FILE_ZERO_DATA_INFORMATION(0, length);
            uint structSize = (uint)Marshal.SizeOf(data);
            IntPtr pBuffer = Marshal.AllocHGlobal((int)structSize);


            uint bytesReturned = 0;
            bool succeed = DeviceIoControl(handle, (uint)EIOControlCode.FsctlSetZeroData, pBuffer, 5, IntPtr.Zero, 5, out bytesReturned, IntPtr.Zero);

            // bool succeed = DeviceIoControl(handle, (uint)EIOControlCode.FsctlSetZeroData, IntPtr.Zero, 0, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
            if (succeed)
            {
                Console.WriteLine("Works fine");
            }
            else
            {
                uint err = GetLastError();
                Console.WriteLine("Error number: {0}", err);
            }
        }

    }
}
c#
windows
deviceiocontrol
asked on Stack Overflow Feb 23, 2017 by E235 • edited Feb 24, 2017 by aybe

1 Answer

1

Credit to @Aybe.

The fix is:

uint pBufferSize = 16; 
Marshal.StructureToPtr(data, pBuffer, false);
bool succeed = DeviceIoControl(handle, (uint)EIOControlCode.FsctlSetZeroData, pBuffer, pBufferSize, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);  
  • Very important to add Marshal.StructureToPtr(data, pBuffer, false);

If you won't add it, sometimes the function will return true and sometime false.

Here is a reference to code that also uses FSCTL with C#.

answered on Stack Overflow Feb 23, 2017 by E235 • edited Feb 24, 2017 by E235

User contributions licensed under CC BY-SA 3.0