Einzelnen Beitrag anzeigen

Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#1

Falsch/Inkonsistent definierte Win32Api Funktionen in Winapi.Windows

  Alt 13. Aug 2015, 11:34
Warum wurde die Win32Api Funktion
Code:
BOOL WINAPI GetDiskFreeSpaceEx(
  _In_opt_  LPCTSTR        lpDirectoryName,
  _Out_opt_ PULARGE_INTEGER lpFreeBytesAvailable,
  _Out_opt_ PULARGE_INTEGER lpTotalNumberOfBytes,
  _Out_opt_ PULARGE_INTEGER lpTotalNumberOfFreeBytes
);
in Delphi so deklariert
Delphi-Quellcode:
function GetDiskFreeSpaceEx(
  {} lpDirectoryName: LPCWSTR;
  // äh, wieso var und ohne Typen-Angabe?
  {} var
  {}   lpFreeBytesAvailableToCaller,
  {}   lpTotalNumberOfBytes;
  // ja, das verstehe ich wieder
  {} lpTotalNumberOfFreeBytes: PLargeInteger
  ): BOOL; stdcall;
und nicht so
Delphi-Quellcode:
function GetDiskFreeSpaceEx(
  {} lpDirectoryName: LPCWSTR;
  {} lpFreeBytesAvailableToCaller: PLargeInteger;
  {} lpTotalNumberOfBytes: PLargeInteger;
  {} lpTotalNumberOfFreeBytes: PLargeInteger
  ): BOOL; stdcall;
Warum werden zwei Parameter bewusst falsch implementiert und der letzte korrekt?

Folgender Aufruf (nach der Win32Api wäre das völlig korrekt) wird sofort angemeckert.
Delphi-Quellcode:
GetDisFreeSpaceEx(
  'C:\',
  nil, // <- Mecker
  nil, // <- Mecker
  nil );
Eine mögliche Antwort darauf könnte sein, dass diese Funktion in der RTL verwendet wird.
Delphi-Quellcode:
unit System.SysUtils;

function InternalGetDiskSpace(Drive: Byte;
  var TotalSpace, FreeSpaceAvailable: Int64): Bool;
var
  RootPath: array[0..4] of Char;
  RootPtr: PChar;
begin
  RootPtr := nil;
  if Drive > 0 then
  begin
    RootPath[0] := Char(Drive + $40);
    RootPath[1] := ':';
    RootPath[2] := '\';
    RootPath[3] := #0;
    RootPtr := RootPath;
  end;
  Result := GetDiskFreeSpaceEx(RootPtr, FreeSpaceAvailable, TotalSpace, nil);
end;
So, weil dort also zwei Parameter immer gesetzt werden und der letzte Parameter nicht benötigt wird und somit auf nil gesetzt werden soll, hat man also den Api-Aufruf so deklariert, dass der zu dieser Funktion passt.

Gut man hätte die Api-Funktion auch richtig deklarieren können und diese Funktion so umsetzen:
Delphi-Quellcode:
function InternalGetDiskSpace( Drive: Byte; var TotalSpace, FreeSpaceAvailable: Int64 ): Bool;
var
  RootPath: array [ 0 .. 4 ] of Char;
  RootPtr: PChar;
begin
  RootPtr := nil;
  if Drive > 0 then
  begin
    RootPath[ 0 ] := Char( Drive + $40 );
    RootPath[ 1 ] := ':';
    RootPath[ 2 ] := '\';
    RootPath[ 3 ] := #0;
    RootPtr := RootPath;
  end;
  Result := GetDiskFreeSpaceEx( RootPtr, PLargeInteger( FreeSpaceAvailable ), PLargeInteger( TotalSpace ), nil );
end;
Ich finde es jetzt auf die Schnelle nicht, aber solche fehlerhaften Übersetzungen der Api wurden/werden regelmässig als Bug gemeldet und mit der gleichen stoischen Regelmässigkeit als as designed geschlossen. Ja, nee .. is klar.

Ich mache mir die Welt, wie sie mir gefällt.

Übrig bleibt dann nur noch, sich diese Funktionen selber zu deklarieren - gut dann aber auch so richtig richtig:
Delphi-Quellcode:
unit netAlike.Winapi.Windows;

interface

uses
  Winapi.Windows;

procedure CheckApiCall( ApiCallResult: BOOL; const AdditionalInfo: string = '' );

