Michael Vincent
Michael Vincent

Reputation: 1660

Access Violation - how do I track down the cause?

I'm getting an access violation when I close a form in my application. It seems to happen only after I have access a database a couple of times, but that doesn't seem to make sense.

I have traced through and put outputdebugstring messages in all the related OnDestroy() methods, but the AV appears to be outside of my code.

This is the text of the message:

Access violation at address 00405F7C in module 'MySoopaApplication.exe'. Read of address 00000008.

How do I find where in the application 00405F7C is?

What tools are available in Delphi 10.1 Berlin to help me with this?

Edit: added a bit more info ... when clicking "Break" the IDE always takes me to this piece of code in GETMEM.INC:

@SmallPoolWasFull:
  {Insert this as the first partially free pool for the block size}
  mov ecx, TSmallBlockType[ebx].NextPartiallyFreePool

Further edit: well, I found the culprit, though I can't honestly say that the debug tools got me there - they just seemed to indicate it wasn't in my code.

I had used code from the net that I used to find the Windows logged in user - this is it:

function GetThisComputerName: string;
var
  CompName: PChar;
  maxlen: cardinal;
begin
  maxlen := MAX_COMPUTERNAME_LENGTH +1;
  GetMem(CompName, maxlen);
  try
    GetComputerName(CompName, maxlen);
    Result := CompName;
  finally
    FreeMem(CompName);
  end;
end;

once I had replaced the code with a simple result := '12345' the AVs stopped. I have no changed it to this code:

function GetThisComputerName: string;
var
  nSize: DWord;
  CompName: PChar;
begin
  nSize := 1024;
  GetMem(CompName, nSize);
  try
    GetComputerName(CompName, nSize);
    Result := CompName;
  finally
    FreeMem(CompName);
  end;
end;

which seems to work and, as a bonus, doesn't cause AVs.

Thanks for your help, much appreciated.

Upvotes: 2

Views: 13530

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 595305

Others have explained how to diagnose an AV.

Regarding the code itself, there are issues with it:

  1. Most importantly, you are not allocating enough memory for the buffer. GetMem() operates on bytes but GetComputetName() operates on characters, and in this case SizeOf (Char) is 2 bytes. So you are actually allocating half the number of bytes that you are reporting to GetComputerName(), so if it writes more than you allocate then it will corrupt heap memory. The corruption went away when you over-allocated the buffer. So take SizeOf(Char) into account when allocating:

    function GetThisComputerName: string;
    var
      CompName: PChar;
      maxlen: cardinal;
    begin
      maxlen := MAX_COMPUTERNAME_LENGTH +1;
      GetMem(CompName, maxlen * SizeOf(Char)); // <-- here
      try
        GetComputerName(CompName, maxlen);
        Result := CompName;
      finally
        FreeMem(CompName);
      end;
    end;
    

In addition to that:

  1. you are ignoring errors from GetComputerName(), so you are not guaranteeing that CompName is even valid to pass to Result in the first place.

  2. You should use SetString(Result, CompName, nSize) instead of Result := CompName, since GetComputerName() outputs the actual CompName length. There is no need to waste processing time having the RTL calculate the length to copy when you already know the length. And since you don't check for errors, you can't rely on CompName being null terminated anyway if GetComputerName() fails.

  3. You should get rid of GetMem() altogether and just use a static array on the stack instead:

    function GetThisComputerName: string;
    var
      CompName: array[0..MAX_COMPUTERNAME_LENGTH] of Char;
      nSize: DWORD;
    begin
      nSize := Length(CompName);
      if GetComputerName(CompName, nSize) then
        SetString(Result, CompName, nSize)
      else
        Result := '';
    end;
    

Upvotes: 3

MartynA
MartynA

Reputation: 30715

Under Tools|Options in the IDE go to Embarcadero Debuggers | Language Exceptions and make sure Notify on Language Exceptions is checked. Also under Project Options | Compiling, make sure Debugging | Use debug DCUs is checked.

Allow the exception to happen, then go to View | Debug Windows | Call stack and you should be able to see exactly where it occurred. The fact that it occurs after db access is probably because that causes some object to be created which generates the AV when it is destroyed. Possibly because it is being Free()ed twice.

If that doesn't solve it, you may may need an exception-logging tool like madExcept mentioned by DavidH.

Read of address 00000008.

The fact that this address is a low number is suggestive of it being the address of a member of an object (because they are typically at low offsets from the base address of the object).

How do I find where in the application 00405F7C is?

With your app running, in the IDE go to Search | Go to Address. This should find it if the exception is in your application and not in some related module like a .DLL it is using. The menu item is enabled once the application is running in the IDE and stopped at a breakpoint. Also there is a compiler command line switch to find an error by address.

Upvotes: 5

Related Questions