TSaveDialog Options ofAllowMultiSelect doesn't work properly on a W10 system - delphi

I have an old application, untouched for a long time, built with C++ Builder 2009, that still works fine.
That is to say ..
Today I noticed some of the TSaveDialog->Options don't work as intended on my Windows 10 system. To make sure I'm not dreaming I tested the same application on an older Windows version (I tried XP) and there it worked perfectly fine as intended.
The TSaveDialog instance is setup at design time with Options: [ofHideReadOnly,ofAllowMultiSelect,ofEnableSizing]
I noticed today (on Windows 10) that ofAllowMultiSelect doesn't work anymore ?
Instead ofOverwritePrompt is (incorrectly) used !
In other words I cannot select two ore more files anymore and when I select a file that already exists I first get a 'Confirm Save As' dialog.
When I compile again on my Windows 10 system, using C++ Builder 2009, in debug mode and inspect Options, the debugger seems to (still) properly see ofHideReadOnly, ofAllowMultiSelect, ofEnableSizing, yet the problem persists. So it's not as if the values changed somehow ?
When I try at runtime:
SaveDialog->Options.Clear() ;
SaveDialog->Options << ofHideReadOnly << ofEnableSizing << ofAllowMultiSelect ;
the problem also persists !
When I remove ofAllowMultiSelect (at run time or at design time) 'Confirm Save As' is not shown anymore on an existing file (but I obviously also still can't select multiple files).
I'm flabbergasted by this to be honest ? Not sure what to do next ?
I have no option to test a more recent c++ version but I'm also having difficulties comprehending how the compiler could be responsible here.
Any guidance appreciated.
Delphi tag added because of VCL overlap between c++ Builder and Delphi

On Windows Vista and later, IF AND ONLY IF all of these conditions are met:
the global Dialogs::UseLatestCommonDialogs variable is true
and the TSaveDialog::Template property is NULL
and the TSaveDialog::OnIncludeItem, TSaveDialog::OnClose, and TSaveDialog::OnShow events have no handlers assigned
Then TSaveDialog will internally use the Win32 IFileSaveDialog interface, where the ofAllowMultiSelect option will be mapped to that dialog's FOS_ALLOWMULTISELECT option, which is NOT SUPPORTED by IFileSaveDialog, only by IFileOpenDialog, per the documentation:
FOS_ALLOWMULTISELECT
Enables the user to select multiple items in the open dialog. Note that when this flag is set, the IFileOpenDialog interface must be used to retrieve those items.
If the above 3 conditions are not satisfied, then TSaveDialog will internally use the Win32 GetSaveFileName() function instead, where the ofAllowMultiSelect option will be mapped to that dialog's OFN_ALLOWMULTISELECT option, which IS SUPPORTED by GetSaveFileName() 1.
That is why you are seeing behavioral differences when running your app on Windows XP vs Windows 10.
So, if you want the old TSaveDialog behavior on newer Windows versions, you need to make sure at least 1 of those 3 conditions is not satisfied. For instance, by setting UseLatestCommonDialogs=false before calling SaveDialog->Execute(), or by assigning an (empty) event handler to one of the OnIncludeItem/OnClose/OnShow events.
Or, you could simply call GetSaveFileName() directly, instead of using TSaveDialog at all.
1: However, just note that on Vista+, GetSaveFileName() is just a wrapper for IFileSaveDialog, and is provided only for backwards compatibility. So, you still might not get the exact behavior you want even if you did use GetSaveFileName() on Windows 10.
On a side note: this code does not work the way you think it does:
SaveDialog->Options.Clear();
SaveDialog->Options << ofHideReadOnly << ofEnableSizing << ofAllowMultiSelect;
The Options property is not actually updated! In both statements, the Options property is read from, returning a temporary TOpenOptions, which you are then modifying, but not assigning back to the Options property. IOW, the code is effectively doing the following:
TOpenOptions temp1 = SaveDialog->Options;
temp1.Clear();
TOpenOptions temp2 = SaveDialog->Options;
temp2 << ofHideReadOnly << ofEnableSizing << ofAllowMultiSelect;
So, to update the Options property correctly, use this instead:
SaveDialog->Options = TOpenOptions() << ofHideReadOnly << ofEnableSizing << ofAllowMultiSelect;

Related

Delphi TurboPower Async Professional Data Packet Access Violation…

I use windows 7, 64-bit SP1, and TP Async V4.07 and have the following problem:
I have two Com ports, Com11, and Com18.
I add the following components to my form:
I open a Com port on ApdComPort2 (Com18) and it works without a problem. The ApdDataPacket2 detects the packet terminator and the result displayed is what is expected. Both Com11 and Com18 work fine.
Now if I open another Com port (Com11) with ApdComPort1 I get an Access violation:
The code that generates the error is this in the AdPacket module:
procedure TApdDataPacketManager.EnablePackets;
var
i : integer;
begin
for i := 0 to pred(PacketList.Count) do
with TApdDataPacket(PacketList[i]) do
if Enabled then
Enable;
end;
It is the PacketList.Count that seems to be the problem when it iterates through the list but I can’t catch why:
Note that ApdComPort2 works without problem with both Com11 and Com18.
If I remove the Apd2 components then Apd1 works as expected. The problems surface when I try to use two (or more) Apd components at the same time.
Does anybody have a suggestion or can recommend a component that works with more than one serial port simultaneously?
Some notes about the Turbo Power Async Professional components:
When using the Async components it is very important on how you add the components to the form. If you don’t does it in the right order and in the correct way it will not work if you use more than one serial port. You will actually get an access violation. For example, if you add the components below you have to do it in this way:
Add one ApdComPort to the form, it will become ApdComPort1
Now copy and paste this component to the form, it will become ApdComPort2
Add one ApdDataPacket component to the form, it will become ApdDataPacket1
Now copy and paste this component to the form, it will become ApdDataPacket2
Add one ApdSLController component to the form, it will become ApdSLController1
Now copy and paste this component to the form, it will become ApdSLController2
When doing it, as described above, it works to use two serial ports with ApdDatapacket. Now I don’t get any getting Access violations. I have tested it up to 4 ports and it works as well.

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.

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.

Windows Shell and Citrix

I have this line of code in my Delphi app:
sh := CoShellWindows.Create;
When run through a Citrix session, this raises an exception "Not enough storage is available to complete this operation."
Can someone confirm my suspicion that I can't access this through Citrix? I'm running in Seamless mode if that makes any difference. Maybe there's something I need to change on the published icon to make it work?
I am guessing that there is no "Shell" in Citrix to create.
Thanks
EDIT
The CoShellWindows is simply a class which creates an object which implements the IShellWindows interface. This interface is then used to iterate through it's items looking for an instance of Internet Explorer (or more specifically, an item which implements the IWebBrowser2 interface).
There are a few other use case scenarios using the CoShellWindows, but all ultimately are used to interact with the IWebBrowser2 interface (Internet Explorer 8). My requirement is to obtain this IWebBrowser2 object.
The call, behind the scenes is calling the Windows API CoCreateInstance with the following parameters:
rclsid = {9BA05972-F6A8-11CF-A442-00A0C90A8F39} (CLSID of
IShellWindows)
pUnkOuter = null (nil)
dwClsContext = CLSCTX_ALL (I've tried various combinations of these
flags)
riid = {85CB6900-4D95-11CF-960C-0080C7F4EE85} (IID of IShellWindows)
ppv = a variable declared as type IShellWindows
eg:CoCreateInstance(CLASS_ShellWindows, nil, CLSCTX_ALL, IID_IShellWindows, sh)
Your exception "Not enough storage is available to complete this operation." should really read "Shell does not exist so no instance can be created"
Basically you are correct in your assumption that there is no shell to create in Citrix.
What are you using the shell for? as if you provide more information we may well be able to offer a full work around.

How to read the current machine NTFS settings?

Before inserting filestream data I'd like to check the following NTFS settings:
1) 8.3 naming status (this is disabled by using fsutil behavior set disable8dot3 1)
2) last access status (this is disabled by using fsutil behavior set disablelastaccess 1)
3) cluster size (this is set with format F: /FS:NTFS /V:MyFILESTREAMContainer /A:64K)
The filestream recomendation is to disable (1) and (2) and to set (3) at 64kb.
But before setting this I'd like to know the existing settings. How do I check this? Answer can be in Delphi but not necessarly.
The GetDiskFreeSpace Windows API call returns the sector_per_cluster and bytes_per_sector values. I think this function should be in Windows unit.
You can read the registry for points 1 and 2 (using xp_regread in SQL)
Number 3 is not essential but helps and has been SQL Server best practice for a decade or more. You'd have to use sp_OA% or a CLR function to read this in SQL.

Resources