C#调用WinApi?
Posted
技术标签:
【中文标题】C#调用WinApi?【英文标题】:C# call 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 Persist
是single byte (typedef BYTE BOOLEAN;
),而不是像C# 的bool
或WinAPI 的BOOL
这样的四个字节。使用MarshalAs(UnmanagedType.I1)
。
@RemyLebeau 是applied by default。
【参考方案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 个字节)的同义词,而不是BOOL
是int
(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));
【讨论】:
以上是关于C#调用WinApi?的主要内容,如果未能解决你的问题,请参考以下文章