Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

Paramstr and multiple files

Posted on 1999-07-12
11
Medium Priority
?
438 Views
Last Modified: 2010-04-06
Hi,

I use this for opening a file when launched from explorer.

Procedure Tform1.FormCreate(Sender: TObject);
Var
 Parameters : String;
 X : Integer;
Begin
 // This code handles long filenames
 For x := 1 To Paramcount do
 Begin
 Parameters := Parameters + paramstr(X);

 If X < Paramcount then Parameters := Parameters + ' ';
 End;
 if Parameters <> '' Then Opencode(Parameters) Else
 //Nothing
End;

And then it is passed to

Procedure TForm1.Opencode(Const CreateFile: Ansistring);
Begin
 Listbox1.Items.Add(CreateFile);
 // I want to use .Assign, but that is not accepted
End;

This all works fine, when using only 1 file, When I select multiple files to open, Let's say 3, it launches 3 instances of my program, each with the next file.

How do I get all of the files in 1 instance, I use a Listbox
to store multiple files.

I also need to know if 1 or multiple files are available.
Can anyone give a suggestion??

Thanks
Marc
0
Comment
Question by:hellfire052497
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
  • 2
  • +2
11 Comments
 
LVL 13

Expert Comment

by:Epsylon
ID: 1387902
I can hardly understand what you are doing but is this what you want?


procedure TForm1.FormCreate(Sender: TObject);
Var
 Parameters : String;
 X : Integer;
Begin
 // This code handles long filenames
 For x := 1 To Paramcount do
   ListBox1.Items.Add(paramstr(X));
 //Nothing
End;

Epsylon.

0
 
LVL 5

Expert Comment

by:heathprovost
ID: 1387903
hellfire, I see what you are trying to do, but that is not right.  When windows passes your program a filename, it encloses it in quotes if there are spaces in it.  You code here:

// This code handles long filenames
 For x := 1 To Paramcount do
 Begin
 Parameters := Parameters + paramstr(X);

Is NOT needed at all. Epsylon's code is a textbook example of how to do this correctly.  But I digress, that is not what is causing your problem.  Windows does NOT pass your program the file names of each selected file.  It executes your executable passing each instance one of the selected filenames.  This is by design.  I dont know a simple way around this.  It is possible to do this, but I think you would need to use a DLL which had a exported function that loads the file into the list box.  That way you could detect if your program was already running, and if it was call the function in the DLL, passing it the handle of your first instance so that it could load the file into the listbox.  This would be rather involved code, and I am afraid it wouldnt be very easy.  I hope someone else knows a better way to do this...

Heath
0
 
LVL 5

Expert Comment

by:heathprovost
ID: 1387904
After thinking about it a while, I dont think it would be a good idea to try and change the default behavior of windows.  People expect it to work a certain way, and you would be altering the expected behavior.  Another method which would be much easier is to add filedrop support to your program.  that way a user could drop a group of files onto your program and it would open all of them in the current instance.  this is how most other programs (word, excel, etc) handle it, so you would be doing it the same way as everyone else.  there are components available that add this support to a form, it should be easy enough to find one.

Heath  
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 1

Expert Comment

