CreateVirtualDisk gibt Fehler 87 aus (Der Parameter ist falsch.)

8

Unter Windows 10 schlägt der Versuch, mithilfe der CreateVirtualDisk-API eine virtuelle Festplatte zu erstellen, fehl und gibt den Fehlercode 87 zurück.

Komplettes minimal reproduzierbares Beispiel.

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.

Offensichtlich macht es keinen Unterschied, als Administrator zu arbeiten.

Bonuslesung

Ian Boyd
quelle
2
Ich schlage vor, VIRTUAL_DISK_ACCESS_ALLstattdessen zu verwenden VIRTUAL_DISK_ACCESS_NONE. in c ++ kann ich deinen fehler nicht reproduzieren (oder ich konvertiere delphi falsch in c ++).
Seien
Hängen Sie

Antworten:

11

@ RbMms erster Kommentar zu den Fragen zeigt, wo gesucht und wie das Problem gelöst werden muss. Er gibt an, dass die c ++ - Übersetzung das Problem nicht reproduziert. Dann muss das Problem bei der Übersetzung des Headers (virtdisk.h) liegen. Der Kommentar besagt sogar, dass die Übersetzung von Delphi möglicherweise nicht korrekt ist.

Beim schnellen Durchsuchen des Codes nach häufigen Übersetzungsfehlern stoßen wir auf Aufzählungen. Bei explizit zugewiesenen Werten (der größte ist 3 Byte) ist der erste (VIRTUAL_DISK_ACCESS_MASK) gut, der Compiler verwendet hier 4 Byte.

Der nächste ist problematisch:

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).

Aus Gründen der Aufzählungsgröße verwendet der Compiler 1 Byte für diesen Typ. CreateVirtualDiskDies führt zu einer binären Nichtübereinstimmung mit der exportierten Funktion ( ), daher 87 (ERROR_INVALID_PARAMETER).

Sie können {$Z4}vor der Deklaration für diesen Teil verwenden.

Tests zeigen, dass Sie auch die anderen Ratschläge im selben Kommentar berücksichtigen müssen, nämlich die Verwendung VIRTUAL_DISK_ACCESS_NONE. Dies verursacht in meinem Test eine 5, die ERROR_ACCESS_DENIED ist. Ich kann die Festplatte mit erstellen VIRTUAL_DISK_ACCESS_ALL, wie im Kommentar empfohlen.

Weitere Tests zeigen, dass die Verwendung des Stammverzeichnisses des Stammlaufwerks für die virtuelle Festplatte möglicherweise keine sehr gute Idee ist, die in diesem Kommentar erwähnt wird . Mein Test mit 'C: \ test.vhd' war erfolgreich, aber ich kann diese Datei nicht finden. Bei Verwendung eines anderen beschreibbaren Verzeichnisses kann ich die Datei problemlos finden.

Sertac Akyuz
quelle
1
Durch Umschalten auf VIRTUAL_DISK_ACCESS_CREATEfunktioniert es. Anscheinend müssen Sie angeben, wenn Sie "v2" verwendenVIRTUAL_DISK_ACCESS_NONE . Wenn Sie jedoch "v1" verwenden , dürfen Sie dies nicht angeben VIRTUAL_DISK_ACCESS_NONE. Ich habe CREATEWerke gefunden - solange die Datei noch nicht existiert (löschen Sie sie, wenn dies der Fall ist). Ich habe auch {$MINENUMSIZE 4}oben am VirtDisk.pasGerät hinzugefügt , damit es dem Windows-ABI folgt.
Ian Boyd