File: Instance.pas

package info (click to toggle)
mysql-query-browser 1.2.5beta-3
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 63,792 kB
  • ctags: 46,485
  • sloc: pascal: 249,299; ansic: 80,111; cpp: 72,467; sh: 25,271; objc: 20,015; yacc: 10,755; java: 9,917; xml: 4,580; php: 2,806; python: 1,566; sql: 1,563; makefile: 1,452; perl: 3
file content (226 lines) | stat: -rw-r--r-- 8,627 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
unit Instance;

// Copyright (C) 2003, 2004 MySQL AB
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// This unit contains code to prevent an application from starting more than once.
//
// The initial author is Mike Lischke.

interface

uses
  Windows, SysUtils, Classes;

const
  OtherInstanceMsgName = 'MySQLGeneralCommunicationMessage'; // Do not localize.
  OtherInstanceMutexName = 'MySQLMutexOtherInstance'; // Do not localize.

var
  // This message is posted to the (hidden) application window (and can be handled anywhere by hooking this window)
  // if another instance was started and forwarded execution to this instance.
  WM_OTHERINSTANCE: Cardinal;

function OtherInstanceIsRunning(const Name: WideString = ''): Boolean;
procedure GetInstanceParams(const List: TStringList);

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

implementation

uses
  Forms;

const
  ParamSize = 32768; // The maximum number of Unicode characters that can be transferred from one instance to another.

type
  // Standard record containing general info shared between all instances.
  PInstanceInfo = ^TInstInfo;
  TInstInfo = record
    FirstInstance: THandle;             // Application handle of the first instance that created this record.
    ForeignExe: Boolean;                // Shows if this info was created for an application with different executable name
                                        // (True) or the same name as this application (False).
    Mutex: THandle;                     // Used for access synchronization between processes.
    Params: array[0..ParamSize - 1] of WideChar;
  end;

  // Short instance record used between different applications. Only a subset of the general case can be transferred.
  PShortInstInfo = ^TShortInstInfo;
  TShortInstInfo = record
    FirstInstance: THandle;
    ForeignExe: Boolean;
  end;

var
  MappingHandle: THandle;               // Non-shared file mapping handle used in this instance to access the shared data.

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

function OtherInstanceIsRunning(const Name: WideString = ''): Boolean;

// Checks whether another instance of a given application is running. It does this by asking for a file mapping object
// with either the given name or the name of this application (without path info). If this object exists then there is
// already an instance and True is returned. Otherwise the file mapping object is freshly created and this method returns false.

var
  Info: PInstanceInfo;
  CommandLine: PWideChar;
  InfoSize: Cardinal;
  MappingName: WideString;

begin
  // This test should not be necessary unless an application calls this method twice.
  Result := MappingHandle <> 0;
  if not Result then
  begin
    // Determine name to be used for the file mapping object.
    if Name = '' then
    begin
      InfoSize := SizeOf(TInstInfo);
      MappingName := ExtractFileName(Application.ExeName);
    end
    else
    begin
      InfoSize := SizeOf(TShortInstInfo);
      MappingName := Name;
    end;

    // Keep in mind CreateFileMappingW is not supported on Win 9x/Me!
    MappingHandle := CreateFileMappingW(DWORD(-1), nil, PAGE_READWRITE, 0, InfoSize, PWideChar(MappingName));
    if MappingHandle <> 0 then
    begin
      // Get a pointer to the shared memory area.
      Info := MapViewOfFile(MappingHandle, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, InfoSize);
      // Did the mapping already exist?
      if GetLastError = ERROR_ALREADY_EXISTS then
      begin
        // Yes, there was already an instance that registered the shared area already. Pass it our command line and
        // get out of here.
        Result := True;
        // Command line can only be passed if we are dealing with an instance of the same application (actually, the
        // same application name was used for the shared area).
        if Name = '' then
        begin
          Info.ForeignExe := False;
          CommandLine := GetCommandLineW;

          // Synchronize access to the shared area.
          WaitForSingleObject(Info.Mutex, INFINITE);
          Move(CommandLine^, Info.Params, 2 * (lstrlenW(CommandLine) + 1)); // Move one char more for #0.
          ReleaseMutex(Info.Mutex);
        end
        else
          Info.ForeignExe := True;

        // Restore application window if it is minimized. Bring it to foreground and tell it that we were here.
        if IsIconic(Info.FirstInstance) then
          ShowWindow(Info.FirstInstance, SW_RESTORE);
        SetForegroundWindow(Info.FirstInstance);
        PostMessage(Info.FirstInstance, WM_OTHERINSTANCE, 0, 0);
      end
      else
      begin
        // This is the first instance. Store our info in the shared area.
        FillChar(Info^, InfoSize, 0);

        // The handle is that of the main form so WM_OTHERINSTANCE must be handled there if needed.
        Info.FirstInstance := Application.Handle;

        // Mutex is only needed if there is a shared memory area. And a shared area is only used for instances
        // of the same application.
        if Name = '' then
        begin
          Info.Mutex := CreateMutexW(nil, False, PWideChar(OtherInstanceMutexName + '#' + MappingName));
          Info.ForeignExe := False;
        end
        else
          Info.ForeignExe := True;
      end;
      // Free mapping to memory area.
      UnmapViewOfFile(Info);
    end
    else
      raise Exception.Create('Internal error (Instance check): file mapping failed');
  end;
end;

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

procedure GetInstanceParams(const List: TStringList);

// Reads the current content of the shared memory area. The info is filled into the given string list.
// Note. TStringList is not Unicode aware so all strings are converted to ANSI using the current user locale.

var
  Info: PInstanceInfo;

begin
  if Assigned(List) and (MappingHandle <> 0) then
  begin
    Info := MapViewOfFile(MappingHandle, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, SizeOf(TInstInfo));
    try
      if Info.ForeignExe then
        raise Exception.Create('internal error (Instance parameters): foreign executable');

      // Synchronize access to the shared area.
      WaitForSingleObject(Info.Mutex, INFINITE);
      List.Clear;
      List.CommaText := Info.Params;
      ReleaseMutex(Info.Mutex);
    finally
      UnmapViewOfFile(Info);
    end;
  end;
end;

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

procedure FreeInstanceInfo;

// Do some house keeping.

var
  Info: PInstanceInfo;

begin
  // Anything to do?
  if MappingHandle <> 0 then
  begin
    // The instance that created the mutex must also release it.
    Info := MapViewOfFile(MappingHandle, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, SizeOf(TInstInfo));
    if Info = nil then
      Info := MapViewOfFile(MappingHandle, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, SizeOf(TShortInstInfo));
    if (Info.FirstInstance = Application.Handle) and not Info.ForeignExe then
    begin
      // Wait until the mutex is free and then close it.
      WaitForSingleObject(Info.Mutex, INFINITE);
      CloseHandle(Info.Mutex);
    end;
    // Close the view and the mapping too.
    UnmapViewOfFile(Info);
    CloseHandle(MappingHandle);
  end;
end;

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

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