{$REGION 'GetDiskFreeSpaceEx'}
/// <summary>
/// Retrieves information about the amount of space that is available on a disk volume, which is the total amount of space,
/// the total amount of free space, and the total amount of free space available to the user that is associated with the calling thread.
/// </summary>
/// <param name="lpDirectoryName">[in, optional]
/// <para>A directory on the disk.</para>
/// <para>If this parameter is NULL, the function uses the root of the current disk.</para>
/// <para>If this parameter is a UNC name, it must include a trailing backslash, for example, "\\MyServer\MyShare\".</para>
/// <para>This parameter does not have to specify the root directory on a disk. The function accepts any directory on a disk.</para>
/// <para>The calling application must have <see cref="FILE_LIST_DIRECTORY"/> access rights for this directory.</para>
/// </param>
/// <param name="lpFreeBytesAvailableToCaller">[out, optional]
/// <para>
/// A pointer to a variable that receives the total number of free bytes on a disk that are available
/// to the user who is associated with the calling thread.
/// </para>
/// <para>This parameter can be NULL.</para>
/// <para>If per-user quotas are being used, this value may be less than the total number of free bytes on a disk.</para>
/// </param>
/// <param name="lpTotalNumberOfBytes">[out, optional]
/// <para>
/// A pointer to a variable that receives the total number of bytes on a disk that are available to the user
/// who is associated with the calling thread.
/// </para>
/// <para>This parameter can be NULL.</para>
/// <para>If per-user quotas are being used, this value may be less than the total number of bytes on a disk.</para>
/// <para>To determine the total number of bytes on a disk or volume, use <see cref="IOCTL_DISK_GET_LENGTH_INFO"/>.</para>
/// </param>
/// <param name="lpTotalNumberOfFreeBytes">[out, optional]
/// <para>A pointer to a variable that receives the total number of free bytes on a disk.</para>
/// <para>This parameter can be NULL.</para>
/// </param>
function GetDiskFreeSpaceEx(
  { IN OPT  } lpDirectoryName: LPCWSTR;
  { OUT OPT } lpFreeBytesAvailableToCaller: PLargeInteger;
  { OUT OPT } lpTotalNumberOfBytes: PLargeInteger;
  { OUT OPT } lpTotalNumberOfFreeBytes: PLargeInteger ): BOOL; stdcall;
{$EXTERNALSYM GetDiskFreeSpaceEx}
function GetDiskFreeSpaceExA(
  { IN OPT  } lpDirectoryName: LPCSTR;
  { OUT OPT } lpFreeBytesAvailableToCaller: PLargeInteger;
  { OUT OPT } lpTotalNumberOfBytes: PLargeInteger;
  { OUT OPT } lpTotalNumberOfFreeBytes: PLargeInteger ): BOOL; stdcall;
{$EXTERNALSYM GetDiskFreeSpaceExA}
function GetDiskFreeSpaceExW(
  { IN OPT  } lpDirectoryName: LPCWSTR;
  { OUT OPT } lpFreeBytesAvailableToCaller: PLargeInteger;
  { OUT OPT } lpTotalNumberOfBytes: PLargeInteger;
  { OUT OPT } lpTotalNumberOfFreeBytes: PLargeInteger ): BOOL; stdcall;
{$ENDREGION}
{$REGION 'GetVolumeInformation'}
/// <summary>
/// Retrieves information about the file system and volume associated with the specified root directory.
/// </summary>
/// <param name="lpRootPathName">[in, optional]
/// <para>A pointer to a string that contains the root directory of the volume to be described.</para>
/// <para>
/// If this parameter is NULL, the root of the current directory is used. A trailing backslash is required.
/// For example, you specify \\MyServer\MyShare as "\\MyServer\MyShare\", or the C drive as "C:\".
/// </para>
/// </param>
/// <param name="lpVolumeNameBuffer">[out, optional]
/// <para>
/// A pointer to a buffer that receives the name of a specified volume. The buffer size is specified by
/// the nVolumeNameSize parameter.
/// </para>
/// </param>
/// <param name="nVolumeNameSize">[in]
/// <para>The length of a volume name buffer, in TCHARs. The maximum buffer size is MAX_PATH+1.</para>
/// <para>This parameter is ignored if the volume name buffer is not supplied.</para>
/// </param>
/// <param name="lpVolumeSerialNumber">[out, optional]
/// <para>A pointer to a variable that receives the volume serial number.</para>
/// <para>This parameter can be NULL if the serial number is not required.</para>
/// <para>
/// This function returns the volume serial number that the operating system assigns when a hard disk is formatted.
/// To programmatically obtain the hard disk's serial number that the manufacturer assigns, use the Windows Management
/// Instrumentation (WMI) Win32_PhysicalMedia property SerialNumber.
/// </para>
/// </param>
/// <param name="lpMaximumComponentLength">[out, optional]
/// <para>A pointer to a variable that receives the maximum length, in TCHARs, of a file name component that a specified file system supports.</para>
/// <para>A file name component is the portion of a file name between backslashes.</para>
/// <para>
/// The value that is stored in the variable that *lpMaximumComponentLength points to is used to indicate that a
/// specified file system supports long names. For example, for a FAT file system that supports long names, the
/// function stores the value 255, rather than the previous 8.3 indicator. Long names can also be supported on
/// systems that use the NTFS file system.
/// </para>
/// </param>
/// <param name="lpFileSystemFlags">[out, optional]
/// <para>A pointer to a variable that receives flags associated with the specified file system.</para>
/// <para>This parameter can be one or more of the following flags. However, FS_FILE_COMPRESSION and FS_VOL_IS_COMPRESSED are mutually exclusive.</para>
/// </param>
/// <param name="lpFileSystemNameBuffer">[out, optional]
/// <para>
/// A pointer to a buffer that receives the name of the file system, for example, the FAT file system or the NTFS file system.
/// The buffer size is specified by the nFileSystemNameSize parameter.
/// </para>
/// </param>
/// <param name="nFileSystemNameSize">[in]
/// <para>The length of the file system name buffer, in TCHARs. The maximum buffer size is MAX_PATH+1.</para>
/// <para>This parameter is ignored if the file system name buffer is not supplied.</para>
/// </param>
function GetVolumeInformation(
  { IN OPT  } lpRootPathName: LPCWSTR;
  { OUT OPT } lpVolumeNameBuffer: LPWSTR;
  { IN      } nVolumeNameSize: DWORD;
  { OUT OPT } lpVolumeSerialNumber: LPDWORD;
  { OUT OPT } lpMaximumComponentLength: LPDWORD;
  { OUT OPT } lpFileSystemFlags: LPDWORD;
  { OUT OPT } lpFileSystemNameBuffer: LPWSTR;
  { IN      } nFileSystemNameSize: DWORD ): BOOL; stdcall;
{$EXTERNALSYM GetVolumeInformation}
function GetVolumeInformationA(
  { IN OPT  } lpRootPathName: LPCSTR;
  { OUT OPT } lpVolumeNameBuffer: LPSTR;
  { IN      } nVolumeNameSize: DWORD;
  { OUT OPT } lpVolumeSerialNumber: LPDWORD;
  { OUT OPT } lpMaximumComponentLength: LPDWORD;
  { OUT OPT } lpFileSystemFlags: LPDWORD;
  { OUT OPT } lpFileSystemNameBuffer: LPSTR;
  { IN      } nFileSystemNameSize: DWORD ): BOOL; stdcall;
{$EXTERNALSYM GetVolumeInformationA}
function GetVolumeInformationW(
  { IN OPT  } lpRootPathName: LPCWSTR;
  { OUT OPT } lpVolumeNameBuffer: LPWSTR;
  { IN      } nVolumeNameSize: DWORD;
  { OUT OPT } lpVolumeSerialNumber: LPDWORD;
  { OUT OPT } lpMaximumComponentLength: LPDWORD;
  { OUT OPT } lpFileSystemFlags: LPDWORD;
  { OUT OPT } lpFileSystemNameBuffer: LPWSTR;
  { IN      } nFileSystemNameSize: DWORD ): BOOL; stdcall;
{$EXTERNALSYM GetVolumeInformationW}
{$ENDREGION}

implementation

uses
  System.SysUtils;

procedure CheckApiCall( ApiCallResult: BOOL; const AdditionalInfo: string );
begin
  if not ApiCallResult then
    RaiseLastOsError( GetLastError, sLineBreak + AdditionalInfo );
end;

function GetDiskFreeSpaceEx; external kernelbase name 'GetDiskFreeSpaceExW';
function GetDiskFreeSpaceExA; external kernelbase name 'GetDiskFreeSpaceExA';
function GetDiskFreeSpaceExW; external kernelbase name 'GetDiskFreeSpaceExW';

function GetVolumeInformation; external kernelbase name 'GetVolumeInformationW';
function GetVolumeInformationA; external kernelbase name 'GetVolumeInformationA';
function GetVolumeInformationW; external kernelbase name 'GetVolumeInformationW';

end.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat