Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Setting File Associations - need help

56 views
Skip to first unread message

Rob Bennigan

unread,
Mar 13, 1999, 3:00:00 AM3/13/99
to
Two questions:

1) How do I programmatically set file associations so when a person double
clicks it will open my application?

2) Once a file association is set, when double clicking, if my app is already
open, a second instance is opened. How do I prevent a second instance from
opening (I can do this with the "lonely" component) yet STILL have the existing
app that is opened get a message to process the file that is double clicked?

Peter Below (TeamB)

unread,
Mar 14, 1999, 3:00:00 AM3/14/99
to

These two questions are connected, you need to create a number of keys in the
registry and make your application a DDE server. Since this question comes up
regularly i have created a sample application to show the steps involved. The app
registers itself when started but you can move that part to an installation
program, of course.

Apologies to any lurkers for the size of this message, but i think in this case
well commented code is more important than a small message size.

{+------------------------------------------------------------
| Unit FileAssociation_Demo1
|
| Version: 1.0 Created: 14.03.99
| Last Modified: 14.03.99
| Environment : Delphi 4.02, tested on Win95B
| Author : P. Below
| Project: Sample applications
| Description:
| This is a simple demo application that shows how to register
| an application as server for a file extension (.TED in this
| case) and how to use DDE to open files from Explorer in
| an existing instance of the program.
| A file association requires, at minimum, the following keys
| in the registry under HKEY_CLASSES_ROOT (HKCR):
|
| HKCR\<extension> = <filetype>
| HKCR\<filetype> = <description>
| HKCR\<filetype>\shell\open\command = <application> "%1"
|
| "open" is one of the standard verbs, others that may be used
| are "edit", "print", and "printto". If all verbs are implemented
| by the same application command line switches may be used
| to differentiate the action to take in the command key string.
| See the entry for HKCR\rtffile in regedit.exe for an example.
|
| If only the three keys above are present Explorer will open
| a new instance of the application for each file. To get it to
| use an existing instance one needs to make the application into
| a DDE server and add some more keys to the registry:
|
| HKCR\<filetype>\shell\open\ddeexec = <macrostring>
| HKCR\<filetype>\shell\open\ddeexec\topic = <topicname>
| HKCR\<filetype>\shell\open\ddeexec\application = <DDE Servername>
|
| If a TDDEServerConv object is used to implement the DDE server
| then <topicname> is the name of the TDDEServerConv component
| and <DDE Servername> is the applications filename, without
| path and extension.
|
| To test this application copy a number of textfiles (e.g. PAS
| files) to extension TED and open them in Explorer. Note that
| you have to run the application once manually to get it to
| register itself.
+------------------------------------------------------------}
Unit Fileassociation_demo1;

Interface

Uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, DdeMan, ComCtrls;

Type
TForm1 = Class(TForm)
TEDDdeServer: TDdeServerConv;
PageControl1: TPageControl;
Procedure FormCreate(Sender: TObject);
Procedure TEDDdeServerExecuteMacro(Sender: TObject; Msg: TStrings);
Private
Procedure RegisterAssociation;
Procedure AddFileeditor(Const filename: String);
{ Private declarations }
Public
{ Public declarations }
End;

Var
Form1: TForm1;

Implementation

Uses Registry, ShlObj;
{$R *.DFM}

Type
ERegistryError = Class( Exception );
ResourceString
eCannotCreateKey =
'Cannot create key %s, the user account may not have the required '+
'rights to create registry keys under HKEY_CLASSES_ROOT.';

{+------------------------------------------------------------
| Procedure CreateKey
|
| Description:
| This is a helper routine which uses the passed reg object
| to create a registry key.
| Error Conditions:
| If the key cannot be created a ERegistryError exception is
| raised.
| Created: 14.03.99 by P. Below
+------------------------------------------------------------}
Procedure CreateKey( reg: TRegistry; Const keyname: String );
Begin
If not reg.OpenKey( keyname, True ) Then
raise ERegistryError.CreateFmt( eCannotCreateKey, [keyname] );
End; { CreateKey }

