C# call WinApi?

≡放荡痞女 提交于 2020-01-05 03:54:05

问题


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?


回答1:


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;
    ...
}



回答2:


Finally code to set disk readonly

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

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

const uint IOCTL_DISK_UPDATE_PROPERTIES = 459072;

[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;
   [MarshalAs(UnmanagedType.I1)]
   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;
};

public IntPtr CreateHandle(string driveLetter)
{
   string filename = @"\\.\" + driveLetter[0] + ":";
   return CreateFile(filename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, 0x3, FILE_FLAG_WRITE_THROUGH, IntPtr.Zero);
}

private void SetReadonly(IntPtr handle)
{
   var sda = new SET_DISK_ATTRIBUTES();
   sda.Persist = true;
   sda.AttributesMask = DISK_ATTRIBUTE_READ_ONLY;
   sda.Attributes = DISK_ATTRIBUTE_READ_ONLY;
   sda.Reserved1 = new byte[3] { 0, 0, 0 };
   sda.Reserved2 = new uint[4] { 0, 0, 0, 0 };

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

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

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

   var mess = new List<string>();
   mess.Add(new Win32Exception(Marshal.GetLastWin32Error()).Message);
   mess.Add(new Win32Exception(Marshal.GetLastWin32Error()).Message);

   MessageBox.Show(string.Join(" ", mess));
}

private void button1_Click(object sender, EventArgs e)
{
   SetReadonly(CreateHandle(textBox1.Text));
}


来源:https://stackoverflow.com/questions/53898839/c-sharp-call-winapi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!