Cast from Variant String to Double ignores decimal point - delphi

I have a few simple lines of code:
var
vRecordValue:Double;
begin
vRecordValue:= someVariant;
Where someVariant is a string (type 256).
On a production system we have a variant of '23.4' and vRecordValue becomes 234. I can only reproduce this is my system separator is ','. In our production system this was the case but we have changed the setting and still see this occurring. It is interesting to note that we cannot reproduce this at all except in debug mode (no idea what the relationship is here), and by doing a simple test.
I fixed this with an explicit conversion:
vRecordValue:= StrToFloatDef(VarToStrDef(someVariant, '0'), 0);
Questions:
Is there any other way that a decimal point could be being ignored?
Can someone please provide a reference for how the default cast from string to double is performed? I want to understand the difference.
Is there any way that the separator used in the default cast is being cached from the value when the code was first run? ... Doubtful but desperate to understand.
Thanks,
Wayne.

It is your regional settings
I've made this samall demo applikation inorder for testing it:
procedure TForm60.FormCreate(Sender: TObject);
var
vRecordValue: Double;
sRecordValue: Variant;
begin
sRecordValue:= '23.4';
vRecordValue:= StrToFloatDef(VarToStrDef(sRecordValue, '0'), 0);
ShowMessage(FloatToStr(vRecordValue));
sRecordValue:= '23,4';
vRecordValue:= StrToFloatDef(VarToStrDef(sRecordValue, '0'), 0);
ShowMessage(FloatToStr(vRecordValue));
end;
In Denmark (Where I live) the we use , as decimal seperator, so the last one gives me the correct result

In regards to my third question. The problem reoccurred this week. I didn't realize that the locale/regional settings are only being set for the current user.
To copy the settings for system users, we needed to use:
Control Panel > Region and Language > Administrative > Copy Settings.
This is why I had inconsistencies when testing. It depended on the user my application was running as, and that users settings.

Related

Win10 Theme used in running program

