Public Type PARTITION_INFORMATION
StartingOffset As LARGE_INTEGER
PartitionLength As LARGE_INTEGER
HiddenSectors As Long
PartitionNumber As Long
PartitionType As Byte
BootIndicator As Boolean
RecognizedPartition As Boolean
RewritePartition As Boolean
End Type
Public Type DRIVE_LAYOUT_INFORMATION
PartitionCount As Long
Signature As Long
PartitionEntry(32) As PARTITION_INFORMATION
End Type
Dim newdli As DRIVE_LAYOUT_INFORMATION
Dim k As Integer
newdli.PartitionCount = UBound(newdli.PartitionEntry) - 1
newdli.Signature = 0
With newdli.PartitionEntry(0)
.BootIndicator = False
.HiddenSectors = 63
.PartitionLength.liLow = 1073447424
.PartitionLength.liHigh = 0
.PartitionNumber = 1
.PartitionType = 7
.RecognizedPartition = True
.RewritePartition = True
.StartingOffset.liLow = 32256
.StartingOffset.liHigh = 0
End With
For k = 1 To newdli.PartitionCount
With newdli.PartitionEntry(k)
.BootIndicator = False
.HiddenSectors = 0
.PartitionLength.liLow = 0
.PartitionLength.liHigh = 0
.PartitionNumber = k
.PartitionType = PARTITION_ENTRY_UNUSED
.RecognizedPartition = False
.RewritePartition = True
.StartingOffset.liLow = 0
.StartingOffset.liHigh = 0
End With
Next k
This should create a 1GB partition on the selected disk and leave the
remaining space as unallocated. Instead, the partition consumes the
entire disk (2GB). I do not receive any errors, so I believe that I have
something defined incorrectly in the PARTITION_INFORMATION.
Any help would be appreciated.
Thanks,
B-Mann
Wrong declaration! Instead use
Public Type PARTITION_INFORMATION
StartingOffset As LARGE_INTEGER
PartitionLength As LARGE_INTEGER
HiddenSectors As Long
PartitionNumber As Long
PartitionType As Byte
BootIndicator As Byte
RecognizedPartition As Byte
RewritePartition As Byte
End Type
Note:
A VB Boolean has 2 bytes in memory and takes the value True (-1) or False
(0)
A C BOOLEAN has 1 byte in memory and takes the value TRUE (1) or FALSE (0)
> Public Type DRIVE_LAYOUT_INFORMATION
> PartitionCount As Long
> Signature As Long
> PartitionEntry(32) As PARTITION_INFORMATION
> End Type
If you want to create only one partition, why don't you declare this
structure with
PartitionEntry(1) As PARTITION_INFORMATION
or
PartitionEntry As PARTITION_INFORMATION
instead?
> newdli.PartitionCount = UBound(newdli.PartitionEntry) - 1
This should be the number of partitions on the drive; since you want to
create 1 partition only:
newdli.PartitionCount = 1
> With newdli.PartitionEntry(0)
> .BootIndicator = False
See above:
.BootIndicator = 0
> .RecognizedPartition = True
See above:
.RecognizedPartition = 1
> .RewritePartition = True
See above:
.RewritePartition = 1
> End With
Unfortunately these corrections, as far as I can see, do not explain why a
2 GB partition is created instead of a 1 GB partition...
--
----------------------------------------------------------------------
THORSTEN ALBERS Universität Freiburg
albers@
uni-freiburg.de
----------------------------------------------------------------------
Using the 'Byte' type produced:
"Run-time error '49':
Bad DLL calling convention"
> Note:
> A VB Boolean has 2 bytes in memory and takes the value True (-1) or False
> (0)
> A C BOOLEAN has 1 byte in memory and takes the value TRUE (1) or FALSE (0)
>
With this information I changed the declaration to:
Public Type PARTITION_INFORMATION
StartingOffset As LARGE_INTEGER
PartitionLength As LARGE_INTEGER
HiddenSectors As Long
PartitionNumber As Long
PartitionType As Byte
BootIndicator As Integer
RecognizedPartition As Byte
RewritePartition As Byte
End Type
This time I did not receive an error, but the partition was still the entire
size of the
disk (2GB).
>> Public Type DRIVE_LAYOUT_INFORMATION
>> PartitionCount As Long
>> Signature As Long
>> PartitionEntry(32) As PARTITION_INFORMATION
>> End Type
>
> If you want to create only one partition, why don't you declare this
> structure with
> PartitionEntry(1) As PARTITION_INFORMATION
> or
> PartitionEntry As PARTITION_INFORMATION
> instead?
>
Changing the declaration to either of these produced the following error:
"Error: 122 - The data area passed to a system call is too small."
Seems the smallest I can make this is:
PartitionEntry(4) As PARTITION_INFORMATION
>> newdli.PartitionCount = UBound(newdli.PartitionEntry) - 1
>
> This should be the number of partitions on the drive; since you want to
> create 1 partition only:
> newdli.PartitionCount = 1
>
>> With newdli.PartitionEntry(0)
>> .BootIndicator = False
> See above:
> .BootIndicator = 0
>> .RecognizedPartition = True
> See above:
> .RecognizedPartition = 1
>> .RewritePartition = True
> See above:
> .RewritePartition = 1
>> End With
>
Even with all these modifications, the partition created is still the entire
size of the
disk (2GB).
Using the following values:
.BootIndicator = 0
.HiddenSectors = 63
.PartitionLength.liLow = 1073447424
.PartitionLength.liHigh = 0
.PartitionNumber = 1
.PartitionType = 7
.RecognizedPartition = 1
.RewritePartition = 1
.StartingOffset.liLow = 32256
.StartingOffset.liHigh = 0
Produces the following results:
BootIndicator: 1
Hidden Sectors: 0
Partition length: 2147475456 bytes.
Partition Number: 1
Partition Type: 4
Recognized Partition: 56
Rewrite Partition: 177
Starting Offset: 0 bytes.
Does anyone have a working example of this call?
Again, thanks for any assistance.
B-Mann
A structure can >not< lead to this VB run-time error! There must be
something wrong elsewhere in you application.
> With this information I changed the declaration to:
>
> Public Type PARTITION_INFORMATION
> StartingOffset As LARGE_INTEGER
> PartitionLength As LARGE_INTEGER
> HiddenSectors As Long
> PartitionNumber As Long
> PartitionType As Byte
> BootIndicator As Integer
> RecognizedPartition As Byte
> RewritePartition As Byte
> End Type
Why? The C declaration of this structure is as follows:
typedef struct _PARTITION_INFORMATION {
LARGE_INTEGER StartingOffset;
LARGE_INTEGER PartitionLength;
DWORD HiddenSectors;
DWORD PartitionNumber;
BYTE PartitionType;
BOOLEAN BootIndicator;
BOOLEAN RecognizedPartition;
BOOLEAN RewritePartition;
} PARTITION_INFORMATION, *PPARTITION_INFORMATION;
What I gave you is the exact matching VB equivalent. You can't declare
BootIndicator "As Integer" in VB if it is BOOLEAN in C.
This is the type definition for BOOLEAN in C:
typedef BYTE BOOLEAN;
typedef unsigned char BYTE;
> Even with all these modifications, the partition created is still the
entire
> size of the
> disk (2GB).
You should show as all your declarations and code, not just the part
posted.
> Using the following values:
>
> .BootIndicator = 0
> .HiddenSectors = 63
> .PartitionLength.liLow = 1073447424
> .PartitionLength.liHigh = 0
> .PartitionNumber = 1
> .PartitionType = 7
> .RecognizedPartition = 1
> .RewritePartition = 1
> .StartingOffset.liLow = 32256
> .StartingOffset.liHigh = 0
>
> Produces the following results:
>
> BootIndicator: 1
> Hidden Sectors: 0
> Partition length: 2147475456 bytes.
> Partition Number: 1
> Partition Type: 4
> Recognized Partition: 56
> Rewrite Partition: 177
> Starting Offset: 0 bytes.
I don't know what an "IFS partition" is (.PartitionType = 7 =
PARTITION_IFS) - but are you sure that this type of partition is supported
by your version of Windows? I rather doubt this since the OS creates a
FAT16 partition (Partition type = 4 = PARTITION_FAT_16) instead. The
results of the actions seem to show that the OS has created a partition
with default values instead since it doesn't support the specified values.
On what Windows version is your code executed?
I am running this under Windows XP SP2, and have tried partition types of:
PARTITION_FAT_16
PARTITION_FAT32
<code>
Option Explicit
Public Const GENERIC_READ = &H80000000
Public Const GENERIC_WRITE = &H40000000
Public Const FILE_SHARE_READ As Long = &H1
Public Const FILE_SHARE_WRITE As Long = &H2
Public Const OPEN_EXISTING As Long = 3
Public Const INVALID_HANDLE_VALUE As Long = -1
Public Const IOCTL_DISK_GET_DRIVE_GEOMETRY As Long = &H70000
Public Const IOCTL_DISK_GET_PARTITION_INFO = &H74004
Public Const IOCTL_DISK_GET_DRIVE_LAYOUT = &H7400C
Public Const IOCTL_DISK_CREATE_DISK = &H7C058
Public Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000
Public Const IOCTL_DISK_DELETE_DRIVE_LAYOUT = &H7C100
Public Const IOCTL_DISK_SET_PARTITION_INFO = &H7C008
Public Const IOCTL_DISK_SET_DRIVE_LAYOUT = &H7C010
Public Const IOCTL_DISK_UPDATE_PROPERTIES = &H70140
Public Const FSCTL_DISMOUNT_VOLUME = &H90020
Public Const PARTITION_ENTRY_UNUSED = &H0
Public Const PARTITION_FAT_12 = &H1
Public Const PARTITION_XENIX_1 = &H2
Public Const PARTITION_XENIX_2 = &H3
Public Const PARTITION_FAT_16 = &H4
Public Const PARTITION_EXTENDED = &H5
Public Const PARTITION_HUGE = &H6
Public Const PARTITION_IFS = &H7
Public Const PARTITION_OS2BOOTMGR = &HA
Public Const PARTITION_FAT32 = &HB
Public Const PARTITION_FAT32_XINT13 = &HC
Public Const PARTITION_XINT13 = &HE
Public Const PARTITION_XINT13_EXTENDED = &HF
Public Const PARTITION_PREP = &H41
Public Const PARTITION_LDM = &H42
Public Const PARTITION_UNIX = &H63
Private Declare Function FormatMessage Lib "kernel32" Alias _
"FormatMessageA" (ByVal dwFlags As Long, lpSource As Any, _
ByVal dwMessageId As Long, ByVal dwLanguageId As Long, _
ByVal lpBuffer As String, ByVal nSize As Long, _
Arguments As Long) As Long
Public Declare Function CreateFile Lib "kernel32" _
Alias "CreateFileA" _
(ByVal lpFileName As String, _
ByVal dwDesiredAccess As Long, _
ByVal dwShareMode As Long, _
ByVal lpSecurityAttributes As Any, _
ByVal dwCreationDisposition As Long, _
ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" _
(ByVal hObject As Long) As Long
Public Declare Function DeviceIoControl Lib "kernel32" _
(ByVal hdevice As Long, _
ByVal dwIoControlCode As Long, _
lpInBuffer As Any, ByVal _
nInBufferSize As Long, _
lpOutBuffer As Any, _
ByVal nOutBufferSize As Long, _
lpBytesReturned As Long, _
lpOverlapped As Any) As Long
Private Type LARGE_INTEGER
liLow As Long
liHigh As Long
End Type
Public Type PARTITION_INFORMATION
StartingOffset As LARGE_INTEGER
PartitionLength As LARGE_INTEGER
HiddenSectors As Long
PartitionNumber As Long
PartitionType As Byte
BootIndicator As Byte
RecognizedPartition As Byte
RewritePartition As Byte
End Type
Public Type DRIVE_LAYOUT_INFORMATION
PartitionCount As Long
Signature As Long
PartitionEntry(1) As PARTITION_INFORMATION
End Type
Sub SetDriveLayout(hdevice As Long, dli As DRIVE_LAYOUT_INFORMATION)
Dim retval As Long
Dim bytesreturned As Long
retval = DeviceIoControl(hdevice, _
IOCTL_DISK_SET_DRIVE_LAYOUT, _
dli, _
Len(dli), _
ByVal 0&, _
0&, _
bytesreturned, _
ByVal 0&)
If Err.LastDllError <> 0 Then Debug.Print "Error: " & Err.LastDllError &
" - " & APIErrorDescription(Err.LastDllError)
End Sub
Sub DiskUpdateProperties(hdevice As Long)
Dim retval As Long
Dim bytesreturned As Long
retval = DeviceIoControl(hdevice, _
IOCTL_DISK_UPDATE_PROPERTIES, _
ByVal 0&, _
0&, _
ByVal 0&, _
0&, _
bytesreturned, _
ByVal 0&)
If Err.LastDllError <> 0 Then Debug.Print "Error: " & Err.LastDllError &
" - " & APIErrorDescription(Err.LastDllError)
End Sub
Sub Main()
Dim retval As Long
Dim hdevice As Long
Dim bytesreturned As Long
Dim PhysicalDrive As Long
Dim newdli As DRIVE_LAYOUT_INFORMATION
PhysicalDrive = 1
hdevice = CreateFile("\\.\PHYSICALDRIVE" & CStr(PhysicalDrive), _
GENERIC_READ Or GENERIC_WRITE, _
FILE_SHARE_READ Or FILE_SHARE_WRITE, _
ByVal 0&, _
OPEN_EXISTING, _
0&, 0&)
If hdevice <> INVALID_HANDLE_VALUE Then
newdli.PartitionCount = 1
newdli.Signature = 0
With newdli.PartitionEntry(0)
.BootIndicator = 0
.HiddenSectors = 63
.PartitionLength.liLow = 16384
.PartitionLength.liHigh = 0
.PartitionNumber = 1
.PartitionType = PARTITION_FAT32
.RecognizedPartition = 1
.RewritePartition = 1
.StartingOffset.liLow = 32256
.StartingOffset.liHigh = 0
End With
Call SetDriveLayout(hdevice, newdli)
Call DiskUpdateProperties(hdevice)
CloseHandle hdevice
End If
End Sub
Private Function CLargeInt(Lo As Long, Hi As Long) As Double
Dim dblLo As Double, dblHi As Double
If Lo < 0 Then
dblLo = 2 ^ 32 + Lo
Else
dblLo = Lo
End If
If Hi < 0 Then
dblHi = 2 ^ 32 + Hi
Else
dblHi = Hi
End If
CLargeInt = dblLo + dblHi * 2 ^ 32
End Function
Public Function APIErrorDescription(ErrorCode As Long) As String
Dim sAns As String
Dim lRet As Long
sAns = Space(255)
lRet = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, _
ErrorCode, 0, sAns, 255, 0)
APIErrorDescription = StripNull(sAns)
End Function
Private Function StripNull(ByVal InString As String) As String
Dim iNull As Integer
If Len(InString) > 0 Then
iNull = InStr(InString, vbNullChar)
Select Case iNull
Case 0
StripNull = Trim(InString)
Case 1
StripNull = ""
Case Else
StripNull = Left$(Trim(InString), iNull - 1)
End Select
End If
End Function
</code>
Thanks again,
B-Mann
Change this to something like:
If retval = 0 Then
If Err.LastDLLError Then
Debug.Print "Error: " & Err.LastDllError & _
" - " & APIErrorDescription(Err.LastDllError)
Else
Debug.Print "Unknown Error"
End If
End If
Err.LastDLLError never indicates whether a Windows API function failed or
not; the failure indicator to be used is that one specified as such by the
Windows API function documentation - it usually is the function's return
code.
Follow this simple rule:
function's failure indicator specifies
A) Success
The contents of Err.LastDLLError is undefined
- don't assume in any case that it is = 0!
B) Failure
B 1) If the function's documentation states
that GetLastError() may be used to retrieve
the error code Err.LastDLLError will hold
the error code.
B 2) If the function's documentation doesn't state
anything about GetLastError() assume
Err.LastDLLError to be >undefined<
For control codes passed to DeviceIoControl() the function's failure
indicator is FALSE = 0 as the return code of DeviceIoControl().
And, of course, your function SetDriveLayout() should inform the caller
about success/failure!
> Sub DiskUpdateProperties(hdevice As Long)
> ...
> retval = DeviceIoControl(hdevice, _
> IOCTL_DISK_UPDATE_PROPERTIES, _
> ByVal 0&, _
> 0&, _
> ByVal 0&, _
> 0&, _
> bytesreturned, _
> ByVal 0&)
>
> If Err.LastDllError <> 0 Then Debug.Print "Error: " & Err.LastDllError
&
> " - " & APIErrorDescription(Err.LastDllError)
Same as above!
> Case Else
> StripNull = Left$(Trim(InString), iNull - 1)
Just BTW:
This must be changed to
StripNull = Trim$(Left$(InString, iNull - 1))
If Trim$() is applied before Left$(), the position of the found vbNullChar
may have changed beeing <> iNull.
In general it is best to apply Trim$() after all other string manipulations
have been down
> I am running this under Windows XP SP2, and have tried partition types
of:
> PARTITION_FAT_16
> PARTITION_FAT32
Now successfull, or still without correct result?
Thanks for your assistance. With your input I have successfully created
a partition
with the following:
<code>
Option Explicit
PartitionEntry(4) As PARTITION_INFORMATION
End Type
Sub SetDriveLayout(hdevice As Long, dli As DRIVE_LAYOUT_INFORMATION)
Dim retval As Long
Dim bytesreturned As Long
retval = DeviceIoControl(hdevice, _
IOCTL_DISK_SET_DRIVE_LAYOUT, _
dli, _
Len(dli), _
ByVal 0&, _
0&, _
bytesreturned, _
ByVal 0&)
If retval = 0 Then
If Err.LastDllError Then
Debug.Print "Error: " & Err.LastDllError & _
" - " & APIErrorDescription(Err.LastDllError)
Else
Debug.Print "Unknown Error"
End If
End If
End Sub
Sub DiskUpdateProperties(hdevice As Long)
Dim retval As Long
Dim bytesreturned As Long
retval = DeviceIoControl(hdevice, _
IOCTL_DISK_UPDATE_PROPERTIES, _
ByVal 0&, _
0&, _
ByVal 0&, _
0&, _
bytesreturned, _
ByVal 0&)
If retval = 0 Then
If Err.LastDllError Then
Debug.Print "Error: " & Err.LastDllError & _
" - " & APIErrorDescription(Err.LastDllError)
Else
Debug.Print "Unknown Error"
End If
End If
End Sub
Sub Main()
Dim retval As Long
Dim hdevice As Long
Dim bytesreturned As Long
Dim PhysicalDrive As Long
Dim newdli As DRIVE_LAYOUT_INFORMATION
PhysicalDrive = 1
hdevice = CreateFile("\\.\PHYSICALDRIVE" & CStr(PhysicalDrive), _
GENERIC_READ Or GENERIC_WRITE, _
FILE_SHARE_READ Or FILE_SHARE_WRITE, _
ByVal 0&, _
OPEN_EXISTING, _
0&, 0&)
If hdevice <> INVALID_HANDLE_VALUE Then
newdli.PartitionCount = UBound(newdli.PartitionEntry)
newdli.Signature = 0
With newdli.PartitionEntry(0)
.BootIndicator = 0
.HiddenSectors = 1
.PartitionLength.liLow = 16777216 ' 1MB = 1024*1024
.PartitionLength.liHigh = 0
.PartitionNumber = 0
.PartitionType = PARTITION_IFS
.RecognizedPartition = 1
.RewritePartition = 1
.StartingOffset.liLow = .HiddenSectors * 512
Case Else
StripNull = Left$(Trim(InString), iNull - 1)
End Select
End If
End Function
</code>
I can now create a partition of any size within the size of the drive. I am
now having a problem with creating additional partitions. You will notice
that I have to define the
PartitionEntry size to 4:
PartitionEntry(4) As PARTITION_INFORMATION
Anything smaller fails to create a partition. At that point I thought that I
could then
create partition 2 using the following prior to calling SetDriveLayout:
With newdli.PartitionEntry(1)
.BootIndicator = 0
.HiddenSectors = 0
.PartitionLength.liLow = 33554432
.PartitionLength.liHigh = 0
.PartitionNumber = 1
.PartitionType = PARTITION_IFS
.RecognizedPartition = 1
.RewritePartition = 1
.StartingOffset.liLow = newdli.PartitionEntry(0).PartitionLength.liLow
.StartingOffset.liHigh = 0
End With
The first partition is created, but the additional is not. I am having
trouble understanding
what values to use for .HiddenSectors and .StartingOffset. When creating the
first
partition, if I set .HiddenSectors to 0, this removes the disk signature but
still creates the
partition. From what I have read, for FAT partitions, the first 512 bytes
contains the
partition information. So, I set .HiddenSectors to 1 and .StartingOffset to
a multiple of
512 based on the number of .HiddenSectors.This seems to work, but I do not
believe
this would be correct for NTFS partitions. In addition, when I create a
partition though
disk administrator, the number of hidden sectors have been 32 and 64 and the
starting offset is still a multiple of 512. I'll continue to research this
but any help you could provide
would be great.
Thanks,
B-Mann
> that I have to define the
> PartitionEntry size to 4:
> PartitionEntry(4) As PARTITION_INFORMATION
> Anything smaller fails to create a partition.
The MSDN documentation doesn't seem to state anything about such a
limitation.
But this restriction to a minimum of 4 partitions reminds me of the general
partition restrictions where on one hard disk may be either only 4 primary
partitions or only 3 primary + 1 extended partition (with additional n
logic partitions in the extended partition).
> At that point I thought that I
> could then
> create partition 2 using the following prior to calling SetDriveLayout:
>
> With newdli.PartitionEntry(1)
> .BootIndicator = 0
> .HiddenSectors = 0
> .PartitionLength.liLow = 33554432
> .PartitionLength.liHigh = 0
> .PartitionNumber = 1
> .PartitionType = PARTITION_IFS
> .RecognizedPartition = 1
> .RewritePartition = 1
> .StartingOffset.liLow =
newdli.PartitionEntry(0).PartitionLength.liLow
> .StartingOffset.liHigh = 0
> End With
>
> The first partition is created, but the additional is not.
Did you increment
newdli.PartitionCount = 1
by one?
> I am having trouble understanding
> what values to use for .HiddenSectors and .StartingOffset.
> ...
> I'll continue to research this but any help you could provide
> would be great.
I can not help you with this since I don't know what a partition of type
PARTITION_IFS is, or what rules such a partition has to follow...
Yes, I have set this explicitly to 2, 3, 4, 5 with the same results.
>
> I can not help you with this since I don't know what a partition of type
> PARTITION_IFS is, or what rules such a partition has to follow...
>
I have tried using PARTITION_FAT_32 as well, still only able to create a
single
partition. I believe that the other values for the PARTITION_INFORMATION
type
may need to be set to values other than what I am using, but I can not find
examples of
the use of this, or clear definitions of the settings. In particular,
HiddenSectors and
StartingOffset.
Thanks,
B-Mann
Maybe this IOCTL control code has the same restriction like MS-DOS FDISK:
FDISK allows you to create only one primary partition at the time. After
you have created one you first have to restart the computer before you can
create another one. This is not valid for logical partitions in an extended
partition.
> I have tried using PARTITION_FAT_32 as well, still only able to create a
> single
> partition. I believe that the other values for the PARTITION_INFORMATION
> type
> may need to be set to values other than what I am using, but I can not
find
> examples of
> the use of this, or clear definitions of the settings. In particular,
> HiddenSectors and
> StartingOffset.
So, what I would suggest to do: Create the partitions of the type and size
you would like to have with an external tool. Then call
IOCTL_DISK_GET_DRIVE_LAYOUT and examine the retrieved PARTITION_INFORMATION
structures.
Of course you should do any partition works only if you are fully aware of
what you are doing. So, first you should get yourself a good documentation
on partitioning. According to my own experince a good starter is the PDF
documentation to POWERQUEST Partition Magic which gives some basic
information on partition types etc. Note that POWERQUEST has been acquiered
by SYMANTEC, so - if these is of interest for you - you have to search for
Partition Magic on the SYMANTEC web site (now it is called NORTON Power
Magic)...
What I forgot to mention:
If you are working with Windows XP you should prefer the new extended IOCTL
functions, e.g. IOCTL_DISK_SET_DRIVE_LAYOUT_EX instead of
IOCTL_DISK_SET_DRIVE_LAYOUT!
> now it is called NORTON Power Magic...
Nonsense: Of course "NORTON Partition Magic"...