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.
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.
Others have explained how to diagnose an AV.
Regarding the code itself, there are issues with it:
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:
you are ignoring errors from GetComputerName(), so you are not guaranteeing that CompName is even valid to pass to Result in the first place.
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.
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;
Related
Our programming dept just spent about a non-mythical man-month tracking down what we think is a bug in a 3rd party component, here's their copyrighted source code:
function TGDIPPicture.GetImageSizes: boolean;
var
multi: TGPImage;
pstm: IStream;
hGlobal: THandle;
pcbWrite: Longint;
begin
result := false;
if Empty then
Exit;
if FDataStream.Size = 0 then
Exit;
hGlobal := GlobalAlloc(GMEM_MOVEABLE, FDataStream.Size);
if (hGlobal = 0) then
raise Exception.Create('Could not allocate memory for image');
try
pstm := nil;
// Create IStream* from global memory
CreateStreamOnHGlobal(hGlobal, TRUE, pstm);
pstm.Write(FDataStream.Memory, FDataStream.Size,#pcbWrite);
multi := TGPImage.Create(pstm);
FWidth := multi.GetWidth;
FHeight := multi.GetHeight;
Result := true;
multi.Free;
finally
GlobalFree(hGlobal);
end;
end;
We found the problem was with TMS's AdvOfficeTabSet. If we added tabs, then it crashed, if we didn't add tabs then it didn't crash. (the crash was one of those un-debuggable app hangs that hits you 10 steps after the real problem).
Following Raymond Chen's advice I replaced GMEM_MOVEABLE with GPTR and it appears to have fixed the problem.
I'm wondering if anyone can tell me if the above code had any legitimate reason for using GMEM_MOVEABLE. AFAIK it's only for the clipboard and it should always be used with GlobalAlloc.
while I was typing this another programmer got an error in the GlobalFree function using my code. So, apparently this doesn't work either. Could really use some help here!
*CreateStreamOnHGlobal is a Windows API function. (which apparently prefers GMEM_MOVEABLE)
*TGPImage is part of TMS's implementation of the GDI+ library.
Jonathan has identified the obvious problem, that being the double free of the HGLOBAL. But as you have found, the use is GMEM_MOVEABLE is correct.
Frankly, the code seems needlessly complex. I suggest you use the built in stream adapter and avoid any GlobalAlloc. To get an IStream you just need to do this:
pstm := TStreamAdapter.Create(FDataStream);
That's it.
I've got some unexpected access violations for Delphi code that I think is correct, but seems to be miscompiled. I can reduce it to
procedure Run(Proc: TProc);
begin
Proc;
end;
procedure Test;
begin
Run(
procedure
var
S: PChar;
procedure Nested;
begin
Run(
procedure
begin
end);
S := 'Hello, world!';
end;
begin
Run(
procedure
begin
S := 'Hello';
end);
Nested;
ShowMessage(S);
end);
end;
What happens for me is that S := 'Hello, world!' is storing in the wrong location. Because of that, either an access violation is raised, or ShowMessage(S) shows "Hello" (and sometimes, an access violation is raised when freeing the objects used to implement anonymous procedures).
I'm using Delphi XE, all updates installed.
How can I know where this is going to cause problems? I know how to rewrite my code to avoid anonymous procedures, but I have trouble figuring out in precisely which situations they lead to wrong code, so I don't know where to avoid them.
It would be interesting to me to know if this is fixed in later versions of Delphi, but nothing more than interesting, upgrading is not an option at this point.
On QC, the most recent report I can find the similar #91876, but that is resolved in Delphi XE.
Update:
Based on AlexSC's comments, with a slight modification:
...
procedure Nested;
begin
Run(
procedure
begin
S := S;
end);
S := 'Hello, world!';
end;
...
does work.
The generated machine code for
S := 'Hello, world!';
in the failing program is
ScratchForm.pas.44: S := 'Hello, world!';
004BD971 B89CD94B00 mov eax,$004bd99c
004BD976 894524 mov [ebp+$24],eax
whereas the correct version is
ScratchForm.pas.45: S := 'Hello, world!';
004BD981 B8B0D94B00 mov eax,$004bd9b0
004BD986 8B5508 mov edx,[ebp+$08]
004BD989 8B52FC mov edx,[edx-$04]
004BD98C 89420C mov [edx+$0c],eax
The generated code in the failing program is not seeing that S has been moved to a compiler-generated class, [ebp+$24] is how outer local variables of nested methods are accessed how local variables are accessed.
Without seeing the whole Assembler Code for the whole (procedure Test) and only assuming on the Snippet you posted, it's probably that on the failing Snippet only a Pointer has been moved where on the correct version there is some Data moved too.
So it seems that S:=S or S:='' causes the Compiler to create a reference by it's own and could even allocate some Memory, which would explain why it works then.
I also assume that's why a Access Violation occurs without S:=S or S:='', because if there is no Memory allocated for the String (remember you only declared S: PChar) then a Access Violation is raised because non-allocated Memory was accessed.
If you simply declare S: String instead, this probably won't happen.
Additions after extended Commenting:
A PChar is only a Pointer to Data Structure, that must exist. Also another common Issue with PChar is to declare local Variables and then passing a PChar to that Variable to other Procs, because what happens is that the local Variable is freed once the routine ends, but the PChar will still point to it, which then raise Access Violations once accessed.
The only possibility that exists per Documentation is declaring something like that const S: PChar = 'Hello, world!' this works because the Compiler can resolve a relative Adresse to it. But this only works for Constants and not for Variables like on the Example above. Doing it like in the Example above needs Storage to be allocated for the string literal to which the PChar then points to like S:String; P:PChar; S:='Hello, world!'; P:=PChar(S); or similar.
If it still fails with declaring String or Integer then perhaps the Variable disappears somewhere along or suddenly isn't visible anymore in a proc, but that would be another Issue that has nothing to do with the existing PChar Issue explained already.
Final Conclusion:
It's possible to do S:PChar; S:='Hello, world!' but the Compiler then simply allocates it as a local or global Constant like const S: PChar = 'Hello, world!' does which is saved into Executable, a second S := 'Hello' then creates another one which is also saved into Executable and so on - but S then only points to the last one allocated, all others are still in the Executable but not accessible any more without knowing the exact Location, because S only points to the last one allocated.
So depending which one was the last S points either to Hello, world! or Hello. On the Example above i can only guess which one was the last and who knows perhaps the Compiler can only guess too and depending on optimizations and other unpredictable Factors S could suddenly point to unallocated Mem instead of the last one by the Time Showmessage(S) is executed which then raises a Access Violation.
How can I know where this is going to cause problems?
It's hard to tell at this point in time.
If we knew the nature of the fix in Delphi XE2 we'd be in a better position.
All you can do is refrain from using anonymous functions.
Delphi has had procedural variables, so the need for anonymous functions ready is not that dire.
See http://www.deltics.co.nz/blog/posts/48.
It would be interesting to me to know if this is fixed in later versions of Delphi
According to #Sertac Akyuz this has been fixed in XE2.
Personally I dislike anonymous methods and have had to ban people from using them in my Java projects because a sizable proportion of our code base was going anonymous (event handlers).
Used in extreme moderation I can see the use case.
But in Delphi where we have procedural variables and nested procedures... Not so much.
For some reason, trying to create a TLanguages object provided by the SysUtils header by using the singleton or by calling the constructor directly is causing trouble in the wild, where some users report this error (X varies):
Access violation at address X. Write of address X (at address X)
... when the following seemingly innocent line of code is executed:
TLanguages.Create;
To clarify, this is not related to context. I can put this line in any place I like (as the only line of code of an empty program for example), but the problem remains.
The weird part is that this class is part of Delphi's standard headers, which should not fail (right?).
constructor TLanguages.Create;
type
TCallbackThunk = packed record
POPEDX: Byte;
MOVEAX: Byte;
SelfPtr: Pointer;
PUSHEAX: Byte;
PUSHEDX: Byte;
JMP: Byte;
JmpOffset: Integer;
end;
var
Callback: TCallbackThunk;
begin
inherited Create;
Callback.POPEDX := $5A;
Callback.MOVEAX := $B8;
Callback.SelfPtr := Self;
Callback.PUSHEAX := $50;
Callback.PUSHEDX := $52;
Callback.JMP := $E9;
Callback.JmpOffset := Integer(#TLanguages.LocalesCallback) - Integer(#Callback.JMP) - 5;
EnumSystemLocales(TFNLocaleEnumProc(#Callback), LCID_SUPPORTED);
end;
The constructor attempts to use a member function as the EnumSystemLocales callback, which seems to be causing the crashes, because copying the TLanguages.LocalesCallback function to global scope and passing that to EnumSystemLocales works perfectly fine.
The struct contains the following Intel x86 assembly, where each item is given by its opcode:
pop edx
mov eax Self
push eax
push edx
jmp JmpOffset
Can anyone explain how the trick works and tell me why it's not working as expected?
It appears to be a known issue with older Delphi versions, related to DEP, as I guessed in comments to the question. It's clear that the code in the RTL cannot work when DEP is enabled.
Here's a link to confirm the theory: http://codecentral.embarcadero.com/Item/23411
Although that CodeCentral article includes code to fix the problem in Delphi 5, it looks like it will work in Delphi 7 too. The fix works by hooking the SysUtils.Languages function. So make sure you always use that rather than calling TLanguages.Create yourself, for obvious reasons.
Is there a way to (read-only) access any arbitrary memory location without running into an access violation? I thought that each process has its own virtual adress space and that it can read all available memory locations...seems not to be the case, since my program hangs if I do something like
var
IntPtr : PInteger;
AnInteger : Integer;
...
IntPtr := $100;
AnInteger := IntPtr^;
I'm still trying to write my low-level recursive size-of function and try to detect if a data member is an object reference or not.
Thanks!
You can only access your own process' memory via pointers, and even then it's only those parts that have been mapped for your process. There are debugger hooks that will give you access to other processes memory; but they're tricky to get right.
So if you really want to iterate through your process memory, you can probably find the functions that you need here: http://msdn.microsoft.com/en-us/library/ms878234.aspx
AFAIR in windows also part of kernel is mapped to your processes memory space (which is the reason to not have all of 4G available for your process).
Your application hangs? There is something wrong with your application then.
Usually, there will be a simple AV. An AV leads to error message. That is all.
BTW, you should not be scared of it - just handle it.
function IsValidObject(const AObj: Pointer { or TObject} ): Boolean;
begin
try
...
// place your checking code there
Result := ...;
except
on EAccessViolation do
Result := False;
end;
end;
The only exception to this rule that comes to mind is if you're writing some sort of exception handler and want to detect if there is a valid object. In that case you probably do not want to generate an exception in handler of exception ;)
If this is your case - then try to use this code (this is an example):
function GetReadableSize(const AAddress: Pointer; const ASize: Cardinal): Cardinal;
const
ReadAttributes = [PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE];
var
MemInfo: TMemoryBasicInformation;
Tmp: Cardinal;
begin
Result := 0;
if (VirtualQuery(AAddress, MemInfo, SizeOf(MemInfo)) = SizeOf(MemInfo)) and
(MemInfo.State = MEM_COMMIT) and (MemInfo.Protect in ReadAttributes) then
begin
Result := (MemInfo.RegionSize - (Cardinal(AAddress) - Cardinal(MemInfo.BaseAddress)));
if Result < ASize then
begin
repeat
Tmp := GetReadableSize(Pointer(DWord(MemInfo.BaseAddress) + MemInfo.RegionSize), (ASize - Result));
if (Tmp > 0) then
Inc(Result, Tmp)
else
Result := 0;
until (Result >= ASize) or (Tmp = 0);
end;
end;
end;
function IsValidBlockAddr(const AAddress: Pointer; const ASize: Cardinal): Boolean;
begin
Result := (GetReadableSize(AAddress, ASize) >= ASize);
end;
But usually you should prefer first approach.
If you want to safely try reading any memory address with no fuss, and get a nice error-code rather than an exception when the memory you're trying to read is inaccessible, the function you want to use is in the WinAPI: ReadProcessMemory.
Memory might not be mapped at all adresses. And the lower 4kb or so are always protected afaik.
However if it is for VMs, if you control the memory manager, you can build up a list with all memory ranges that your application mapped.
Unless there is some Magic way that I'm unaware of, I'm pretty sure you can't do this. Windows uses protected memory, which means that you can't access anything that hasn't been specifically allocated to you.
There is DMA, but it's reserved for driver-level software.
In old Windows, 95, 98, Me you could do some inline asm in your function and read/write some arbitrary memory location or hardware port...
function ReadPortByte : Byte;
var
Base : Word;
begin
Base := FAddress;
asm
mov DX, Base
in AL, DX
mov Result, AL
end;
end;
You can still do this by using a device driver, but Vista may cause you some problems unless the driver is compiled properly for Vista and above.
There are several free ones out there and worth experimenting with.
John
I'm testing DelphiModbus library on Delphi 2009 and don't get quite the results I want. I think the problem lies with the following line on IdModbusClient.pas:
Move(Buffer, ReceiveBuffer, iSize);
It looks like ReceiveBuffer is set to some garbage.
Buffer is defined as TIdBytes (from Indy components)
ReceiveBuffer is defined as TCommsBuffer:
TModBusFunction = Byte;
TModBusDataBuffer = array[0..256] of Byte;
TCommsBuffer = packed record
TransactionID: Word;
ProtocolID: Word;
RecLength: Word;
UnitID: Byte;
FunctionCode: TModBusFunction;
MBPData: TModBusDataBuffer;
Spare: Byte;
end; { TCommsBuffer }
And iSize is of course the size of the Buffer in bytes.
I wonder if this has anything to do with unicode conversion?
Indy's TIdBytes type is a dynamic array, defined in IdGlobal.pas:
type
TIdBytes = array of Byte;
You can't pass a variable of that type directly to Move and expect it to work because it will only copy the four-byte reference stored in that variable. (And if you told it to copy more than four bytes, then it will proceed to copy whatever else resides in memory after that variable — who knows what.) Given these declarations:
var
Buffer: TIdBytes;
ReceiveBuffer: TCommsBuffer;
The way to call Move on those variables is like this:
if Length(Buffer) > 0 then
Move(Buffer[0], ReceiveBuffer, iSize);
It works like that because Move's parameters are untyped, so you need to pass the value that you want to copy, not a pointer or reference to the value. The compiler handles the referencing by itself.
The code is a little weird because it looks like you're just copying one byte out of Buffer, but don't let it bother you too much. It's a Delphi idiom; it's just the way it works.
Also, this has nothing to do with Delphi 2009; it's worked this way ever since Delphi 4, when dynamic arrays were introduced. And Move has been this way forever.
It looks to me like you're missing a couple of pointer dereferences, and therefore corrupting memory addresses.
If I'm not mistaken, the Move() call should be:
Move(Buffer^, ReceiveBuffer^, iSize);
I've removed my totally worthless post content (leaving it for posterity and to give someone a good laugh).
I don't see anything that would be affected by Unicode at all. I'm going to edit the tags to include Delphi (without the 2009), as some of the CodeGear Delphi developers are currently posting there. Perhaps one of them can see what's happening.
I made up a contrived example (actually a pretty useless one):
uses
IdGlobal;
type
TModBusFunction = Byte;
TModBusDataBuffer = array[0..256] of Byte;
TCommsBuffer=packed record
TransactionID: Word;
ProtocolID: Word;
RecLength: Word;
UnitID: Byte;
FunctionCode: TModBusFunction;
MBPData: TModBusDataBuffer;
Spare: Byte;
end;
procedure TForm1.FormShow(Sender: TObject);
var
Buffer: TIdBytes;
ReceiveBuffer: TCommsBuffer;
//iSize: Word;
begin
FillChar(ReceiveBuffer, SizeOf(ReceiveBuffer), 0);
ReceiveBuffer.TransactionID := 1;
ReceiveBuffer.ProtocolID := 2;
ReceiveBuffer.RecLength := 3;
ReceiveBuffer.UnitID := 4;
ReceiveBuffer.FunctionCode := 5;
FillChar(ReceiveBuffer.MBPData[0], SizeOf(ReceiveBuffer.MBPData), 6);
ReceiveBuffer.Spare := 7;
SetLength(Buffer, SizeOf(ReceiveBuffer));
Move(ReceiveBuffer, Buffer, SizeOf(ReceiveBuffer));
Move(Buffer, ReceiveBuffer, SizeOf(ReceiveBuffer));
ReceiveBuffer.UnitID := 8;
end;
I then set a breakpoint on the last line before the end, and ran it. When the breakpoint was hit, I looked at the contents of ReceiveBuffer using ToolTip Evaluation, and everything looked perfectly fine. I could see all of the proper values, including the ReceiveBuffer.Spare being 7. I then single stepped, and looked at ReceiveBuffer.UnitID; it in fact had a value of 8.
However, pressing F9 to continue running (expecting to be able to just close the form and end the application), I ended up in the CPU window and got a message from Vista that the application wasn't responding. I was just outside ntdll.DebugBreakPoint, IIRC, and single stepping brought me into ntdll.RtlReportException. I'm not quite sure what's happening, but it ain't good. Still looking.
Edit2: I ran it again, with the same results. However, this time I noticed before I used Ctrl+F2 to terminate the app that Vista was giving me a popup tooltray window indicating that "Project1.exe has been closed" and mentioning DEP (which I have enabled in hardware on this machine).