{+------------------------------------------------------------
| Procedure RegisterFiletype
|
| Parameters :
| extension : file extension, including the dot, to register
| filetype : string to use as key for the file extension
| description: string to show in Explorer for files with this
| extension. If description is empty the file
| type will not show up in Explorers list of
| registered associations!
| verb : action to register, 'open', 'edit', 'print' etc.
| The action will turn up as entry in the files
| context menu in Explorer.
| serverapp : full pathname of the executable to associate with
| the file extension, including any command line
| switches. Include the "%1" placeholder as well.
| Actions like printto may require more than one
| placeholder.
| Description:
| Creates the three basic registry keys for a file extension.
| HKCR\<extension> = <filetype>
| HKCR\<filetype> = <description>
| HKCR\<filetype>\shell\<verb>\command = <serverapp>
| If the keys already exist they are overwritten!
| Error Conditions:
| A ERegistryError exception will result if a key cannot be
| created. Failure to create a key is usually due to insufficient
| user rights and only a problem on NT.
| Created: 14.03.99 by P. Below
+------------------------------------------------------------}
Procedure RegisterFiletype( Const extension, filetype, description,
verb, serverapp: String );
Var
reg: TRegistry;
keystring: String;
Begin
reg:= TRegistry.Create;
Try
reg.Rootkey := HKEY_CLASSES_ROOT;
CreateKey( reg, extension );
reg.WriteString( '', filetype );
reg.CloseKey;
CreateKey( reg, filetype );
reg.WriteString('', description );
reg.closekey;
keystring := Format('%s\shell\%s\command', [filetype, verb] );
CreateKey( reg, keystring );
reg.WriteString( '', serverapp );
reg.CloseKey;
Finally
reg.free;
End;
End; { RegisterFiletype }

{+------------------------------------------------------------
| Procedure RegisterDDEServer
|
| Parameters :
| filetype : file type key name to register the server for
| verb : action to register, 'open', 'edit', 'print' etc.
| topic : DDE topic name to use. This is usually the name
| of a TDDEServerConv component.
| servername: DDE server name to use. This is usually the
| filename of the executable, without extension
| and path.
| macro : DDE macro to execute for the action, needs to
| include a "%1" placeholder for a filename.
| Description:
| Creates the registry keys required to open files of this type
| via DDE from Explorer or ShellExecute. RegisterFileType needs
| to be called first to associate the filetype with an extension.
| The registry keys added are
| HKCR\<filetype>\shell\<verb>\ddeexec = <macro>
| HKCR\<filetype>\shell\<verb>\ddeexec\topic = <topic>
| HKCR\<filetype>\shell\<verb>\ddeexec\application = <servername>
| If the keys already exist they are overwritten!
| Error Conditions:
| A ERegistryError exception will result if a key cannot be
| created. Failure to create a key is usually due to insufficient
| user rights and only a problem on NT.
| Created: 14.03.99 by P. Below
+------------------------------------------------------------}
Procedure RegisterDDEServer( Const filetype, verb, topic, servername, macro:
String );
Var
reg: TRegistry;
keystring: String;
Begin
reg:= TRegistry.Create;
Try
reg.Rootkey := HKEY_CLASSES_ROOT;
keystring := Format( '%s\shell\%s\ddeexec',[filetype, verb] );
CreateKey( reg, keystring );
reg.WriteString( '', macro );
reg.CloseKey;
CreateKey( reg, keystring + '\Application' );
reg.WriteString( '', servername );
reg.CloseKey;
CreateKey( reg, keystring + '\topic' );
reg.WriteString( '', topic );
reg.CloseKey;
Finally
reg.free;
End;
End; { RegisterDDEServer }

