AccessViolationException in Delphi - impossible (c

2019-09-26 07:25发布

问题:

Delphi XE. Windows 7.

There is a function (please see a code below) or I:=0 that causes an AV error in a big project. There is no the error with the same function in a new project!!! I deleted everything from the big project, and I left only a button and that function. It still causes the error...

A line with the error:

if ISAeroEnabled then // this line is a cause
       i:=0;         // or this line

I set breakpoints everywhere (I checked the whole function, I set breakpoints on EACH LINE -> no errors in the function), a debugger shows me that the error is in i:=0;

If to delete a function (and leave i:=0;) -> all is ok!

The error message: First chance exception at $747FB727. Exception class EAccessViolation with message 'Access violation at address 004AE5AF in module 'MngProject.exe'. Write of address 0017FFF8'. Process MngProject.exe (4980)

Why does it work in a new project but not in mine?

Here's the whole project: http://www.2shared.com/file/UP22Om4j/Bug.html

The code:

unit MainFormModule;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  StdCtrls;
type
  TMainForm = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public

    { Public declarations }
  end;

var
     mainform:tmainform;
implementation
{$R *.dfm}

function  ISAeroEnabled: Boolean;
type
  _DwmIsCompositionEnabledFunc = function(IsEnabled: PBoolean): HRESULT; stdcall;
var
  Flag                       : Boolean;
  DllHandle                  : THandle;
  OsVersion                  : TOSVersionInfo;
  DwmIsCompositionEnabledFunc: _DwmIsCompositionEnabledFunc;
begin
  Result:=False;
  ZeroMemory(@OsVersion, SizeOf(OsVersion));
  OsVersion.dwOSVersionInfoSize := SizeOf(TOSVERSIONINFO);

  if ((GetVersionEx(OsVersion)) and (OsVersion.dwPlatformId = VER_PLATFORM_WIN32_NT) and (OsVersion.dwMajorVersion >= 6)) then //is Vista or Win7?
  begin
    DllHandle := LoadLibrary('dwmapi.dll');
    if DllHandle <> 0 then
    begin
      @DwmIsCompositionEnabledFunc := GetProcAddress(DllHandle, 'DwmIsCompositionEnabled');
      if (@DwmIsCompositionEnabledFunc <> nil) then
      begin
        DwmIsCompositionEnabledFunc(@Flag);
        Result:=Flag;
      end;
    end;
      FreeLibrary(DllHandle);
  end;
end;

procedure Tmainform.Button1Click(Sender: TObject);
var i:integer;
begin
    if ISAeroEnabled then // AV is here
       i:=0;              // Or here
end;
end.

回答1:

Try changing PBoolean to PBOOL

function(IsEnabled: PBOOL): HRESULT; stdcall;

var
  Flag: BOOL;

PBoolean is a pointer to a Pascal Boolean which is 1 byte in size. PBOOL is a pointer to a Windows (C based) BOOL, which is 4 bytes in size. You need to match the size expected by windows.

In general, when translating Windows API calls to Delphi, use the same named data type as the API. Windows.pas has type definitions mapping these to Delphi types, e.g. type BOOL = LongBool;

Also it is usual (but not required) in Delphi to change pointer parameters to var. A var parameter is Pascal syntactic sugar for pass-by-reference which isn't available in C.

function(var IsEnabled: BOOL): HRESULT; stdcall;
....
    DwmIsCompositionEnabledFunc(Flag); // no @ operator

NOTE: I can't test this, as I only have XP available.