by:rpo
ID: 1387905
There is a standard way to open multiple file using single application instance. It involves DDE, works on Windows 9x and NT and does not alter default behaviour of anything. The information about the method can be found in "Microsoft Programmer's Guide to Windows 95" shipped with Delphi 4. To get  it make a search for either "ddeexec" or "Q122787" in Find tab.
It can also be found in MSDN library.
The only problem with it is that you can not know how many files where selcted for opening, you receive their names one by one (of course you can make dirty trick with timer to see when you stop to receive filenames for some period of time, but, as i said it's dirty trick).

Roman.
0
 
LVL 10

Expert Comment

by:Lischke
ID: 1387906
hellfire,

As usually also multiple instances of an application are to prevent one can use a very elegant solution for this problem, without using DDE.

When an application is started and has already a running instance it stores its parameters into a shared area and notifies the previous instance, which then retrieves these parameters and acts accordingly. This would also work in your case. If you are interested in proved code I'll post it here...

Ciao, Mike
0
 

Author Comment

by:hellfire052497
ID: 1387907
yeah sure, post it

Thanks
Marc
0
 
LVL 10

Accepted Solution

by:
Lischke earned 300 total points
ID: 1387908
Ok then, here we go:

unit Instance;

// 20-JUN-99 ml: last change
//-----------------------------------------------------------------------------
// This unit provides a mechanism to allow only one instance of an application to run. Any
// following start of the same executable results in a notification of the first instance
// passing all parameters given to the second instance through to the first one.
//
// usage:
//  Call (as soon as possible) the function 'CheckForOtherInstance'.
//  You can use the source of the project:
//
//  begin
//    if not CheckForOtherInstance then
//    begin
//      Application.Title := 'Blah blah';
//      Application.CreateForm(TMainForm, MainForm);
//        :
//      Application.Run;
//    end;
//  end;
//
//  That's all for this part. The application will not be terminated automatically as this might not
//  desirable, but with the example shown above not much more happens beyond the check.
//
//  If the first instance needs to know which parameters have been passed then include a hook of the
//  application window (HookMainWindow/UnhookMainWindow) somewhere it fits (place is uncritical,
//  the handler doesn't even need to be in a TWinControl). The message to be catched is WM_OTHERINSTANCE
//  (posted, not sent).
//
//  While receiving the message WM_OTHERINSTANCE the WindowHook can use "GetInstanceParams" to get
//  a string list with the passed parameters (this list is guarded with a mutex object to synchronize
//  access to it. Note: The entry 0 in the list is the file name of the executable
//  (just like in ParamCount/ParamStr).

interface

uses Windows, SysUtils, Classes;

const OtherInstanceMsgName = 'MessageOtherInstance';
      OtherInstanceMutexName = 'MutexOtherInstance';

var WM_OTHERINSTANCE: Cardinal;

function CheckForOtherInstance: Boolean;
procedure GetInstanceParams(const List: TStringList);

//------------------------------------------------------------------------------

implementation

uses Dialogs, Forms;

const ParamSize = 4096;

type PInstInfo = ^TInstInfo;
     TInstInfo = record
       FirstInstance: THandle;
       Mutex: THandle; // for process synchronisation
       Params: array[0..ParamSize - 1] of Char;
     end;

var MappingHandle: THandle;  // handle of the filemapping object

//------------------------------------------------------------------------------

function CheckForOtherInstance: Boolean;

// Checks whether there is already a running instance of the same executable and
// ensures (if there's one) that it is activated and the own command line parameters
// are passed to it.

var Info: PInstInfo;
    CommandLine: PChar;

begin
  Result := False;
  // create shared memory
  MappingHandle := CreateFileMapping(DWORD(-1), nil, PAGE_READWRITE, 0, SizeOf(TInstInfo), PChar(ExtractFileName(Application.ExeName)));
  if MappingHandle <> 0 then
  begin
    // get pointer to data
    Info := MapViewOfFile(MappingHandle, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, SizeOf(TInstInfo));
    // does the mapping already exist?
    if GetLastError = ERROR_ALREADY_EXISTS then
    begin
      // well, there's already another instance running, so pass the parameter list,
      // notify the first instanz and get out off here
      Result := True;
      // ParamCount and ParamStr are much too long winded
      CommandLine := GetCommandLine;
      StrLCopy(Info.Params, CommandLine, ParamSize);
      if IsIconic(Info.FirstInstance) then ShowWindow(Info.FirstInstance, SW_RESTORE);
      SetForegroundWindow(Info.FirstInstance);
      PostMessage(Info.FirstInstance, WM_OTHERINSTANCE, 0, 0);
    end
    else
    begin
      // hey, this is the first instance, so init the data area and store the application handle
      FillChar(Info^, SizeOf(Info^), 0);
      Info.FirstInstance := Application.Handle;
      Info.Mutex := CreateMutex(nil, False, OtherInstanceMutexName);
    end;
    // free the view
    UnmapViewOfFile(Info);
  end
  else raise Exception.Create('internal error (Instance check): file mapping failed');
end;

//------------------------------------------------------------------------------

procedure GetInstanceParams(const List : TStringList);

// fills a list with the strings contained in the info structure

var Info : PInstInfo;

begin
  if assigned(List) then
  begin
    Info := MapViewOfFile(MappingHandle, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, SizeOf(TInstInfo));
    // wait until mutex is free
    WaitForSingleObject(Info.Mutex, INFINITE);
    List.Clear;
    List.CommaText := Info.Params;
    // free the view
    ReleaseMutex(Info.Mutex);
    UnmapViewOfFile(Info);
  end;
end;

//------------------------------------------------------------------------------

procedure FreeInstanceInfo;

// remove all allocated data from paging file

var Info : PInstInfo;

begin
  // something to do?
  if MappingHandle <> 0 then
  begin
    // free the mutex if it has been created by this instance
    Info := MapViewOfFile(MappingHandle, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, SizeOf(TInstInfo));
    if Info.FirstInstance = Application.Handle then
    begin
      // wait until mutex is free
      WaitForSingleObject(Info.Mutex, INFINITE);
      CloseHandle(Info.Mutex);
    end;
    UnmapViewOfFile(Info);
    CloseHandle(MappingHandle);
  end;
end;

//------------------------------------------------------------------------------

initialization
  WM_OTHERINSTANCE := RegisterWindowMessage(OtherInstanceMsgName);
finalization
  FreeInstanceInfo;
end.

The comments herein should explain what is to be considered. The first instance creates the file mapping. If another instance is created (which happens also if you have multiple files selected in the Explorer and choose "open" from the popup menu) it puts its paramters into the shared area and notifies the previous instance. The previous instance, in turn, gets the special message and can now read the parameters stored in the shared area by calling GetInstanceParams.

Ciao, Mike
0
 

Author Comment

by:hellfire052497
ID: 1387909
Hi,

Does this code work??, The List.Commatext value remains empty, I added at various places a Showmessage('passed'); But none where called.
But then I may be missing something, it's so complex.

Perhaps it is easier if you send a small working sample, If you could add a listbox on the form, and add all files that are launched from explorer to it automatically, then I have all I need.

You can send it at hellfire@unforgettable.com

Thanks
Marc
0
 
LVL 10

Expert Comment

by:Lischke
ID: 1387910
Oh yes, this code is proved to be working. Let's make sure that you have build in all you need. Look through the check list below:

1) You have created a project and registered it to be called when double clicking (etc.) on a specific file.
2) In the project source code (*.dpr) you have included the check for another instance.
3) Somewhere in the project you called HookMainWindow.
4) The code which you registered with HookMainWindow is called and you filter out WM_OTHERINSTANCE like:

function TMainForm.WindowHook(var Message: TMessage): Boolean;

// watch other instances of this application

var List: TStringList;

begin
  if Message.Msg = WM_OTHERINSTANCE then
  begin
    List := TStringList.Create;
    List.Duplicates := dupIgnore;

    GetInstanceParams(List);
    // element 0 is the executable itself
    List.Delete(0);
    AddProjectFiles(List);
    List.Free;
    Message.Result := 0;
    Result := True;
  end
  else
  :
5) You received this message and you called GetInstanceParams.

Does this all apply?

Ciao, Mike
0
 

Author Comment

by:hellfire052497
ID: 1387911
How does 2 work?

Never changed anything in a .dpr before?

Thanks
Marc
0
 
LVL 10

Expert Comment

by:Lischke
ID: 1387912
Go to Project|View Source (D4) or View|Project Source (D3). You can also open the dpr file in any text editor (don't use Delphi's open function, as this will open the project as such).

Ciao, Mike
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…
In this video, Percona Solution Engineer Dimitri Vanoverbeke discusses why you want to use at least three nodes in a database cluster. To discuss how Percona Consulting can help with your design and architecture needs for your database and infras…
Suggested Courses

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question