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.
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 have a Delphi 6 app that uses an ActiveX DLL to interface to another popular app (aka the "host" app for lack of a better word). The host app provides the integration DLL. I do not have the source code for it or any control over it. To use the DLL I create a TypeLib using the IDE Import ActiveX Control facility.
The problem arises when the host app vendor creates a new version of the ActiveX DLL. I have to scramble and provide my users with a new version of my program as they upgrade to the host app vendor's latest or beta versions. Otherwise my app crashes of course when certain calls are made to the integration DLL due to variances between the old TypeLib and the new DLL. This also leads to the burden of maintaining multiple code bases of my app to maintain support for my users that still use the old versions of the host app. I'm trying to avoid a big messy code overhaul where I wrap everything in sight exposed by the integration DLL, in order to create a version of my code that can adapt at run-time to the current DLL version.
That leads to my question. The TypeLib(s) generated by Delphi is a big list of IDispatch methods and properties. Apparently the Delphi compiler converts these to IDispatch.Invoke() calls behind the scenes. Now I can detect the current version of the host app before I call CoCreate() to create the ActiveX object. So, is there any way I can at run-time switch between the two DLL TypeLib definitions? Right now I do it via compile-time conditionals that include the correct TypeLib based on the version of the host app I'm building for. I can do this because I retain each version of the TypeLib and give it a unique name as the vendor updates the DLL. But that doesn't help me to do it at run-time.
I can't fathom how to do this because everything from defined variables to method calls is based on the TypeLib as it is compiled. But I was wondering if there is something clever I could do at the IDispatch level to make this happen? Otherwise I'm stuck creating wrappers for each exposed object that calls the correct TypeLib method/property definition based on the current version of the host app. This is a big job and will also lead to some pretty convoluted code.
How have those of you faced with this same predicament solved or coped with it?
The practical solution would be to use late binding (getting a reference using CreateOleObject and having the references resolved at runtime) instead of using early binding via the typelibs. This would mean that as long as the vendor doesn't remove functionality, your code would continue to work regardless of what version of the control was installed.
For examples of doing this with MS Office applications, you can see several of the old (but still accurate and usable) posts at Deborah Pate's site (see note below). For instance, this one for Word uses late binding to either retrieve the currently running Word instance or create a new one:
var
Word: Variant;
Filename: OleVariant;
begin
try
Word := GetActiveOleObject('Word.Application');
except
Word := CreateOleObject('Word.Application');
end;
FileName := 'C:\WordDocs\MyFile.doc';
Word.Documents.Open(FileName, EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam);
Word.Visible := True;
Note that there is no included type library here, and no prior declaration of Word.Documents or the Documents.Open method. These are both resolved for you at runtime, and if not implemented will raise an exception.
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
Using dev express makes it really easy to extend the Quantum Grid Views as described in
http://devexpress.com/Support/Center/KB/p/A334.aspx?searchtext=viewinfo
you just have to declare and overwrite the methods you need:
TMyGridDBTableView = class(TcxGridDBTableView)
protected
function GetViewInfoClass: TcxCustomGridViewInfoClass; override;
end;
But in order to cosume the TMyGridDBTableView you either
have to install it as a component package with RegisterComponent()
or build the whole UI from code like this
View := Grid.CreateView(TMyGridDBTableView) as TMyGridDBTableView;
View.OptionsView.ColumnAutoWidth := True;
View.OptionsView.NewItemRow := True;
View.DataController.DataSource := DataSource1;
View.DataController.CreateAllItems;
Neither of the ways is good to me because:
I dropped installation of components in the IDE years ago due to unwillingness to rebuild, reinstall them after each small change and even though I write lots of components I initialize them with code
I still install the dev express components though and manipulate them through the UI. Having to switch to pure source code instantination of all views will result in literally thousands of lines of code.
Is there a way I keep my already form-designed TMyGridDBTableView but enhanced them at runtime with the TMyGridDBTableView overloaded methods?
Installing your own component in the IDE is tried and tested. Many thousands of developers around the globe do it. I do it too. It works fine. You actually know this yourself since you do exactly that with the devexpress components.
However, if you are dead set against registering your own components in the IDE, you can use an interposer as Sertac suggested. This works so long as you don't need to publish any new properties, which I believe is the case in the situation you describe.
It's possible that the problems you have with registering components in the IDE are actually faults in your code. That's not meant as criticism, coding for design time behaviour is quite challenging. Since you don't need to publish new properties, and since the component in question is a third party component, an interposer or similar seems to be quite a reasonable approach.
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.