En Windows 10, al intentar utilizar la API CreateVirtualDisk para crear un disco virtual, falla y devuelve el código de error 87.
Ejemplo reproducible mínimo completo.
program Project3;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Winapi.Windows;
type
// Identifiers for virtual storage types and providers
VIRTUAL_STORAGE_TYPE = record
DeviceId: ULONG; // VIRTUAL_STORAGE_TYPE_DEVICE_xxx
VendorId: TGUID; // VIRTUAL_STORAGE_TYPE_VENDOR_xxx
end;
PVIRTUAL_STORAGE_TYPE = ^VIRTUAL_STORAGE_TYPE;
const
VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT: TGUID = '{EC984AEC-A0F9-47e9-901F-71415A66345B}';
VIRTUAL_STORAGE_TYPE_VENDOR_UNKNOWN: TGUID = '{00000000-0000-0000-0000-000000000000}';
type
// Version definitions
CREATE_VIRTUAL_DISK_VERSION = (
CREATE_VIRTUAL_DISK_VERSION_UNSPECIFIED = 0,
CREATE_VIRTUAL_DISK_VERSION_1 = 1
);
// Versioned CreateVirtualDisk parameter structure
CREATE_VIRTUAL_DISK_PARAMETERS_V1 = record
Version: CREATE_VIRTUAL_DISK_VERSION;
UniqueId: TGUID;
MaximumSize: ULONGLONG;
BlockSizeInBytes: ULONG;
SectorSizeInBytes: ULONG;
ParentPath: LPCWSTR;
SourcePath: LPCWSTR;
end;
PCREATE_VIRTUAL_DISK_PARAMETERS = Pointer;
const
VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN = 0; //Device type is unknown or not valid.
VIRTUAL_STORAGE_TYPE_DEVICE_ISO = 1; //CD or DVD image file device type. (.iso file) Windows 7 and Windows Server 2008 R2: This value is not supported before Windows 8 and Windows Server 2012.
VIRTUAL_STORAGE_TYPE_DEVICE_VHD = 2; //Virtual hard disk device type. (.vhd file)
VIRTUAL_STORAGE_TYPE_DEVICE_VHDX = 3; //VHDX format virtual hard disk device type. (.vhdx file) Windows 7 and Windows Server 2008 R2: This value is not supported before Windows 8 and Windows Server 2012.
type
VIRTUAL_DISK_ACCESS_MASK = (
VIRTUAL_DISK_ACCESS_NONE = $00000000,
VIRTUAL_DISK_ACCESS_ATTACH_RO = $00010000,
VIRTUAL_DISK_ACCESS_ATTACH_RW = $00020000,
VIRTUAL_DISK_ACCESS_DETACH = $00040000,
VIRTUAL_DISK_ACCESS_GET_INFO = $00080000,
VIRTUAL_DISK_ACCESS_CREATE = $00100000,
VIRTUAL_DISK_ACCESS_METAOPS = $00200000,
VIRTUAL_DISK_ACCESS_READ = $000d0000,
VIRTUAL_DISK_ACCESS_ALL = $003f0000,
VIRTUAL_DISK_ACCESS_WRITABLE = $00320000
);
// Flags for CreateVirtualDisk
CREATE_VIRTUAL_DISK_FLAG = (
CREATE_VIRTUAL_DISK_FLAG_NONE = $00000000, // i.e. dynamically expanding disk
CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION = $00000001 // Pre-allocate all physical space necessary for the virtual size of the disk (e.g. a fixed VHD).
);
function CreateVirtualDisk(
{in} VirtualStorageType: PVIRTUAL_STORAGE_TYPE;
{in} Path: PWideChar;
{in} VirtualDiskAccessMask: VIRTUAL_DISK_ACCESS_MASK;
{in_opt} SecurityDescriptor: PSECURITY_DESCRIPTOR;
{in} Flags: CREATE_VIRTUAL_DISK_FLAG;
{in} ProviderSpecificFlags: ULONG;
{in} Parameters: PCREATE_VIRTUAL_DISK_PARAMETERS;
{in_opt} Overlapped: POverlapped;
out Handle: THandle
): DWORD; stdcall; external 'VirtDisk.dll';
procedure CreateVhd(Path: UnicodeString; FileSizeBytes: Int64);
var
storageType: VIRTUAL_STORAGE_TYPE;
parameters: CREATE_VIRTUAL_DISK_PARAMETERS_V1;
vhdHandle: THandle;
res: DWORD;
begin
// Specify UNKNOWN for both device and vendor so the system will use the file extension to determine the correct VHD format.
storageType.DeviceId := VIRTUAL_STORAGE_TYPE_DEVICE_VHD; //VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN;
storageType.VendorId := VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT; //VIRTUAL_STORAGE_TYPE_VENDOR_UNKNOWN;
parameters := Default(CREATE_VIRTUAL_DISK_PARAMETERS_V1);
parameters.Version := CREATE_VIRTUAL_DISK_VERSION_1;
parameters.UniqueId := TGuid.NewGuid;
parameters.MaximumSize := FileSizeBytes;
parameters.BlockSizeInBytes := 0; //CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_BLOCK_SIZE;
parameters.SectorSizeInBytes := 512; //CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_SECTOR_SIZE;
parameters.ParentPath := nil;
parameters.SourcePath := nil;
res := CreateVirtualDisk(
@storageType,
PWideChar(Path),
VIRTUAL_DISK_ACCESS_NONE,
nil, // default security descriptor
CREATE_VIRTUAL_DISK_FLAG_NONE, // dynamically expanding disk
0,
@parameters,
nil, //not overlapped
{out}vhdHandle);
if res <> ERROR_SUCCESS then
begin
RaiseLastOSError(res);
Exit;
end;
CloseHandle(vhdHandle);
end;
begin
try
CreateVhd('C:\test.vhd', 15*1024*1024); //15 MB
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
WriteLn('Press enter to close...');
ReadLn;
end.
Obviamente, correr como administrador no hace ninguna diferencia.
Lectura adicional
delphi
winapi
virtual-disk
Ian Boyd
fuente
fuente
VIRTUAL_DISK_ACCESS_ALL
en su lugarVIRTUAL_DISK_ACCESS_NONE
. en c ++ no puedo reproducir su error (o convierto incorrectamente delphi a c ++). será más fácil encontrar dónde fallar si también adjunta exe binario, qué posible depuraciónRespuestas:
El primer comentario de @RbMm a los puntos de pregunta dónde buscar y cómo resolver el problema. Afirma que la traducción de c ++ no reproduce el problema. Entonces el problema debe estar con la traducción del encabezado (virtdisk.h). El comentario incluso afirma que la traducción de Delphi podría no ser precisa.
Navegando rápidamente por el código en busca de errores de traducción comunes, nos encontramos con enumeraciones. Con valores asignados explícitamente (el más grande es de 3 bytes), el primero (VIRTUAL_DISK_ACCESS_MASK) es bueno, el compilador usará 4 bytes aquí.
El siguiente es problemático:
Siendo conservador sobre los tamaños de enumeración, el compilador usará 1 byte para este tipo. Eso provocará un desajuste binario con la función exportada (
CreateVirtualDisk
), por lo tanto 87 (ERROR_INVALID_PARAMETER).Puede usar
{$Z4}
antes de la declaración para esta parte.Las pruebas muestran que también debe tener en cuenta los otros consejos en el mismo comentario, es decir, el uso
VIRTUAL_DISK_ACCESS_NONE
. Esto causa un 5 en mi prueba, que es ERROR_ACCESS_DENIED. Puedo crear el disco conVIRTUAL_DISK_ACCESS_ALL
, como aconseja el comentario.Más pruebas muestran que usar la raíz de la unidad raíz para el disco virtual podría no ser una muy buena idea, lo cual se menciona en este comentario . Mi prueba con 'C: \ test.vhd' tuvo éxito pero no puedo encontrar este archivo. Usando otro directorio de escritura, no tengo ningún problema para localizar el archivo.
fuente
VIRTUAL_DISK_ACCESS_CREATE
hace funcionar. Al parecer, cuando se está utilizando "v2" se debe especificarVIRTUAL_DISK_ACCESS_NONE
. Pero si está utilizando "v1" no debe especificarVIRTUAL_DISK_ACCESS_NONE
. EncontréCREATE
trabajos, siempre y cuando el archivo no exista (bórrelo si es así) También agregué{$MINENUMSIZE 4}
a la parte superior de laVirtDisk.pas
unidad para que siga el ABI de Windows.