{+------------------------------------------------------------
| Procedure TForm1.RegisterAssociation
|
| Call method: static
| Visibility : private
| Description:
| Register this application as server for the .TED file
| extension.
| Error Conditions:
| A ERegistryError exception will result if a key cannot be
| created.
| Created: 14.03.99 by P. Below
+------------------------------------------------------------}
Procedure TForm1.RegisterAssociation;
Begin
RegisterFiletype(
'.TED',
'TEDFile',
'TED File',
'open',
Application.Exename+' "%1"' );
RegisterDDEServer(
'TEDFile',
'open',
TEDDdeServer.Name,
Uppercase( ChangeFileExt(
ExtractFilename( Application.Exename ),
EmptyStr )),
'[Open("%1")]' );
ShChangeNotify( SHCNE_ASSOCCHANGED, 0, Nil, Nil );
End; { TForm1.RegisterAssociation }

{+------------------------------------------------------------
| Procedure TForm1.FormCreate
|
| Event : OnCreate
| Used by : the form
| Call method: static
| Visibility : published
| Description:
| On form creation we register the application as server for
| the .TED extension and create editors for any file that
| may have been passed on the commandline.
| Note that the commandline processing code needs to be
| changed if command line switches are used to select
| between different actions!
| Error Conditions:
| A ERegistryError exception may be raised in the registration
| process.
| Created: 14.03.99 by P. Below
+------------------------------------------------------------}
Procedure TForm1.FormCreate(Sender: TObject);
Var
i: integer;
Begin
RegisterAssociation;
For i:= 1 To ParamCount Do
AddFileeditor( ParamStr( i ));
End; { TForm1.FormCreate }

{+------------------------------------------------------------
| Procedure TForm1.TEDDdeServerExecuteMacro
|
| Event : OnExecuteMacro
| Used by : TEDDdeServer
| Call method: static
| Visibility : published
| Description:
| This method is called when the DDE server receives a macro
| request. In this case the request will always be a single
| line but the code is able to deal with several macros rolled
| into one request, as long as the macros are separated by
| line breaks.
| Error Conditions: none
| Created: 14.03.99 by P. Below
+------------------------------------------------------------}
Procedure TForm1.TEDDdeServerExecuteMacro(Sender: TObject; Msg: TStrings);
Var
filename: String;
i, n: Integer;
Begin
If Msg.Count > 0 Then Begin
For i := 0 To Msg.Count-1 Do Begin
filename := Msg[i];
If Pos('[Open(', filename) = 1 Then Begin
n:= Pos('"', filename );
If n > 0 Then Begin
Delete( filename, 1, n );
n:= Pos('"', filename );
If n > 0 Then
Delete( filename, n, maxint );
AddFileeditor( filename );
End; { if }
End; { If }
End; { For }
End; { if }
End; { TForm1.TEDDdeServerExecuteMacro }

{+------------------------------------------------------------
| Procedure TForm1.AddFileeditor
|
| Parameters :
| filename: full pathname of a file to view
| Call method: static
| Visibility : private
| Description:
| Creates a new tabsheet in the pagecontrol and a TMemo on
| this tabsheet. The file is loaded into the memo and the
| tabsheet is made active.
| Error Conditions:
| Exceptions may result if the file is not found or is not
| a textfile.
| Created: 14.03.99 by P. Below
+------------------------------------------------------------}
Procedure TForm1.AddFileeditor( Const filename: String );
Var
tab: TTabSheet;
memo: TMemo;
Begin
tab := TTabsheet.Create(self);
tab.Pagecontrol := pagecontrol1;
tab.caption := Extractfilename( filename );
memo := TMemo.Create( tab );
memo.Align := alClient;
memo.Parent := tab;
memo.lines.LoadFromFile( filename );
Pagecontrol1.ActivePage := tab;
End; { TForm1.AddFileeditor }

End.

