Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# call WinApi?

I am trying to call a WinAPI function DeviceIoControl in C# with code IOCTL_DISK_SET_DISK_ATTRIBUTES and pass struct SET_DISK_ATTRIBUTES. I am trying do it with this code:

const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000;
const int FILE_SHARE_READ = 0x1;
const int FILE_SHARE_WRITE = 0x2;

const uint IOCTL_DISK_SET_DISK_ATTRIBUTES = 0x0007c0f4;
const ulong DISK_ATTRIBUTE_READ_ONLY = 0x0000000000000002;

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(
    string lpFileName,
    uint dwDesiredAccess,
    uint dwShareMode,
    IntPtr SecurityAttributes,
    uint dwCreationDisposition,
    uint dwFlagsAndAttributes,
    IntPtr hTemplateFile
);

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

struct SET_DISK_ATTRIBUTES
{
    public uint Version;
    public bool Persist;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] Reserved1;
    public ulong Attributes;
    public ulong AttributesMask;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public uint[] Reserved2;
};

private bool SetReadonly(IntPtr handle)
{
    var sda = new SET_DISK_ATTRIBUTES();
    sda.AttributesMask = DISK_ATTRIBUTE_READ_ONLY;
    sda.Attributes = DISK_ATTRIBUTE_READ_ONLY;

    int nPtrQryBytes = Marshal.SizeOf(sda);
    sda.Version = (uint)nPtrQryBytes;

    IntPtr ptrQuery = Marshal.AllocHGlobal(nPtrQryBytes);
    Marshal.StructureToPtr(sda, ptrQuery, false);

    uint byteReturned;
    var res = DeviceIoControl(handle, IOCTL_DISK_SET_DISK_ATTRIBUTES, ptrQuery, (uint)nPtrQryBytes, IntPtr.Zero, 0, out byteReturned, IntPtr.Zero);

    var ex = new Win32Exception(Marshal.GetLastWin32Error());
    MessageBox.Show(ex.Message);

    return res;
}

I receive error "Parameter incorrect". What is the right way to call DeviceIoControl function passing structure SET_DISK_ATTRIBUTES?

like image 560
IceCreamVan Avatar asked Jun 22 '26 12:06

IceCreamVan


1 Answers

The original definition of SET_DISK_ATTRIBUTES:

typedef struct _SET_DISK_ATTRIBUTES {
  DWORD     Version;
  BOOLEAN   Persist;
  BYTE      Reserved1[3];
  DWORDLONG Attributes;
  DWORDLONG AttributesMask;
  DWORD     Reserved2[4];
} SET_DISK_ATTRIBUTES, *PSET_DISK_ATTRIBUTES;

makes use of BOOLEAN data type, which is defined as a synonym of unsigned char (1 byte), as opposed to BOOL that is a synonym of int (4 bytes).

C#'s bool is marshaled as BOOL by default.
You need to force it into one byte:

{
    ...
    [MarshalAs(UnmanagedType.I1)]
    public bool Persist;
    ...
}
like image 133
GSerg Avatar answered Jun 24 '26 01:06

GSerg



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!