Is there any information available to the running program about which Win10 theme is being used?
I am looking primarily to get more debug information, as I have a user with a reported "scrambled GUI".
Is there any way to kill themes, or force to a default theme, from within the running program?
If I understood your question correctly I would recommend to you to use SetWindowTheme.
You should check out information provided with link above to see what docs.microsoft (former msdn) tells us in Remark section:
When pszSubAppName and pszSubIdList are NULL, the theme manager removes the previously applied associations. You can prevent visual styles from being applied to a specified window by specifying an empty string, (L" "), which does not match any section entries.
So, having this in your mind you can easily restrict theming for any window those Handle is known by calling SetWindowTheme with both arguments set to "there should be whitespace between braces".
By the way I wouldn't highly recommend to kill themes for entire OS from your application. Personally, I delete any soft that has such a useful feature.
Important addendum!
I've read your question again after some time passed and I can answer to the first part of your question.
You can obtain theme's info via UXTheme unit.
Primarily you must use this two functions:
GetCurrentThemeName
GetThemeDocumentationProperty
Here is some code that shows how to do this.
uses
..., UXTheme;
var
ThemeName: Array[0..512] of Char;
ThemeColorScheme: Array[0..512] of Char;
ThemeSizeName: Array[0..512] of Char;
PropertyName: Array[0..512] of Char;
begin
UXTheme.GetCurrentThemeName(#ThemeName, SizeOf(ThemeName), #ThemeColorScheme, SizeOf(ThemeColorScheme), #ThemeSizeName, SizeOf(ThemeSizeName));
UXTheme.GetThemeDocumentationProperty(#ThemeName, SZ_THDOCPROP_CANONICALNAME, #PropertyName, SizeOf(PropertyName));
end;
Please read about GetThemeDocumentationProperty attentively - this function takes one of arguments that is in a charge of what property of OS theme will be return. According to docs.microsoft, the flag SZ_THDOCPROP_CANONICALNAME in the code above will return string property for
Retrieves the name of the theme.
So you will be able to check what theme is being using at the moment.
But you still can just disable themes for your own application.

Delphi : Field 'False' not found. occurred only on my computer

I have a TCustomClientDataSet (in memory) which is used to do some filtering in a Delphi program.
My filtering code look like :
CDSFilteredGroup.Filter := 'ACP_type = 1 AND ACP_by_default <> False';
CDSFilteredGroup.Filtered := True;
When I compile and run it, I got the following error only with Delphi (XE4) installed on my computer :
CDSFilteredGroup: Champ 'False' non trouvé.
Translation: Field 'False' not found. The same code compile and run perfectly fine on my colleagues computers, but not on mine. There must be an option somewhere that I miss, any idea ?
Remember, the code is ok and working...
The TExprParser instance used for handling the filter string relies on the resourcestrings given as STextTrue and STextFalse. You probably have another language than English configured for your IDE library. Try switching to English and see if the error disappears.
To workaround that problem in general replace the text "False" with the content of the resourcestring STextFalse.
BTW, this was corrected in later Delphi versions that also accept the literal "False" even in non English environments.

Error loading file with full name containing spaces in directory with delphi

I am using XE8, win 8.1.
When trying load a file with spaces in directory, I am getting a exception of syntax name of the file or directory is invalid.
If I use imageen dialog to preview the file, no erros are found.
I did two tests with the procedure load_file1 and load_file2 and I have the same problem.
Is there a wrokaround to solve it?
function get_file:string;
begin
result:='"C:\Compartilhada\dicomserver versoes\dicomserverx\data\Genesis-1000\1.2.410.200013.1.215.1.200912141600580009_0001_000001_13061821270002.dcm"'
end;
procedure load_file1;
var fStm:Tstream;
p1:string;
begin
p1:=get_file;
fStm := tFileStream.Create( p1, fmOpenRead or fmShareDenyNone ); //->Error Here
try
TBlobField(FieldByName('dicom')).LoadFromStream(fStm);
Post;
finally
fSTm.Free;
end;
end;
procedure load_file2;
p1:string;
begin
p1:=get_file;
TBlobField(FieldByName('dicom')).LoadFromFile(p1); //-->Error Here
Post;
end;
Remove the double quote marks from your string. It should be:
'C:\Compartilhada\dicomserver versoes\dicomserverx\data\Genesis-1000\1.2.410.200013.1.215.1.200912141600580009_0001_000001_13061821270002.dcm'
You might use " for paths containing spaces in some situations, for instance a command interpreter. But at the API level, it is simply not needed. And indeed it is a mistake as you have discovered. The double quote character " is actually a reserved character in a file name. That is documented on MSDN:
Naming Files, Paths, and Namespaces: Naming Conventions
The following fundamental rules enable applications to create and process valid names for files and directories, regardless of the file system:
...
Use any character in the current code page for a name, including Unicode characters and characters in the extended character set (128–255), except for the following:
The following reserved characters:
< (less than)
> (greater than)
: (colon)
" (double quote)
/ (forward slash)
\ (backslash)
| (vertical bar or pipe)
? (question mark)
* (asterisk)
...
...
In comments below you indicate that the code in the question does not reflect your actual problem. Which makes me wonder how you expect us to help. Your real problem is not the error message produced by the specific code, but that your debugging skills are letting you down. Let me try to explain how to debug a problem like this.
First of all, you are passing a file name to LoadFromFile or TFileStream.Create. These calls fail with an error that indicates that the file name is not valid.
So, when faced with that knowledge, the first step is to check the value of the file name that you are passing. Use debugging techniques to do that. Either the IDE debugger, or logging.
Once you have identified what value you are actually passing to these functions you can try to work out what is invalid about it.
To repeat, your real problem is not with the specifics, but in your debugging skills. You should take this as an opportunity to learn more about debugging. Stack Overflow is not a substitute for debugging. Learn to debug better, and your life as a programmer will become very much easier.

Decimal rounding and printer selection

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.

Debugging code in Delphi XE

I am a long time Delphi dev and in the past I use a third party tool for logging and debugging while developing (called Smart Inspect) however now that I've upgraded to Delphi XE I want to try and use the IDE for debugging.
My question is, given a function like
procedure MyFunction;
var
str : string;
begin
str := 'Foo';
//Debug, show value of str?
str := AnotherFunction(str);
//Debug, show value of str?
end;
how can I debug and get the value of str, without doing stupid things like
showmessage(str);
if there is a video somewhere (or article) then I am more than happy to read.
Is there a way to watch/output the value of variables.
If you want to use the IDE Debugger only, then do the following:
put a breakpoint somewhere
right click on the breakpointr circle and choose "Breakpoint Properties ..."
press "Advanced" button to show more options
uncheck "Break" checkbox
then use "Log message" and "Eval expression" edit boxes to enter trace values
Such messages will be send to "Event Log" debugger pane. Right click on the pane and choose "Properties". There you can filter ("Messages") / hilight ("Colors") the trace messages as you whish.
Well, Delphi XE comes with CodeSite logging, but I get the feeling you're talking about the debugger itself.
If you place a breakpoint in a routine, it will break to the debugger when it hits it. From there, you've got a Local Variables pane and a Watches pane along the left side of the IDE. Local Variables will show the value of all locals, and Watches lets you set up expressions whose value it will keep track of.
You can also get something similar to a watch, but with more detailed information (especially for structured types such as objects) by using Inspect (Alt-F5). Also, the Evaluate/Modify (Ctrl-F7) will allow you to type in expressions and evaluate them. It's not quite as detailed as Inspect, but it gives you a lot more flexibility.
If you familiarize yourself with these tools, you'll find debugging much easier.
1) You can use OutputDebugString Function to output string to debug window
2) You can use CodeSite Express. I recommend video from CodeRage 5 as a starting point for using CodeSite
Other answers are all correct.
My personal favorite technique (same as the answer by da-soft) is to create a breakpoint, that logs a message to the event log, containing a value that I want logged, and does not actually "break" (that is, execution continues without you hitting the Run icon). Then every time that line of code is reached, I get my message, and my values in the log. Since I can go back and read the history, as well as see the current values, I find this more useful than merely using the debugger watch window.
But since Delphi XE includes CodeSite, you can go far beyond what expression evaluation in breakpoints does for you. Code Site however requires that you modify your code to add some logging. But it's much better than a message box.
You can also use OutputDebugString(PChar(s)) to output any string to the debugger. Since this can contain whatever you want, it's a very nice way to debug but not show stuff to the end user.
In many of my applications, I have a special trace buffer, which is circular (that is, it keeps only the last 500 or so lines). When ever I see a problem, not only do I get a stack traceback, I also save that in-memory trace log, so I have some history on what was going on just before my problem.
You can also check out the Log 4 Delphi project.
I prefer debugger hints. After breaking to the debugger move your mouse to the "str" anywhere in your code and you will see its current value. Also you can highlight some statement by a mouse and evaluate it. For example highlight "AnotherFunction(str)" and place your mouse over it.
Nothing wrong with any of the other answers but I just wanted to add these useful functions.
procedure DebugString ( const s : string ) ; overload ;
begin
{$IFDEF DEBUG}
OutputDebugString ( PChar ( s ) ) ;
{$ENDIF}
end ;
procedure DebugString ( const s : string ; args : array of const ) ; overload ;
begin
{$IFDEF DEBUG}
OutputDebugString ( PChar ( Format ( s , args ) ) ) ;
{$ENDIF}
end ;

Resources