{
object Form1: TForm1
Left = 192
Top = 128
AutoScroll = False
Caption = 'Form1'
ClientHeight = 373
ClientWidth = 632
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 120
TextHeight = 16
object PageControl1: TPageControl
Left = 0
Top = 0
Width = 632
Height = 373
Align = alClient
TabOrder = 0
end
object TEDDdeServer: TDdeServerConv
OnExecuteMacro = TEDDdeServerExecuteMacro
Left = 32
Top = 24
end
end

}

Peter Below (TeamB) 10011...@compuserve.com)
No e-mail responses, please, unless explicitely requested!


Jim Langston

unread,
Mar 14, 1999, 3:00:00 AM3/14/99
to
Wow, thanks! I actually don't have to do this yet, but will
some time in the future. I'll just try to remember your title
for Dejanews 8-)


Rob Bennigan

unread,
Mar 14, 1999, 3:00:00 AM3/14/99
to
Thanks Peter... perhaps you would allow Borland to offer this in one of their TI
docs so others could benefit in the future once these messages have scrolled
off. An excellent example!

I also am registering the default icon for the file type by the following:

reg.OpenKey(Extension + '\DefaultIcon', true);
reg.WriteString('', 'C:\Myprogram.exe, 0');
reg.CloseKey;

This assigns the program's main icon for display in Explorer with the associated
extension.

The only problem is, it looks bad because it is a 32X32 that Windows shrinks.
So, I was wondering if I could add a second icon (16X16) to the resource file
(myprogram.res) and then change the second line to:

reg.WriteString('', 'C:\Myprogram.exe, 1');

I hoped that the 0 parameter was the icon resource offset... but so far I have
not been able to reference a second ICON in Myprogram.exe. Do you know if this
is possible?

Wayne Niddery (TeamB)

unread,
Mar 14, 1999, 3:00:00 AM3/14/99
to
Rob Bennigan wrote in message <7chavt$t7...@forums.borland.com>...

>
>The only problem is, it looks bad because it is a 32X32 that Windows
shrinks.
>So, I was wondering if I could add a second icon (16X16) to the resource
file
>(myprogram.res) and then change the second line to:
>
>reg.WriteString('', 'C:\Myprogram.exe, 1');


Each icon resource can include both a 16x16 and 32x32 version.

Go into Image Editor and edit this icon. At the top of the editor window
there's a combobox and a 'New' button, Click the button and create the other
size.

--
Wayne Niddery - WinWright Consulting
Delphi, C++Builder, JBuilder, InterDev --
http://home.ican.net/~wniddery/RADBooks.html
...remove chaff when replying...
"You know you've landed gear-up when it takes full power to taxi"

Petro

unread,
Mar 15, 1999, 3:00:00 AM3/15/99
to
Actually, I did this, but I can't seem to get the smaller icon to be the default
file icon. I did this by changing

reg.WriteString('', 'C:\Myprogram.exe, 0');

to
reg.WriteString('', 'C:\Myprogram.exe, 1');

Should this work? I checked the exe, and the smaller icon is there.


Peter Below (TeamB)

unread,
Mar 15, 1999, 3:00:00 AM3/15/99
to
In article <4A8H2.18$hR3...@news7.ispnews.com>, Petro wrote:
> Actually, I did this, but I can't seem to get the smaller icon to be the default
> file icon. I did this by changing

Not quite, i think. What Wayne told you to do is not to create a second, separate
icon but a second version of the *same* icon. An icon resource can contain several
versions of the same icon, e.g. for different sizes or color depth. Explorer is
smart enough to select the 16*16 version if it is there.

Peter Below (TeamB)

unread,
Mar 15, 1999, 3:00:00 AM3/15/99
to
In article <7chavt$t7...@forums.borland.com>, Rob Bennigan wrote:
> Thanks Peter... perhaps you would allow Borland to offer this in one of their TI
> docs so others could benefit in the future once these messages have scrolled
> off.
>
Oh, there is always www.dejanews.com <g>. I intend to submit this thing to
CodeCentral once it is up and running.

Rob Bennigan

