How should developers cope with so many GUI configuration combinations? - delphi

These days, any decent Windows desktop application must perform well and look good under the following conditions:
XP and Vista and Windows 7.
32 bit and 64 bit.
With and without Themes.
With and without Aero.
At 96 and 120 and perhaps custom DPIs.
One or more monitors (screens).
Each OS has its own preferred font.
Oh my! What is a lowly little Windows desktop application developer to do? :(
I'm hoping to get a thread started with suggestions on how to deal with this GUI dilemma.
First off, I'm on Delphi 7.
a) Does Delphi 2010 bring anything new to the table to help with this situation?
b) Should we pick an aftermarket component suite and rely on them to solve all these problems?
c) Should we go with an aftermarket skinning engine?
d) Perhaps a more HTML-type GUI is the way to go. Can we make a relatively complex GUI app with HTML that doesn't require using a browser? (prefer to keep it form based)
e) Should we just knuckle down and code through each one of these scenarios and quit bitching about it?
f) And finally, how in the world are we supposed to test all these conditions?

For the moment I would like to answer only one question:
f) Use virtual machines and (if possible) automated tests. I know it is a big job to set this up but you will never regret.

I, too, am a lowly Windows developer (D7) - much more interested in solving my vertical market application user's problems than coping with M$ foibles.
I cobbled up up a component to deal with all with these issues, plus some more.
As far as I know, all the pieces were in the public domain, and I have credited them where possible.
These are some of the properties:
type
TAppEnvironment = class(TComponent)
private
{ Private declarations }
// environment management
FEnvError : TEnvError; // environment error code
FEnvErrorMsg : string; // environment error message
FEnvLocalComputerName : string; // name of the client computer
FEnvCurrentUserName : string; // logged-on user
FEnvCurrentUserAdmin : Boolean; // is logged-on user Admin?
FEnvProduct : string; // windows edition
FEnvProductFlavour : string; // windows flavour (Home/Pro)
FEnvBuildNumber : string; // windows build number
FEnvServicePack : string; // windows service pack
FEnvThemeActive : Boolean; // Windows Theme active
// calc using product & theme
FEnvTitleHeight : integer; // window title height
FEnvMenuHeight : integer; // window menu height
FEnvStatusHeight : integer; // window status bar height
FEnvBorderHeight : integer; // window border height
FEnvMainHeight : integer; // main menu window height
FEnvMainWidth : integer; // main menu window width
FEnvHeightAdjust : integer; // window height adjust
FEnvWidthAdjust : integer; // window width adjust
FEnvLocalPath : string; // App exe home folder
FEnvAppFolderName : string; // application name less extension
FEnvAppFileVersionStr : string; // like 6.0.0.4567
FEnvAppFileVersion : TFileVersion; // HiVersion, LoVersion, etc.
And some utilities:
function EnvironmentReady : TEnvError;
function GetLocalComputerName : string; // network needs this
function GetAppFolderName : string;
function BuildNumber : Integer;
procedure GetFileInfo(const AFileName: string; var RFileInfo: TFileInfo);
function GetLocalPath : string;
procedure getEnvWindowAdjust(bar : TStatusBar);
function setAppFileVersionStr : string;
function GetFileTime(const FileName: string): LongInt;
function initEnvironment : Boolean;
function exitEnvironment : Boolean;
function AlreadyRunning : Boolean;
function specialBuild : Boolean;
I have a function to size each form correctly, using FEnvTitleHeight, etc.
All the dumb user paths are also generated, depending on Windows version.
I have no clue as to how to manage the process, but if people want, I will toss the whole thing into the pot - so that the masters can work it over.

