【问题标题】:C# call WinApi?C#调用WinApi?
【发布时间】:2018-12-22 19:48:18
【问题描述】:

我正在尝试使用代码 IOCTL_DISK_SET_DISK_ATTRIBUTES 在 C# 中调用 WinAPI 函数 DeviceIoControl 并传递结构 SET_DISK_ATTRIBUTES。我正在尝试使用此代码:

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

我收到错误“参数不正确”。调用DeviceIoControl函数传递结构SET_DISK_ATTRIBUTES的正确方法是什么?

【问题讨论】:

  • 当您将DeviceIoControl 声明为(IntPtr hDevice, uint dwIoControlCode, [In] ref SET_DISK_ATTRIBUTES lpInBuffer, ...) 并仅传递结构时会发生同样的情况吗?
  • 是的 "ref sda" 给出相同的结果
  • 您可能还应该在问题中包含所用常量的值。
  • 啊,显然BOOLEAN Persistsingle byte (typedef BYTE BOOLEAN;),而不是像C# 的bool 或WinAPI 的BOOL 这样的四个字节。使用MarshalAs(UnmanagedType.I1)
  • @RemyLebeau 是applied by default

标签: c# windows winapi interop


【解决方案1】:

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;

使用BOOLEAN 数据类型,即defined 作为unsigned char(1 个字节)的同义词,而不是BOOLint(4 个字节)的同义词。

默认情况下,C# 的 bool 被封送为 BOOL
你需要force it into one byte:

{
    ...
    [MarshalAs(UnmanagedType.I1)]
    public bool Persist;
    ...
}

【讨论】:

    【解决方案2】:

    最后是设置磁盘只读的代码

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-31
      • 2013-03-22
      • 2013-03-19
      • 1970-01-01
      相关资源
      最近更新 更多