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.
Related
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).
I am using Delphi 7.
TOpenDialog.Execute hangs the program if launched from the standalone
EXE.
When the project is executed from Delphi 7 it does not hang.
It does not matter what I add, even a blank project with a button
that has OpenDialog1.Execute on it will cause the hang when clicked.
This problem is recent. Projects compiled before a while ago do not have the hang.
I used the same Windows installation (Windows 7 Ultimate 64bit) and
the same Delphi 7 for years now.
Projects compiled before a while ago do not have the hang.
Projects from Delphi 7 that are complied in Delphi 10 will also cause the hang.
It does not happen in CLX applications, whether in debug or standalone.
Here's an example of what seems to be causing the hang:
procedure TForm1.Button1Click(Sender: TObject);
begin
OpenDialog1.Execute;
end;
Update:
Cause might be "InitialDir" being the Desktop. If it's set to c:\ for example, it won't happen.
Another thing to look at is Project -> Options -> Compiler Messages (are there any unchecked?) If so, those are exceptions begin ignored by the IDE's built-in debugger. Check them, and run, then you might see what is causing the hang-up.
The only time I have been able to produce this is when I have a network share that is sluggish or not there... during the IDE debugger it timesout and keeps going, however, standalone it will eventually timeout, but, much longer.
(Most likely it is something in your Project -> Options screen). As I do strange things that most people never do :-) (sshfs for example will cause this).
I once had this problem in some of my application if i wanted to access network shares. By including the compiler switch
{$SetPEOptFlags IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE}
I could get rid of the problem. See also this blogpost. But be aware, that this switch has some restrictions.
As Microsoft mentions, "The /TSAWARE option is enabled by default for Windows 2000 and later, for Windows and console applications", meaning every .NET application has this switch turned on by default.
Just after running my Delphi application i check for the installed MS Outlook version because my app works differntly if in the machine there is a Outlook version greater than 2007.
I also wrote an Outlook Addin that sometimes crashes. I suspect
those crashes are related to the Outlook instances I create at startup.
Is there a way to create these ActiveX instances in a "softer way". As i create the OLE object now I see in Tray Bar an icon whose hint says "Outlook is being used by another application", may be if it were possible to remove this I would also see my problems disappear.
Somehow what I would like to achive is the same done here, where the wdDoNotSaveChanges parameter allows a "smoother" use of the OLE Object.
This is the code i use to check for Outlook version:
var
OutlookApp: OLEVariant;
Version : String;
begin
{ Create the OLE Object }
Try
OutlookApp := CreateOLEObject('Outlook.Application');
Version := OutlookApp.version;
OutlookVersion := StrToint(SubstrEx(1,Version,'.'));
OutlookApp := VarNull;
except
on E: Exception do
begin
OutlookVersion := -1;
end;
End;
Your approach is different from the one suggested by Microsoft here:
How to: Check the Version of Outlook .
Their version uses the Microsoft Installer functionality to detect the location of the Outlook executable, then extract the file version straight from there. This approach doesn't suffer from any of the problems you might encounter when instancing Outlook like you do. It is also a lot faster.
It does however suffer from one major disadvantage: it will only work if Outlook is deployed properly, the Microsoft way. On consumer machines this isn't so much of a problem, but in enterprise environments you might (/will) occasionally run into stripped down custom installs by overzealous IT departments that do not include the necessary MSI footprint.
Depending on your target audience, that might not be a real problem at all.
This is occurring on 2 machines that are both running Windows XP Pro SP3, yet it runs OK on my development machine within in or outside the Delphi IDE.
Running Windows XP Pro. Exe compiled under Delphi 2010.
When I run the exe I get the Windows Reporting error "Neopos.exe has encountered a problem and needs to close. We are sorry for the inconvenience"
I know it is happening somewhere in the form create of the main form.
Application.Initialize; //Runs this
Application.CreateForm(TfmMain, fmMain); //FAILS HERE
It does not get to: procedure TfmMain.FormCreate(Sender: TObject); in the Main Form and I don't know how to track down this error and debug it.
What happens between: Application.CreateForm(TfmUDF, fmUDF); AND procedure TfmMain.FormCreate(Sender: TObject) in my main form.
How can I trace this to find out what the hell is causing the Windows Error.
Of course the Windows Error report contains a long listing of information. Where can I look in that to find the cause or at least a clue on the cause of the error.
This error has now stopped all development work (and ruined my weekend) so I urgently need to fix this.
The most straightforward route to take would be to include a product like MadExcept or JCL Debugger into your application, to get a full call stack (including line number) of the point of failure. We've rolled our own years ago, and it has been a tremendous help in situations like this.
One alternative, but lots more cumbersome, would be to generate a MAP file from your project, use MAP2DBG to generate a .MAP file, and use the Windows Debbuging Tools to get about the same information. This approach is a lot more hardcore, and only advisable if you really want to learn a lot about the internals of windows debugging (and enjoy working with arcane tools).
Another alternative would be to attach to the failing application from your development environment using Remote Debugging. Only applicable if you have a fair amount of control over the failing machines.
#user576639, here are some debugging ideas:
Look into the System's Event Viewer
If you got the exe has encountered a problem and needs to close chances are you'll find something about it in the System's Event Viewer. That should be your first step.
Any special DLL's required?
Do you need MIDAS.DLL?
Are you using an database engine? Does it require some sort of client library?
I'm talking from experience here: My development machine obviously has all the libraries I might need. Most of my clients also have most of those libraries because they have my software installed. At times I put out small helping applications that don't go throw extensive testing and they fail to work on SOME machines but work fine on other machines. Why? I used TClientDataset and forgot to include MIDAS.DLL with the application; Or the application is trying to access a Firebird SQL server but the user doesn't have Firebird client library installed.
Printer driver issues
Boy I hate Delphi's printer handling. Also hate buggy printer drivers, haven't made up my mind about what's worst. If you have something on your main form that might be requesting information about the default Windows printer (example: an REPORT) give this a try: Install an sane/simple printer and set it as the default printer. If the user has Office 2007+ installed, set the "Microsoft XPS Document Writer" the default printer.
I have seen bad printer driver + delphi issues manifest themselves with the "exe needs to close" symptom.
Prepare an special build of your application
If you got this far without fixing your issue it's time to create an special build of your application that's capable of providing more information. First of all I'd try adding this to your DPR file; Don't know if this is still useful for Delphi 2010 but it did help me see some early exceptions with a Delphi 7 application:
function HandleUnhandledException:integer;stdcall;
begin
Result := 1; // 1 = EXCEPTION_EXECUTE_HANDLER
end;
// and then immediately after "begin" in your DPR file:
begin
SetUnhandledExceptionFilter(#HandleUnhandledException);
// ... the usual stuff goes here
end;
Add some ShowMessage-s to your Main Form's code, in your OnCreate handler (if you have one), in your Create constructor (again, if you have one). If you're adding an ShowMessage to your Create destructor, make sure it's after the "inherited" call. This will help pin-point how far the loading of the form goes before it fails.
If all else fails...
Create a new, blank form; Make it the new Main Form (so it's initialized before your former Main Form). Test it on the client's machine - does it show up? It most likely will, if it doesn't you've got some serious problems.
Start copying the components from the former main form to the new main form; Only the components need to be copied, not the code: Your error is probably caused by some component failing to initialize properly. Make sure no component has "Active=True"! Copy the components in small batches, test often. If you spot the component that causes your form not to load on the client's computer, tell us about it and we'll try to help.
If you manage to get all your components on the new form, write an OnCreate handler that sets Active := True for all the components that need that. Did that fix the issue?
If you got this far then all the components you used on your main form can load properly. The problem's related to YOUR CODE. Start copying all the code from your old main form to your new main form, in small bits, and test. You're bound to find the peace of code that causes your application to stop loading.
Use dependency walker to see if you're missing a required DLL.
You can use information from system reporting (your error and suggestion to send it ) with Error Report Grabber ( http://www.maxerist.net/main/soft-for-win/err-rep-grabber ). I developed this tool when I desperately needed to track a error that appeared very rarely so almost non-reproducible. It helped me to track the information from stack to find actual place in the code.
The tool works only on XP (MS removed this dialog in Win7 and probably Vista), but I see that your cases are XP so this can help.
UPDATE: if you're not familiar with assembler and everything, this can work like this.
You should compile you program and don't change anything. Save the report on a bad machine, copy the file to your developer machine and open to view the contents. Look at the stack of your main thread in the report and find numbers more than $00400000, they're usually the addresses inside the procedures that called some other procedure and wait for return. In your developer machine, start the program and stop at any line, open CPU Window and on the main list with assembler instruction right-click and choose go to address, enter this address. You will see other assembler lines, but wrapped with pascal constructions you can probably recognize as yours
Thanks a lot for the help.
In the end I reverted to a recent backup and traced it down to a particular form.
I did not actually find the error, which is a bit worrying, but in any case I am back up and running (phew!!)
I made the error to occur on my development machine also, when, and only when, I use my install program (Inno Setup) to compile a setup.exe and which installs the exe as well as installing postgreSQL. Seems really strange, as though there is a problem with the setup compiler. In any case I have not seen the error again. I guess it will remains a mystery, like women.
In Delphi withing debugging options select debug dcu's, this will allow you to debug into the Delphi source code for TForm and its descendants and you may be able to track down a more likely culprit.
Set a breakpoint on
Application.CreateForm(TfmMain, fmMain); //FAILS HERE
and then step into the code to see where the issue is.
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.