Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[54.157.56.179] |
|
Сообщ.
#1
,
|
|
|
Всем привет. У меня такая проблема:
Необходимо вывести информацию о диске C:\ с помощью WinAPI-функции GetDiskFreeSpaceEx. Имеется следующий программный код: procedure TForm1.Button1Click(Sender: TObject); var lpRootPathName:PChar; lpFreeBytesAvailable:PLargeInteger; lpTotalNumberOfBytes:PLargeInteger; lpTotalNumberOfFreeBytes:PLargeInteger; begin new(lpFreeBytesAvailable); new(lpTotalNumberOfBytes); new(lpTotalNumberOfFreeBytes); lpRootPathName:=StringToOleStr('C:\'); if not Windows.GetDiskFreeSpaceEx(lpRootPathName,lpFreeBytesAvailable, lpTotalNumberOfBytes,lpTotalNumberOfFreeBytes) then begin lpFreeBytesAvailable^:=0; lpTotalNumberOfBytes^:=0; lpTotalNumberOfFreeBytes^:=0; end; ShowMessage('Диск С:'+#13#10+ 'lpFreeBytesAvailable = '+inttostr(lpFreeBytesAvailable^)+#13#10+ 'lpTotalNumberOfBytes = '+inttostr(lpTotalNumberOfBytes^)+#13#10+ 'lpTotalNumberOfFreeBytes = '+inttostr(lpTotalNumberOfFreeBytes^)) end; После запуска программы и нажатия кнопки Button1 на экране появляется сообщение об ошибке: Project Project1.exe raised exception class EAccessViolation with message 'Access violation at address 004B3465 in module 'Project1.exe'. Read of address 00000008'. Как исправить ошибку, подскажите пожалуйста. |
Сообщ.
#2
,
|
|
|
var lpRootPathName:PChar; lpFreeBytesAvailable: Int64; lpTotalNumberOfBytes: Int64; lpTotalNumberOfFreeBytes: Int64; begin lpRootPathName:=StringToOleStr('C:\'); if not GetDiskFreeSpaceEx(lpRootPathName, lpFreeBytesAvailable, lpTotalNumberOfBytes, @lpTotalNumberOfFreeBytes) then begin lpFreeBytesAvailable := 0; lpTotalNumberOfBytes := 0; lpTotalNumberOfFreeBytes := 0; end; ShowMessage('Диск С:'+#13#10+ 'lpFreeBytesAvailable = '+inttostr(lpFreeBytesAvailable)+#13#10+ 'lpTotalNumberOfBytes = '+inttostr(lpTotalNumberOfBytes)+#13#10+ 'lpTotalNumberOfFreeBytes = '+inttostr(lpTotalNumberOfFreeBytes)) |
Сообщ.
#3
,
|
|
|
Проверяй, выделяется ли вообще память. Потом, проверь, какого типа у тебя возвращается указатель из StringToOleStr(), по описанию там PWideChar, а у тебя в коде PChar, при этом тип, принимаемый GetDiskFreeSpaceEx, зависит от того, определен UNICODE или нет (плюс она с сишной декларацией, не помню, надо ли что-то делать в коде помимо вызова). И наконец, строку из StringToOleStr надо освобождать руками. То есть для начала поменяй тип у lpRootPathName на PWideChar.
Добавлено MBo, то есть если в WinAPI в описании указатель, Дельфи нормально принимает имя переменной, и передает правильный указатель без всяких проблем? |
Сообщ.
#4
,
|
|
|
У меня использовано объявление из SysUtils
GetDiskFreeSpaceEx: function (Directory: PChar; var FreeAvailable, TotalSpace: TLargeInteger; TotalFree: PLargeInteger): Bool т.к. Windows модуль сразу не нашёлся (у меня нужно было WinApi.Windows написать) В любом случае - что var, что указательный тип параметра - в функцию передаётся адрес. Объявление с var это скрывает, и передавать нужно саму переменную (адрес её подставит компилятор) в Windows функция объявлена так: function GetDiskFreeSpaceEx(lpDirectoryName: LPCWSTR; var lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes; lpTotalNumberOfFreeBytes: PLargeInteger): BOOL; stdcall; т.е. два бестиповых var-параметра, и нужно также передавать переменную, а не адрес, иначе получится двойное взятие адреса - будет использован адрес переменной lpFreeBytesAvailable:PLargeInteger; |
Сообщ.
#5
,
|
|
|
опять попался на разыменовании указателей. Отвык уже спасибо.
|
Сообщ.
#6
,
|
|
|
В справочной системе Delphi 2010 нет информации о SysUtils.GetDiskFreeSpaceEx. Чем SysUtils.GetDiskFreeSpaceEx отличается от Windows.GetDiskFreeSpaceEx? Как правильно вызвать Windows.GetDiskFreeSpaceEx?
|
Сообщ.
#7
,
|
|
|
Точно так же
|
Сообщ.
#8
,
|
|
|
Цитата grh2 @ В справочной системе Delphi 2010 нет информации о SysUtils.GetDiskFreeSpaceEx. Чем SysUtils.GetDiskFreeSpaceEx отличается от Windows.GetDiskFreeSpaceEx? Как правильно вызвать Windows.GetDiskFreeSpaceEx? На все три вопроса один ответ - смотри подсказку по параметрам функции при их наборе. Эта подсказка должна появляться автоматически (если не отключена в настройках), а также ее можно вызвать самому по Ctrl+Shift+пробел, когда курсор находится внутри скобок функции. Если видишь, что вместо параметра-указателя (как в оригинальной АПИ-функции) использован var-параметр соответствующего типа (или вообще без указания типа), то нужно передавать не указатель на переменную, а саму переменную указанного типа. Например, в GetDiskFreeSpaceEx последний параметр объявлен как указатель PLargeInteger, а второй и третий параметры заменены на var (либо TLargeInteger либо без указания типа - в этом случае вместо TLargeInteger можно использовать другой совместимый тип, например Int64). Поэтому в примере MBo все три передаваемые параметра объявляются как Int64 (или можно TLargeInteger), два из них передаются как есть (как переменные), а для третьего передается указатель на переменную - @lpTotalNumberOfFreeBytes. Если же использовать твой (весьма своеобразный) подход с объявлением переменных как указателей PLargeInteger с выделением под них памяти через new, то нужно наоборот, последний параметр передавать как есть (как указатель), а для первых двух использовать разьименование указателей - lpFreeBytesAvailable^, lpTotalNumberOfBytes^. PS: Выделять память через new под простые переменные не нужно - так никто не делает. Локальные переменные "сами по себе" выделяются при вызове функции и уничтожаются при выходе из нее без всяких лишних телодвижений. И копия строки lpRootPathName:=StringToOleStr(..) тут совершенно не нужна - можно сразу передавать в функцию строковый литерал 'C:\' или переменную типа S:string с приведением ее типа к PChar(S) |
Сообщ.
#9
,
|
|
|
Всем спасибо. Проблема решена с помощью функций DiskSize, DiskFree.
procedure TForm1.Button1Click(Sender: TObject); var DS,DF:int64; begin DS:=DiskSize(3);DF:=DiskFree(3); ShowMessage('Диск С:'+#13#10+'DiskSize = '+inttostr(DS)+#13#10+ 'DiskFree = '+inttostr(DF)) end; |