I am debugging a DirectShow filter I created with the DSPACK code library using Delphi 6 Pro. When a breakpoint I set is hit in one particular unit named BaseClass.pas, and I begin tracing, the Execution Point jumps to strange places in the source code. This usually indicates that the source code being traced does not match the source code that was compiled into one of the packages being used by the Delphi application. Oddly enough it is only the BaseClass unit since I have traced other units belonging to the DSPACK code library and they do not exhibit this problem. I am not using run-time packages.
I scanned my disk and found only one copy of BaseClass.dcu with a modification date equal to the last time I built the program. I have not modified the source for that unit or any other belonging to DSPACK. Since my Filter is part of the main application this indicates that BaseClass.pas would be subject to a dual use situation since it is used to build the DSPACK component package (dpk), and is also referenced by my main application directly via the TBCSource object my Filter descends from. Note, I did try adding the unit PAS file directly to my Project but that didn't fix anything.
I also went back and re-opened each of the DSPACK package files and did a full re-build. None of this helped. Is there something else I can try to get the source synchronized with the compiled image of the BaseClass unit? Or is a different problem altogether and if so, what is it and how can I fix it?
Sometimes this happens when code is copied/pasted from web pages or other sources, and the lines don't end with CR/LF pairs (#13#10 or 0x0D0A, standard for Windows) but end in only LF (#10 or 0x0A, typically the line ending in *nix systems) or CR (#13 or 0x0D, typical with Mac OSX/iOS). The incorrect line terminators confuse the debugger - this has been an issue for the past several Delphi versions.
You can sometimes fix this by opening the source file using a text editor like Notepad, making a small meaningless change (insert and then delete a blank line, for instance), and then save the file.
I had same problem and made a similar utility. Fixed it.
Basically, just this:
procedure adjustCRLF(filename : String);
var
strList : TStringList;
begin
strList := TStringList.Create;
try
strList.LoadFromFile(filename);
strList.Text := AdjustLineBreaks(strList.Text);
strList.SaveToFile(filename);
finally
strList.Free;
end;
end;
There is another way this can happen: if the IDE erroneously opens another source file with the same name (but different, such as an earlier version) then all the debug points will be incorrect, and the debugger will even allow you to step through the incorrect file.
I've seen Delphi 7 do this once.
Make sure that when you rebuild it, that in the compiler options for your project that you have "Debug Information" turned on. In fact, most of the options under Debugging should be set in your project's Compiler options.
Also, if you haven't already, restart Delphi.
Related
I have a Delphi5 application which exports a file (.pdf) and a very small metadata file to a network location. The intention is that these 2 files should be processed, and then removed, by a polling .NET application.
My approach is to
Write the metadata file with the extension '.part'
Generate the .pdf
Rename the .part file to .dat
The .NET process is looking for files with the extension '.dat' only, so I would expect there to be no conflict between the 2 reader/writers. However, the .NET process is occasionally logging the following error ...
System.IO.IOException: The process cannot access the file '\\server\Path\FileName.dat' because it is being used by another process.
(I say occasionally - we are currently testing, so when volumes increase this may become much more of an issue)
The Delphi code looks like this :
AssignFile(FTextFile, Format('%s\%s.part', [DMSPath, FullFileName]));
try
try
ReWrite(FTextFile);
Writeln(FTextFile, MetaDataString);
finally
CloseFile(FTextFile);
end;
except
raise ELogFileException.Create( LOGFILEWRITEFAILURE );
end;
Then there is a separate method which performs the following lines of code
if FindFirst(Format('%s\*.part',[DMSPath]), faAnyFile, SearchRec) = 0 then begin
repeat
OldName := Format('%s\%s',[DMSPath, SearchRec.Name]);
NewName := Format('%s\%s',[DMSPath, ChangeFileExt(SearchRec.Name, '.dat')]);
RenameFile(OldName, NewName);
until FindNext(SearchRec) <> 0;
FindClose(SearchRec);
end;
I cannot see anything inherently wrong with this code and we have a couple of remedies in mind, but I have 2 questions
Should I try a different technique to more reliably protect the '.dat' file until it is fully ready
What circumstances could be causing this?
So far there has been one suggested cause - Antivirus software.
Any suggestions as to how the file might be produced differently? Note that my application is Delphi5; I wondered if there was a newer, more 'atomic' version of the 'MoveFileA' WinApi call I could use.
In the past, we had a problem with file being locked like that. Investigation pointed to Windows' Prefetch. The file being affected were not on a network directory though.
As far as I know, Prefetch only work on process startup and/or while booting (Controlled by a registry key), so it might not apply to your current situation.
You can check the "C:\"Windows"\Prefetch\" directory. If prefetch is active, it should contains multiple *.pf files. If there is one with your executable filename, it might be worth investigating.
Personally speaking, because there are multiple files involved, I'd create a separate lock file (eg, myfile.lck) that you write first. If the polling app sees it in the folder, then it stops looking for other files. Once that file is gone, then it dives deeper. I don't know if this would solve the problem you're encountering or not, but I'd give it a try. (Files with .dat extensions are frequently created by malicious evildoers, so they can raise spurious issues through other sources, like AV software. A lock file with 0 bytes in it is generally harmless and disregarded.)
I use Delphi RAD Studio 2010 and DecimalRounding_JH1.pas from http://cc.embarcadero.com/item/21909.
It works good, but I don't know why, in some old machines (Pentium IV with Windows XP SP3), the round fails after access to printer.printerindex. I have checked that the problem not is Windows XP due it works in other machines with this OS.
I have made a simple project that round an extended value with DecimalRounding_JH1 (drHalfUp) with two decimals (1.105 -> rounds to 1.11). But If I read printer.printerindex, then 1.105 rounds to 1.10).
I thought it could be the "FDIV bug", but compiling with "FDIV safe" doesn't resolve the problem.
The code:
var d1,d2:extended;
i:integer;
begin
d1:=1.105;
d2:=DecimalRounding_JH1.DecimalRoundExt(d1,2,drHalfUp);
memo1.lines.add(FloatToStr(d2)); // --> shows 1.11 (OK)
i:=Printer.printerindex;
d2:=DecimalRounding_JH1.DecimalRoundExt(d1,2,drHalfUp);
memo1.lines.add(FloatToStr(d2)); // --> shows 1.10 (ERROR!!!)
...
I know that it is very strange, but I've tested it and It's as I said.
What could I do?
Edited:
If I add Printer.printerindex:=1; (for example) before i:=Printer.printerindex; then again it works good. Reading printer unit, the difference is about execute "SetToDefaultPrinter" or not:
function TPrinter.GetPrinterIndex: Integer;
begin
if FPrinterIndex = -1 then SetToDefaultPrinter;
Result := FPrinterIndex;
end;
thanks in advance.
Certain parts of the system printer libraries have a rather nasty habit of modifying the 8087 control word. You should restore it to its default value after using methods and properties of Printer.
For example, you might write it like this:
Set8087CW(Default8087CW);
The comments in my codebase suggest that you only need to do this after the VCL printer code has been initialized for the first time. So you could deal with this in your program's startup. Read Printer.PrinterIndex and then immediately set the control word to its desired value.
What tools would you recommend for unit testing in Delphi.
I've used FastMM4 for memoryleak testing.
And MadExcept, both rule, but it doesn't help me test the logic in my programs.
I would like some alternatives, so don't all rush to suggest DUnit :-).
Any suggestions?
The most commonly used is DUnit. It's actually included in modern versions of Delphi but if your version doesn't come with it you can download it from Sourceforge.
DUnit comes with D2007.
-->File -->New -->Other
Select Unit Test from the dialogue that pops up.
There are a couple of good demo videos on it's use, I'll see if I can dig one up.
This is a pretty good one and others come up on the right:
http://www.youtube.com/watch?v=nyZnfxDqThE
You could take a look at the unit testing classes available in our SynCommons open source unit. It's used in our Open-Source framework for all regression tests. It's perhaps not the best, but it's worth taking a look at it. See http://blog.synopse.info/post/2010/07/23/Unit-Testing-light-in-Delphi
In the up-to-come 1.13 version, there is also a new logging mechanism with stack trace of any raised exception and such, just like MadExcept, using .map file content as source.
It's now used by the unit testing classes, so that any failure will create an entry in the log with the source line, and stack trace:
C:\Dev\lib\SQLite3\exe\TestSQL3.exe 0.0.0.0 (2011-04-13)
Host=Laptop User=MyName CPU=2*0-15-1027 OS=2.3=5.1.2600 Wow64=0 Freq=3579545
TSynLogTest 1.13 2011-04-13 05:40:25
20110413 05402559 fail TTestLowLevelCommon(00B31D70) Low level common: TDynArray "" stack trace 0002FE0B SynCommons.TDynArray.Init (15148) 00036736 SynCommons.Test64K (18206) 0003682F SynCommons.TTestLowLevelCommon._TDynArray (18214) 000E9C94 TestSQL3 (163)
The difference between a test suit without logging and a test suit with logging is only this:
procedure TSynTestsLogged.Failed(const msg: string; aTest: TSynTestCase);
begin
inherited;
with TestCase[fCurrentMethod] do
fLogFile.Log(sllFail,'%: % "%"',
[Ident,TestName[fCurrentMethodIndex],msg],aTest);
end;
The logging mechanism can be used to trace recursive calls. It can use an interface-based mechanism to log when you enter and leave any method:
procedure TMyDB.SQLExecute(const SQL: RawUTF8);
var ILog: ISynLog;
begin
ILog := TSynLogDB.Enter(self,'SQLExecute');
// do some stuff
ILog.Log(sllInfo,'SQL=%',[SQL]);
end; // when you leave the method, it will write the corresponding event to the log
It will be logged as such:
20110325 19325801 + MyDBUnit.TMyDB(004E11F4).SQLExecute
20110325 19325801 info SQL=SELECT * FROM Table;
20110325 19325801 - MyDBUnit.TMyDB(004E11F4).SQLExecute
Here the method name is set in the code ('SQLExecute'). But if you have an associated .map file, the logging mechanism is able to read this symbol information, and write the exact line number of the event.
Note that by default you have time and date written to the log, but it's also possible to replace this timing with high-resolution timestamps. With this, you'll be able to profile your application with data coming from the customer side, on its real computer. Via the Enter method (and its auto-Leave feature), you have all information needed for this. ;)
Like this:
0000000000000B56 + TTestCompression(00AB3570).000E6C79 SynSelfTests.TTestCompression.TestLog (376)
0000000000001785 - TTestCompression(00AB3570).000E6D09 SynSelfTests.TTestCompression.TestLog (385)
I still need to write some tool to compute the profiling, but there is already a dedicated TSynLogFile class able to read the .log file, and recognize its content.
The first time the .map file is read, a .mab file is created, and will contain all symbol information needed. You can send the .mab file with the .exe to your client, or even embed its content to the .exe. This .mab file is optimized: a .map of 927,984 bytes compresses into a 71,943 .mab file.
So this unit could be recognized as the natural child of DUnit and MadExcept wedding, in pure OpenSource. :)
Additional information is available on our forum. Feel free to ask. Feedback and feature requests are welcome! Works from Delphi 6 up to XE.
I've installed JCL into Delphi 2010. In the following code
try
raise Exception.Create('Error Message');
except
on E: Exception do
begin
ResultStatus := JclLastExceptStackListToStrings(sl, True, True, True, True);
end;
end;
Also I have this call in the initialization section:
initialization
JclStartExceptionTracking;
ResultStatus is false and sl is empty. I have set options to generate map and .jdbg files. Other functions, like ProcByLevel work fine. Is there something else I need to do to make JclLastExceptStackListToStrings work?
From JclDebug.pas file:
Last modified: $Date:: 2010-09-07 19:43:19 +0200 (mar., 07 sept. 2010)
Revision: $Rev:: 3331
Try calling
JclStartExceptionTracking;
at the very start of your application.
[Edit]You already are. Then I'm currently out of suggestions. [/Edit]
I use:
JclStackTrackingOptions := [stStack, stExceptFrame, stRawMode, stAllModules,
stStaticModuleList];
I solve this problem in Delphi XE 1.
open Project Options menu
open Delphi Compiler tree node
select Linking tree node
enable Debug Information checkbox
I've been stuck with this problem for a few days. JCL stack tracking was working with my programs and I was able to pinpoint the error location. Somehow it just stopped working.
Then I worked with JCL's demo to see if it is working without any problem. It worked very well. I set all the project options same with the demo and yet my program did not dump stack while the demo was working everytime.
Suddenly I noticed that the only difference between my program and the demo is CPU architecture. Demo is 32 bits and my program is 64 bits.
I compiled the JCL demo as 64 bits and it stopped working too. It is working only with 32 bits code. Not 64 bits.
Unfortunatelly my program needs 6-7 gigabytes of memory and I cannot use the 32 bits mode. I suggest to use Eurekalog since it is working for both architectures.
Delphi 2007 on windows 7 just does nothing on the saveDialog.Execute call. I have seen another person mention this a few weeks back but it was with Borland c++.
See the thread "TOpenDialog.Execute not working " on embarcadero newsgroups.
Problem there was resolved by deleting the executable name from
"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options"
I got the same problem (savedialog not working) in windows XP.
After lots of unsuccessful attempts according to the voluminous exchanges in the embarcadero group you mention (https://forums.embarcadero.com/thread.jspa?messageID=196950&tstart=0#196950).
I found what the reason was : the initial file dir and filename of the Savedialog12 were bad, contradicting each other, the filename containing the fullpath of the last file I had opened (I had thought it was smart to prepare the saving of the file I had opened last; unfortunately what I had put in the initialdir was equal to what I had put in the filename !)
The problem was already solved by clearing both fields of the save dialog.
Further, my initial goal to prepare the saving was reached by putting valid values in the involved fields :
SaveDialog1.FileName:=ExtractFileName(Opendialog1.Filename);
SaveDialog1.InitialFileDir:=ExtractFilePath(Opendialog1.Filename);