Excellent question.
I've been developing my application for over 10 years, starting with Delphi 2, 3 and then 4 and then staying there and waiting many years to upgrade to Delphi 2009 because Unicode was a must. I'll upgrade again when the 64 bit version comes out.
So I've run the gamut of Operating Systems: Windows 98, Windows 2000, XP, Vista and now 7. Each breaks your UI somewhat, but Delphi has been pretty good about it. At some point in time, you have to decide you cannot support the older OS's anymore, and moving to Unicode finally cut out Windows 98 from my supported list.
Generally, I've found that core Delphi gives you the best UI support. Some third party packages may appear to provide more, but their inconsistencies are worse problems than their benefits might be. Minimize other packages where you can.
The one UI goal I've had is to go for the Windows Vista Logo Program, and more recently the Windows 7 program, and Microsoft does provide a lot of information on what the standards should be that relate the your conditions 1 to 7 in your question. But getting a Delphi program to use a manifest and go through Microsoft's hoops was in the end, not worth the hassle and cost to me, especially since my non-compliant program worked just fine on Vista and 7.
Keeping my program running and keeping the UI looking the same on Windows XP, Vista and 7 when I am developing on a 64-bit Vista machine means I use Microsoft Virtual Machine when I need to. I have been told my program also works on Wine, so that's another test machine.
Now answering your questions:
a) Does Delphi 2010 bring anything new to the table to help with this situation?
Yes. Every version adds new VCL components that have been added to the new OS's. e.g. Windows 7's new UI's have been added.
b) Should we pick an aftermarket component suite and rely on them to solve all these problems?, and c) Should we go with an aftermarket skinning engine?
As I said above, I think its better to do it in Delphi itself than in a 3rd party package.
d) Perhaps a more html type gui is the way to go. Can we make a relatively complex gui app with html that doesn't require using a browser? (prefer to keep it form based)
My application is like a Word Processor with Rich Text. I've looked at HTML-based editor suites and there are a few, but I don't think its the way to go for a desktop application. If you want a web-based application, you would be better off with .NET and Prism.
e) Should we just knuckle down and code through each one of these scenarios and quit bitching about it?
Upgrade to Delphi 2010 first. You'll find Delphi itself will handle most of those situations for you.
f) And finally, how in the world are we supposed to test all these conditions?
Doing it yourself is a big task, even with virtual machines. What you've got to do is have an open Beta and get as many different users in different environments to test your program for you. Then you'll handle all the environments that are most important to your users.
Now, if you think getting user interface compatibility in different Windows environments is tough, just wait until Embarcadero comes up with their version of Delphi that will compile for the Mac. Your current UI worries will seem trivial by comparison to what it will then become.

For te scaling of your forms on multiple resolutions / DPI sizes: we use DevExpress LayoutControl for that. It makes sure that the controls on your form always align to use the available space, no matter what. And it does a lot more. Have a look at their site.

Delphi 2010 brings native Unicode support.
Delphi 2010 brings Windows Vista/Seven controls.
There is no 64 bit Delphi compiler for the moment beeing.
You should'nt have any problem to handle dpi and monitor count questions with Delphi 7

Delphi makes that fairly easy, but in the end you'll have run your software on all Windows versions and visually check that everything looks fine. Automated test are great to test the functionality, but the cosmetics can be checked visually only.

Delphi 2009 brought in support for theming (it is an application level setting). When you run a Delphi 2009 or above application in Vista / Windows 7, it changes the message boxes to TaskDialogs, so you get some UI improvements for free.
We had a bit of a struggle porting to Delphi 2009, and Unicode, but that was a 'technical debt' that we had to pay at some point.
Hope this helps.

Related

Can't detect network drive under Debugger with Delphi 2007 on Windows 10

