调用OpenVirtualDisk()返回错误码2“文件未找到”的技术求助
踩坑记录:C#调用Virtual Disk API挂载ISO失败的解决过程
刚从Borland Pascal转C#,正在做一个基于Windows Imaging API的恢复套件,要在WinPE(Windows 10 + .NET 5.4.2)环境里挂载ISO文件,结果调用OpenVirtualDisk()的时候一直翻车——明明文件存在,程序也以管理员权限运行,要么返回错误码87(无效参数,GetLastWin32Error返回1008“无效令牌”),要么返回错误码2(文件未找到)。试了FileIOPermission给全权限也没用,差点头大。
先贴下我一开始的错误代码:
原DllImport声明
[DllImport("VirtDisk.dll", EntryPoint = "OpenVirtualDisk", CharSet = CharSet.Ansi, ThrowOnUnmappableChar = true ,SetLastError = true)] public extern static Int64 OpenVirtualDisk( [In] ref _VIRTUAL_STORAGE_TYPE PVIRTUAL_STORAGE_TYPE, string Path, _VIRTUAL_DISK_ACCESS_MASK LVIRTUAL_DISK_ACCESS_MASK, long OPEN_VIRTUAL_DISK_FLAG, [In, Optional] ref OPEN_VIRTUAL_DISK_PARAMTERS POPEN_VIRTUAL_DISK_PARAMTERS, ref IntPtr pHandle);
原调用逻辑
private void button1_Click(object sender, EventArgs e) { IntPtr pHandle = IntPtr.Zero; Int64 RetVal = 0; string VirtualDiskPath = @"F:\Images\Windows10.ISO"; _VIRTUAL_DISK_ACCESS_MASK VIRTUAL_DISK_ACCESS; Int64 VIRTUAL_DISK_FLAG; _VIRTUAL_STORAGE_TYPE VIRTUAL_STORAGE_TYPE; if (File.Exists(VirtualDiskPath)) { VIRTUAL_DISK_ACCESS = _VIRTUAL_DISK_ACCESS_MASK.VIRTUAL_DISK_ACCESS_ATTACH_ALL; VIRTUAL_DISK_FLAG = OPEN_VIRTUAL_DISK_FLAG_NONE; VIRTUAL_STORAGE_TYPE.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_ISO; VIRTUAL_STORAGE_TYPE.VendorId = VENDORMICROSOFT; IntPtr VirtualStorPtr = IntPtr.Zero; VirtualStorPtr = Marshal.AllocHGlobal(Marshal.SizeOf(VIRTUAL_STORAGE_TYPE)); Marshal.StructureToPtr(VIRTUAL_STORAGE_TYPE, VirtualStorPtr, true); IntPtr VirtualDiskVer = IntPtr.Zero; OPEN_VIRTUAL_DISK_PARAMTERS OPEN_VIRTUAL_DISK_PARAM; OPEN_VIRTUAL_DISK_PARAM = new OPEN_VIRTUAL_DISK_PARAMTERS { version = OPEN_VIRTUAL_DISK_VERSION.OPEN_VIRTUAL_DISK_VERSION_1, version1 = new OPEN_VIRTUAL_DISK_PARAMTERS1 { RWDepth = 0 } }; VirtualDiskVer = Marshal.AllocHGlobal(Marshal.SizeOf(OPEN_VIRTUAL_DISK_PARAM)); Marshal.StructureToPtr(OPEN_VIRTUAL_DISK_PARAM, VirtualDiskVer, true); RetVal = OpenVirtualDisk(ref VIRTUAL_STORAGE_TYPE, VirtualDiskPath, VIRTUAL_DISK_ACCESS, VIRTUAL_DISK_FLAG, ref OPEN_VIRTUAL_DISK_PARAM, ref pHandle); if ((RetVal == 0)) { MessageBox.Show("OpenVirtualDisk() has succeded in opening the file: \r\n" + VirtualDiskPath + "\r\nWith the file Handle: " + pHandle.ToString()); } else { MessageBox.Show("OpenVirtualDisk() failed to open the file: " + VirtualDiskPath + ",\r\nWith Error Code: " + Marshal.GetLastWin32Error().ToString() + "\r\nReturn Value: " + RetVal.ToString()); } Marshal.FreeHGlobal(VirtualStorPtr); } }
最终解决方法
折腾了半天,发现问题出在两个地方:
权限掩码不对:我一开始用了
VIRTUAL_DISK_ACCESS_ATTACH_ALL,但ISO是只读镜像,根本不需要这么高的权限,改成VIRTUAL_DISK_ACCESS_READ(对应FILE_ACCESS_MASK_RO)就解决了参数无效的问题。DllImport的字符集和参数标记错了:
- Windows API的路径默认是Unicode的,我之前设了
CharSet.Ansi,导致路径解析错误,改成CharSet.Unicode就解决了“文件未找到”的问题。 - 给几个参数加上
[In]特性,明确传递方向,避免封送的时候出问题。 - 顺便把dll名改成小写的
virtdisk.dll(其实大小写不影响,但统一规范点)。
- Windows API的路径默认是Unicode的,我之前设了
修改后的DllImport如下:
[DllImport("virtdisk.dll", CharSet = CharSet.UNICODE, ThrowOnUnmappableChar = true, SetLastError = true)] public extern static Int64 OpenVirtualDisk( [In] ref _VIRTUAL_STORAGE_TYPE PVIRTUAL_STORAGE_TYPE, string Path, [In] _VIRTUAL_DISK_ACCESS_MASK LVIRTUAL_DISK_ACCESS_MASK, [In] long OPEN_VIRTUAL_DISK_FLAG, [In, Optional] ref OPEN_VIRTUAL_DISK_PARAMTERS POPEN_VIRTUAL_DISK_PARAMTERS, [In] ref IntPtr pHandle);
改完之后再调用就成功挂载ISO了,希望同样转C#的Pascal老鸟或者WinPE开发的朋友能少踩这个坑~
内容的提问来源于stack exchange,提问作者R.McInnes-Piper
相关产品推荐
相关产品推荐

