Error loading file with full name containing spaces in directory with delphi - 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.

Related

Default TEncoding.UTF8 discarding invalid blocks of data in TStreamReader input

I'm using a TStreamReader to read data from a file that purports to be utf-8. I have no problem reading the file until it comes to a section containing what appears to me to be a UTF-8 "£" symbol with the preceding xC2 missing - the file only contains the xA3 part of the character. I've traced this through the run-time library until it calls
Result := UnicodeFromLocaleChars(FCodePage, FMBToWCharFlags,
PAnsiChar(Bytes), ByteCount, nil, 0);
which returns 0 indicating that it doesn't like the input. Unfortunately the TStreamReader simply ends up discarding this buffer of input and then continues with the rest of the file without raising an error. This is extremely misleading about what the problem but that is just a side issue.
The issue appears to be a "defect" in the UTF-8 TEncoding class in that it simply discards the results of a failed conversion whilst the TStreamReader assumes that this isn't the behaviour of TEncoding.
I can work around this by using
Reader := TStreamReader.Create(FileStream, TMBCSEncoding.Create(CP_UTF8, 0, 0));
instead of
Reader := TStreamReader.Create(FileStream, TEncoding.UTF8);
as this makes it ignore the corrupt UTF-8 and simply include something (I haven't checked what) in my output. However, I would like to combine allowing the data through with reporting it and there doesn't seem to be any obvious way of doing this as the behaviour is hidden deep within the library.
Does anyone know of any standard Delphi library tools for doing this or do I need to resort to a lot of custom code?

Cast from Variant String to Double ignores decimal point

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.

Why is COMMON_APPDATA returned as a null string on Windows XP

One of my users at a large university (with, I imagine, the aggressive security settings that university IT departments general have on their computers) is getting an empty string returned by Windows XP for CSIDL_COMMON_APPDATA or CSIDL_PERSONAL. (I'm not sure which of these is returning the empty string, because I haven't yet examined his computer to see how he's installed the software, but I'm pretty sure it's the COMMON_APPDATA...)
Has anyone encountered this or have suggestions on how to deal with this?
Here's the Delphi code I'm using to retrieve the value:
Function GetSpecialFolder( FolderID: Integer):String;
var
PIDL: PItemIDList;
Path: array[0..MAX_PATH] of Char;
begin
SHGetSpecialFolderLocation(Application.Handle, FolderID, PIDL);
SHGetPathFromIDList(PIDL, Path);
Result := Path;
end; { GetSpecialFolder }
ShowMessage(GetSpecialFolder(CSIDL_COMMON_APPDATA)); <--- This is an empty string
Edit:
Figuring out this API made me feel like I was chasing my tail - I went in circles trying to find the right call. This method and others similar to it are said to be deprecated by Microsoft (as well as by a earlier poster to this question (#TLama?) who subsequently deleted the post.) But, it seems like most of us, including me, regularly and safely ignore that status.
In my searches, I found a good answer here on SO from some time ago, including sample code for the non-deprecated way of doing this: what causes this error 'Unable to write to application file.ini'.
If you want to find out why an API call is failing you need to check the return values. That's what is missing in this code.
You need to treat each function on its own merits. Read the documentation on MSDN. In the case of SHGetSpecialFolderLocation, the return value is an HRESULT. For SHGetPathFromIDList you get back a BOOL. If that is FALSE then the call failed.
The likely culprit here is SHGetSpecialFolderLocation, the code that receives the CSIDL, but you must check for errors whenever you call Windows API functions.
Taking a look at the documentation for CSIDL we see this:
CSIDL_COMMON_APPDATA
Version 5.0. The file system directory that contains application data for all users. A typical path is C:\Documents and Settings\All
Users\Application Data. This folder is used for application data that
is not user specific. For example, an application can store a
spell-check dictionary, a database of clip art, or a log file in the
CSIDL_COMMON_APPDATA folder. This information will not roam and is
available to anyone using the computer.
If the machine has a shell version lower than 5.0, then this CSIDL value is not supported. That's the only documented failure mode for this CSIDL value. I don't think that applies to your situation, so you'll just have to see what the HRESULT status code has to say.

Unit source code does not match code execution path when breakpoint hit

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.

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