It is probably me being stupid, but I am having problems detecting if a network drive is up, but only when running the program in the IDE – when running the program outside of the IDE, the network drive is correctly detected. The IDE works fine on Windows 7.
While the program in question is different, I can reproduce the issue by creating a new project and adding the following into the Forms OnActivate event:
var
bRet: Boolean;
LTemp2: string;
LFreeSpace: Int64;
LTotalSpace: Int64;
begin
LTemp2 := 'T:\';
bRet := GetDiskFreeSpaceEx(PAnsiChar(LTemp2), #LFreeSpace, #LTotalSpace, nil);
ShowMessage('GetDiskFreeSpaceEx: Drive T: is up? '+BoolToStr(bRet, True));
end;
Assuming I have a networked drive T:, if I run the program in the IDE then the above always returns False, but if I run the built program from a desktop shortcut then it returns True. I get the same behaviour if I run it via a button click after the program starts. Doing DiskSize() and FindFirst() on the root directory give the same results.
It is a clean Windows 10 install, not an upgrade, with a clean install of CodeGear 2007 with all the patches applied. I have tried “Run as administrator” and all the Compatibility Modes back to Windows 7.
Am I doing something stupid?
After R. Hoek's great insight, and while it does not fully sort out the issue, it is at least enough for a workaround. By the way, at least in my case, it does not matter if I launch Delphi 2007 with "Run as administrator" or not.
I added a TOpenDialog hooked up to a button: once I run this and select a file on T: then GetLogicalDrives() works fine.
So what I now suspect is that I installed Delphi 2007 as an administrator - not sure how, but I think I will re-install everything.
Even if that does not resolve it, and since it only occurs in the IDE and not in production usage, I can add code to pop up a TOpenDialog if GetLogicalDrives() returns 4.
Weird. Thank you all very much.
By the way, one aspect of this discussion may not have been clear: Delphi 2007 was running under the user account (and it could see the network drive without problems) - it is only the program which is being debugged that cannot see the networked drive. That is why using the TOpenDialog resolved the issue (until Delphi or the computer was restarted).

Opening URL within ios application

I need to open a webpage with safari in my iOS application.
With XE2 there was iphoneall unit, which exposed UIApplication. XE4 doesn't use FPC anymore, so I can't use that.
Embarcadero documentation
says I can use SDKs only with C++ or using delphi interfaces (and still, macapi is for OSX only, not iOS). So, it seems that there is no interface for UIKit framework?!
Another solution I tried was:
_system('open http://www.google.com');
But that had no affect at all!
Is there any other ways to open urls or am I out of luck to accomplish it?
I know there is TWebBrowser component for ios, but I wouldn't want to take that road just to display a webpage.
By chance, someone at Embarcadero posted a code snippet to do exactly this two days ago.
If you are using XE4, look in the Samples, and you can find one (sorry, not sure of the name) where the final code is:
OpenURL('http://www.embarcadero.com');
This uses the XE4 FireMonkey framework and a class helper written by David Clegg, available in the sample.
If you are using an older version of FireMonkey, you can use the rather more cumbersome code:
function SharedApplication: UIApplication;
begin
Result := TUIApplication.Wrap(TUIApplication.OCClass.sharedApplication);
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
SharedApplication.openURL(TNSURL.Wrap(TNSURL.OCClass.URLWithString(NSSTR(PChar(String('http://www.embarcadero.com'))))));
end;
(Attribution: Code snippets all copied from the linked blog post.)
There is also a very old forum post from the early days of FireMonkey showing how to tackle these problems in general (basically, string <-> NSString <-> NSURL), and while it's a bit out of date - as you can see by the above code, FireMonkey has matured greatly - it may give some insight into the underlying reason for the code.

Does FireMonkey have anything similar to GetSystemMetrics?

I'm rewriting an old application using Delphi 2010 and I'd like to put placeholders in my code for when I port it over to XE2. Just curious if FireMonkey has an equivilent to GetSystemMetrics. I'm specifically interested in:
GetSystemMetrics(SM_CXSCREEN)
GetSystemMetrics(SM_CYSCREEN)
If you just need the main monitor size, and not the desktop size (n monitors sum), you can use this:
uses ..., FMX.Platform;
var
p: TPointF;
begin
p := Platform.GetScreenSize;
ShowMessage(Format('X: %f' + LineFeed + 'Y: %f', [p.X, p.Y]));
In fact SM_CXSCREEN and SM_CYSCREEN should probably not be used at all. That just tells you the dimensions of the primary monitor. In these days of multiple monitors, that's not terribly useful. In VCL code you should be using Screen.Monitors[] and related properties to obtain information about your monitors.
As for FMX, there is no multi-monitor support and no platform independent way to find out screen size metrics. In your shoes I would simply use Screen.Monitors[] and deal with the FMX port when you get to it. You are going to have to re-write all your GUI code anyway when you port to FMX and this particular issue is the least of your worries.
Finally, when you say
I'd like to put placeholders in my code for when I port it over to XE2
I trust you are aware that porting to FMX is not compulsory. The VCL is still supported and available in XE2 and I can't see it being removed for a long time to come. You can port to XE2 without having anything to do with FMX.

WMDropFiles problem on win7

I've implemented the following procedure and it works properly when I compile it on my computer running windows vista, computer #1. If I share the created .exe file to another computer running win7, computer #2, it runs fine aswell. The problem is when I let the computer #2 compile it, it wont simply recognize that anything's dropped on the application. There's no response at all in this code. It's built and compiled without errors or warnings.
I've been searching the net for a while without finding anything that explains why this happens or someone else with this problem.
Both computers use Delphi 2010 with the same components installed.
Is this the way to go to allow the user to drop files onto the application? If not, how should it be done nowdays?
Any ideas why it works when it's compiled on computer #1 but not computer #2? (The program works properly on both computers when compiled on computer #1)
Any help or comment is highly appreciated.
procedure TfMainForm.WMDROPFILES(var msg: TWMDropFiles);
const
MaxLength = 255;
var
nFiles : integer;
i : integer;
fileName : array [0..MaxLength] of char;
fileNames : TStringArray;
begin
// Get number of files dropped
nFiles := DragQueryFile(msg.Drop, $FFFFFFFF, fileName, MaxLength);
// Get filenames
SetLength(fileNames, nFiles);
for i := 0 to nFiles - 1 do begin
DragQueryFile(msg.Drop, i, fileName, MaxLength);
fileNames[i] := fileName;
end;
// Add files
projectHandler.addFiles(fileNames);
//release memory
DragFinish(msg.Drop);
end;
I'm going to take a wild guess that if you are running from within the IDE on computer #2. I bet that if you compile on computer #2 but start the executable from explorer rather than from the IDE, it works. The final piece of the jigsaw is that I bet you are running your IDE on computer #2 as administrator.
On Vista and Windows 7 you can't send messages to a process with a higher integrity level. If your process is being run as administrator then it will have a higher integrity level than explorer and so won't accept the dropped files.
If my guess is correct I recommend that you stop running Delphi as administrator, it doesn't need this.
As for whether or not WM_DROPFILES is a reasonable approach, I see no problems with using it.
At http://www.web-developer.de/content/download/7387/137496/file/Listings.zip you can find an example written using Delphi XE (compiles with D2010 as well). The subfolder "2_WmDropFiles" contains a project "WmDropFiles.dpr" that shows how to an app that runs elevated can receive files from an app which does not run elevated. The comments etc. are in German, so please use Google translate when in doubt.
Hope this helps,
Olaf

Printing on citrix server

We are using Delphi 7 to develop database apps with advantage as a backend. Our system is usually installed on the windows server with the pcs acting as terminals. All the settings and database are on the server.
we are having problems running our software on Citrix servers. In particular printing seems to be an issue. Both in selecting the right printer and in the formatting of the report.
We use Rbuilder version 10 to produce our reports and they are sent to a zebra label printer so not a standard windows printer driver. The reports are also of a non-standard size.
things we are seeing are stretching and shifting of the report on the page.
Has anybody seen similar behavior or has any idea of what might be causing this.
we don't have a test Citrix system so it is hard to test. We can't replicate it in a normal windows environment.
On Citrix (and Microsoft Terminal Server), printers often gets "attached" after the application starts. This causes that the printer that might be nedded isent in the Reportbuilder printer list.
We have solved the problem by forcing ReportBuilder to refresh the list, when printers change using the following code (Attached to Application.OnSettingChange)
procedure TMainForm.ApplicationEventsSettingChange(Sender: TObject;
Flag: Integer; const Section: string; var Result: Integer);
begin
if uppercase(Section) = 'DEVICES' then
begin
ppPrintr.ppPrinters.Refresh;
end;
end;
Hope it solves your problem.
Take a look at this link, you can get an evaluation version but you can also download virtual machines with complete citrix installation in it. Btw the product is called XenApp nowadays.
In my experience, Citrix printing is a nightmare.
You'll want to make sure the printer you want to use is installed as a local printer on the Citrix server, then disallow use of client printers for the application. That should help getting the printer right.
Basically, you'll want to make sure you can run the application properly from the server console, then try to use it as a Citrix app.
Good luck.

Resources