unread,
Mar 16, 1999, 3:00:00 AM3/16/99
to
Peter,

I have implemented the Dde feature as you illustrated in your excellent example.
However, I do find one problem. After running the program and registering the
file types, sometimes when you click on an associated file from Explorer, I get
the following error:

Cannot find the file 'E:\robert.jpg' (or one of its components). Make sure the
path and filename are correct and that all required libraries are available.

The program, an image viewer, opens as it should and does display the correct
image file... it's just that this error dialog pops up before the program fully
has a chance to open and display the image. This only seems to happen before my
program is first opened. Once it has opened after the first double click in
Explorer, it seems to no longer generate this error as I double click other
image files in Explorer.

It seems to be an error in timing in the Dde communication. It's as if the OS is
returning this error because my application is opening up. My application is
relatively small and opens fairly quickly. There are other huge programs that
take a long time to open that don't seem to have this problem with their
associated file types.

Any suggestions as to why this would happen, and how I could remove this error
dialog from appearing, as it seems to actually work ok.

I'm using D3 by the way.


Peter Below (TeamB)

unread,
Mar 16, 1999, 3:00:00 AM3/16/99
to
> I have implemented the Dde feature as you illustrated in your excellent example.
> However, I do find one problem. After running the program and registering the
> file types, sometimes when you click on an associated file from Explorer, I get
> the following error:
>
> Cannot find the file 'E:\robert.jpg' (or one of its components). Make sure the
> path and filename are correct and that all required libraries are available.
>
> The program, an image viewer, opens as it should and does display the correct
> image file... it's just that this error dialog pops up before the program fully
> has a chance to open and display the image. This only seems to happen before my
> program is first opened. Once it has opened after the first double click in
> Explorer, it seems to no longer generate this error as I double click other
> image files in Explorer.
>
> It seems to be an error in timing in the Dde communication. It's as if the OS is
> returning this error because my application is opening up. My application is
> relatively small and opens fairly quickly. There are other huge programs that
> take a long time to open that don't seem to have this problem with their
> associated file types.

Your diagnosis is probably correct (timing issue). I did not see this problem in
my tests. Since you have the TDDEServerConv component on the main form the DDE
topic Explorer tries to use will not become available until the main form has been
created. This may take a tad too long for Explorers taste. If the diagnosis is
correct a prossible fix would be to create the TDDEServerConv component in code in
the DPR file or a Unit Initialization section, so it becomes available before the
main form is created. This creates a new problem, however: since the main form has
not been created yet you cannot use one of its methods as handler for the
OnExecuteMacro event. I would create a class that handles the DDE conversation and
puts macros into a stringlist as long as no handler has been attached to its own
event. An instance of this class is created in the main units Initialization
section and destroyed in the Finalization section. It creates the TDDEServerConv
and a TStringlist in its constructor and attaches one of its own methods to the
OnExecutemacro event of the TDDEServerConv. The event handler stuffs the received
strings into the stringlist and then calls a method that fires the classes
OnExecuteMacro event (or whatever you may call it). If the event has a handler the
macros are fed to it, otherwise nothing more happens yet. The event property gets
a Set method, that method immediately fires the event if there are macros in the
queue. This way the macros will get processed when the main form finally gets
around to attaching a handler to the event.

Looks like i have to modify this example <g>, will have to wait for the weekend,
though.

Peter Below (TeamB)

unread,
Mar 21, 1999, 3:00:00 AM3/21/99
to
> I await any further additions you can add to your example. I would attempt it,
> but that could take years <g>.

OK, i've modified the example according to the design i outlined in the last
message and that seems to solve the timing issue, even if i put a long delay
into the Formcreate (Seep(5000)) all files passed from Explorer are received
without any error messages from Explorer. Unfortunately the sample has grows
beyond a size easily put into a text message, so i've zipped it up and will send
it to you via e-mail. Interested lurkers are free to request copies as well.
0 new messages