Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2 3   вперед  Ctrl      все
 Запретить запуск программы два раза  [new]
minva
Member

Откуда: г. Калуга
Сообщений: 1162
Такой код работает, но если войти на комп удаленно под другим пользователем, то программу запустить можно.
 MuxHandle := CreateMutex(nil, false, pchar('XXX' + ParamStr(1) + '.mux'));
  if GetLastError() = ERROR_ALREADY_EXISTS then
  begin
    ShowError('Повторный запуск!');
    CloseHandle(MuxHandle);
    ExitProcess(1)
  end;


Если написать так (если я правильно понял MSDN)
MuxHandle := CreateMutex(nil, false, pchar('Global\XXX' + ParamStr(1) + '.mux'));

то GetLastError() вообще 0 возвращает, программу тогда можно запустить и под одним пользователем два раза.

Собственно вопрос. Какой флаг нужно выбросить в систему, что программу нельзя было запустить два раза на компьютере?
Предложение сделать сервис рассматривается, но там есть другие проблемы.
24 апр 18, 10:26    [21363343]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
GunSmoker
Member

Откуда:
Сообщений: 3065
Влияние на другого пользователя - это то, что должны и могут делать только администраторы, а не обычные пользователи.
24 апр 18, 11:01    [21363446]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
d7i
Member

Откуда:
Сообщений: 343
Вот пример на С (WinAPI), переделать не трудно:

  public:
    typedef HANDLE (WINAPI *CREATESNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID);
    typedef BOOL (WINAPI *PROCESSWALK)(HANDLE hSnapshot, LPPROCESSENTRY32 lppe);
  private:
    PROCESSWALK _pProcess32First;
    PROCESSWALK _pProcess32Next;
    CREATESNAPSHOT _pCreateToolhelp32Snapshot;

    HINSTANCE  hKernel = NULL;
    HANDLE         hProcessSnap = NULL;
    PROCESSENTRY32 pe32;
    WFilePath       path;
    WInt    flag;

    flag=0;

    hKernel = (HINSTANCE) GetModuleHandle("KERNEL32.DLL");
    _pCreateToolhelp32Snapshot = (CREATESNAPSHOT)GetProcAddress(hKernel,"CreateToolhelp32Snapshot");
    _pProcess32First = (PROCESSWALK)GetProcAddress(hKernel,"Process32First");
    _pProcess32Next  = (PROCESSWALK)GetProcAddress(hKernel,"Process32Next");

    hProcessSnap = _pCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(hProcessSnap == (HANDLE)-1)
      return false;

    pe32.dwSize = sizeof(PROCESSENTRY32);

    if(_pProcess32First(hProcessSnap,&pe32)) 
      do
      {
        path = pe32.szExeFile;
        if(path=="XXXXXX.exe")     // имя процесса (программы)
        {
          flag+=1;
          if(flag>1)
          {
            WMessageBox::Info(NULL, "Внимание !!!", "Запуск второго экземпляра запрещен !");
            CloseHandle (hProcessSnap);
            FreeLibrary(hKernel);
            ExitProcess(0);
            Close();
            return false;
          }  
        }   
      }while(_pProcess32Next(hProcessSnap,&pe32));

      CloseHandle(hProcessSnap);
      FreeLibrary(hKernel);
24 апр 18, 11:19    [21363522]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
L_argo
Member

Откуда:
Сообщений: 221
Все запускаются с одного ЕХЕ, или может быть неск. копий ЕХЕ в юзерских папках ?
В общей папке можно создать и заблокировать некий файл.
Тогда попытка пересоздать файл другой копией закончится ошибкой. Как вариант.
24 апр 18, 11:25    [21363556]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
DimaBr
Member

Откуда:
Сообщений: 10596
Я всё равно запущу несколько копий, на нескольких виртуалках. Если мне нужны несколько копий.
24 апр 18, 11:31    [21363578]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
чччД
Guest
minva,

ты на сравнивай с ERROR_ALREADY_EXISTS
 if GetLastError() = ERROR_ALREADY_EXISTS

- ты просто проверяй, не создался ли мьютекс.
Если такой юзер уже есть, мьютекс не будет создан.
И ParamStr(1) для генерации имени не используй: переименуют файл экзешника, и все.
24 апр 18, 11:53    [21363680]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
чччД
Guest
DimaBr
Я всё равно запущу несколько копий, на нескольких виртуалках. Если мне нужны несколько копий.

Бывает, что нужна защита не от злоумышленника. Например, от повторного запуска приложения-сервера, которое слушает tcp порт.
24 апр 18, 11:56    [21363695]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 45444

чччД
Например, от повторного запуска приложения-сервера, которое слушает tcp порт.

Обработка ошибки "port in use" от listen() за пределами способностей современных
программистов?..

Posted via ActualForum NNTP Server 1.5

24 апр 18, 12:04    [21363737]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1085
Есть вот такая реализация, может поможет.
//wrSession - Allow only one instance per login session
//wrDesktop - Allow only one instance on current desktop
//wrTrustee - Allow only one instance for current user
//wrSystem - Allow only one instance at all (on the whole system)

+
unit uOnlyInstanceApplication;

interface

uses
  Winapi.Windows,
  System.SyncObjs;

type
//wrSession - Allow only one instance per login session
//wrDesktop - Allow only one instance on current desktop
//wrTrustee - Allow only one instance for current user
//wrSystem  - Allow only one instance at all (on the whole system)
  TWayToRun = (wrSystem, wrDesktop, wrSession, wrTrustee);

  TOnlyInstanceApplication = class
  private
    FMutex: TMutex;
    FName: string;
    FResult: DWORD;
    FWayToRun: TWayToRun;
    function CreateUniqueName: string;
  public
    constructor Create(aWayToRun: TWayToRun; const aName: string);
    destructor Destroy; override;
    function IsInstancePresent: Boolean;
  end;

implementation

uses
  System.SysUtils;

{ TOnlyInstanceApplication }

constructor TOnlyInstanceApplication.Create(aWayToRun: TWayToRun; const aName: string);
begin
  FWayToRun := aWayToRun;
  FName := aName;
  FMutex := TMutex.Create(nil, False, CreateUniqueName);
  FResult := GetLastError();
end;

function TOnlyInstanceApplication.CreateUniqueName: string;
var
  desktop: HDESK;
  len: DWORD;
  res: BOOL;
  resultError: DWORD;
  buffer, buffer2: TBytes;
  token: THandle;
  uid: LUID;
begin
  case FWayToRun of
    wrSystem:
      Result := FName;
    wrDesktop:
    begin
      desktop := GetThreadDesktop(GetCurrentThreadId());
      res := GetUserObjectInformation(desktop, UOI_NAME, nil, 0, len);
      resultError := GetLastError();
      if (not res) and (resultError = ERROR_INSUFFICIENT_BUFFER) then
      begin
        SetLength(buffer, len);
        GetUserObjectInformation(desktop, UOI_NAME, @buffer[0], len, len);
        Result := FName + '-' + PChar(@buffer[0]);
      end
      else
        Result := FName + '-Win9x';
    end;
    wrSession:
    begin
      res := OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, token);
      if res then
      begin
        GetTokenInformation(token, TokenStatistics, nil, 0, len);
        SetLength(buffer, len);
        GetTokenInformation(token, TokenStatistics, @buffer[0], len, len);
        uid := PTokenStatistics(@buffer[0]).AuthenticationId;
        Result := FName + Format('-%.8x%.8x', [uid.HighPart, uid.LowPart]);
      end
      else
        Result := FName;
    end;
    wrTrustee:
    begin
      SetLength(buffer, 255);
      if GetUserName(@buffer[0], len) then
      begin
        SetLength(buffer, len * 2);
        SetLength(buffer2, 255);
        len := ExpandEnvironmentStrings('%USERDOMAIN%', @buffer2[0], 255);
        SetLength(buffer2, len);
        Result := FName + Format('-%s-%s', [PChar(@buffer2[0]), PChar(@buffer[0])]);
      end
      else
        Result := FName;
    end;
  end;
end;

destructor TOnlyInstanceApplication.Destroy;
begin
  FMutex.Free();
  inherited Destroy();
end;

function TOnlyInstanceApplication.IsInstancePresent: Boolean;
begin
  Result := (FResult = ERROR_ALREADY_EXISTS) or (FResult = ERROR_ACCESS_DENIED);
end;

end.


GUID_APPLICATION = '{11112222-3333-1111-4444-555566661111}';
FOnlyInstanceApplication := TOnlyInstanceApplication.Create(wrSystem, GUID_APPLICATION);
if FOnlyInstanceApplication.IsInstancePresent then
  атата
24 апр 18, 12:16    [21363798]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
чччД
Guest
Dimitry Sibiryakov
чччД
Например, от повторного запуска приложения-сервера, которое слушает tcp порт.

Обработка ошибки "port in use" от listen() за пределами способностей современных
программистов?..

Ну вот и расскажи про это своим друзьям-разработчикам фаерберда.
24 апр 18, 12:28    [21363844]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 45444

чччД
Ну вот и расскажи про это своим друзьям-разработчикам фаерберда.

Firebird, в отличии от некоторых, работает по многим протоколам и проблемы с одним из них
- не повод отказываться работать вообще.

Posted via ActualForum NNTP Server 1.5

24 апр 18, 13:11    [21364098]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
чччД
Guest
Dimitry Sibiryakov
чччД
Ну вот и расскажи про это своим друзьям-разработчикам фаерберда.

Firebird, в отличии от некоторых, работает по многим протоколам и проблемы с одним из них
- не повод отказываться работать вообще.


Странный метод общения. Ты отвечаешь на придуманные именно тобой якобы проблемы у собеседника.
24 апр 18, 13:25    [21364202]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
YuRock
Member

Откуда: Донецк
Сообщений: 3510
чччД
Ты отвечаешь на придуманные именно тобой якобы проблемы у собеседника.
Да нет, он имел ввиду проблемы в работе одного из протоколов Firebird после запуска.
24 апр 18, 13:46    [21364338]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 10450
чччД
И ParamStr(1) для генерации имени не используй: переименуют файл экзешника, и все.
При чем здесь ParamStr(1) и имя экзешника?
24 апр 18, 14:43    [21364623]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
Котовасия
Member

Откуда:
Сообщений: 136
_Vasilisk_,

да, это первый параметр, не имя модуля, ни при чем, лоханулся.
24 апр 18, 14:46    [21364642]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
Соколинский Борис
Member

Откуда: Москва
Сообщений: 7888
minva
Собственно вопрос. Какой флаг нужно выбросить в систему, что программу нельзя было запустить два раза на компьютере?

Очевидно, использовать средства, независящие от контекста пользователя.
Можно просто файл лочить, а-ля BDE.
24 апр 18, 15:00    [21364730]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
Алексей Гаврилов
Guest
X-Cite,

Не хватает для текущей сети :-)

Как ведет под терминальным сервером?
28 апр 18, 11:27    [21376547]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
RADSeatle
Member

Откуда:
Сообщений: 117
Когда то использвоваль этот либ может поможет
http://www.loonies.narod.ru/t-one-instance.htm
30 апр 18, 07:38    [21379446]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
stells2
Member

Откуда: Оклахома Пригород Колымы
Сообщений: 878
minva,
Если еще актуально.
Функция проверки работающего приложения
function IsRunPrg(sname: String): boolean;
// ПРОВЕРКА - ЗАПУЩЕННА ЛИ ЭТА ПРОГРАММА
var
  hMutex: integer;
  hW: hWnd;
BEGIN
  hMutex := CreateMutex(nil, TRUE, PChar(sname)); // Создаем семафор
  result := GetLastError <> 0; // семафор создан, можем работать
  if (result = TRUE) then
  begin
    hW := FindWindow(CLASS_FMAIN, nil);
    if (hW <> 0) then
      SenMsg(MSG_MYFRM, CLASS_FMAIN, OPR_SHOW_WND, '');
  end;
  ReleaseMutex(hMutex);
END;


В главном модуле
uses
  Forms,
  windows,
  SysUtils,
  uMain in 'uMain.pas' {Fmain},
  uUtils in 'uUtils.pas',
  uConst in 'uConst.pas',
....;
{$R *.res}
{$ifdef Debug}
{$ASSERTIONS ON}
{$else}
{$ASSERTIONS OFF}
{$endif}
var frm:TFSplash;
    StartD:TDateTime;
    YY,MM,DD,HH,MN,SS,MS:integer;
begin
  Application.MainFormOnTaskbar := True;
   try
     if( IsRunPrg(ExtractFileName(Application.ExeName))) then halt;
// иначе продолжаем нормальный старт программы


константы можно определить как угодно
в моём случае
CLASS_FMAIN  = 'TFmain';
MSG_MYFRM   = WM_USER + 4243;
OPR_SHOW_WND      = 1000;


стартуем приложение, если оно уже запущено - открывается/активируется работающее и останавливается запускаемое.

function IsRunPrg - у меня находится в отдельном модуле uUtils, в виде обычной функции без класса.
константы естественно в модуле uConst
17 май 18, 06:33    [21415500]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
goldmi45
Member

Откуда:
Сообщений: 986
stells2, предложенный код будет работать, если на этот же компьютер зайти удаленно и запустить программу другим пользователем?
И если этот код будет работать при попытке запуска программы несколькими пользователями, то как вы будете активировать работающее приложение из другой сессии?
17 май 18, 08:26    [21415597]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
stells2
Member

Откуда: Оклахома Пригород Колымы
Сообщений: 878
goldmi45,
Да, проверил. В одной сессии работает корректно. В разных - не "видит" запущенный экземпляр. Видимо, просто в API надо глубже залезть. :(
Возможно, переключение контекста или еще что.
Согласен.
17 май 18, 11:52    [21416435]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1085
stells2
goldmi45,
Да, проверил. В одной сессии работает корректно. В разных - не "видит" запущенный экземпляр. Видимо, просто в API надо глубже залезть. :(
Возможно, переключение контекста или еще что.
Согласен.

Есть глобальный Mutex, а есть локальный.. решается префиксом

On a server that is running Terminal Services, a named system mutex can have two levels of visibility. If its name begins with the prefix "Global\", the mutex is visible in all terminal server sessions. If its name begins with the prefix "Local\", the mutex is visible only in the terminal server session where it was created. In that case, a separate mutex with the same name can exist in each of the other terminal server sessions on the server. If you do not specify a prefix when you create a named mutex, it takes the prefix "Local\". Within a terminal server session, two mutexes whose names differ only by their prefixes are separate mutexes, and both are visible to all processes in the terminal server session. That is, the prefix names "Global\" and "Local\" describe the scope of the mutex name relative to terminal server sessions, not relative to processes.
17 май 18, 12:41    [21416647]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
makhaon
Member

Откуда: A galaxy far far away
Сообщений: 2676
stells2,

мьютекс надо делать именованным и с приставкой Global, тогда будет видно везде.
17 май 18, 12:53    [21416707]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 10450
makhaon
мьютекс надо делать именованным и с приставкой Global, тогда будет видно везде.
Для создания глобального объекта может не хватить прав.

По теме - открывается сокет на прослушку у все. Второй сокет уже не откроется, а по сокету можно еще и команду послать
17 май 18, 13:30    [21416837]     Ответить | Цитировать Сообщить модератору
 Re: Запретить запуск программы два раза  [new]
stells2
Member

Откуда: Оклахома Пригород Колымы
Сообщений: 878
Да, видно :) Ладно, может кому пригодится.
function ExistsPrg(sname: String): boolean;
var
  hMutex: integer;
  hW: hWnd;
BEGIN
  hW := 0;
  hMutex := CreateMutex(nil, TRUE, PChar('Global\'+sname)); 
  result := GetLastError <> 0; 
  if (result = TRUE) then
  begin
    hW := FindWindow(CLASS_FMAIN, nil); 
    if (hW <> 0) then
      SenMsg(MSG_MYFRM, CLASS_FMAIN, OPR_SHOW_WND, ''); // тут увы, пока между сессиями нет переключения.
  end;
  ReleaseMutex(hMutex);
END;
17 май 18, 13:50    [21416947]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2 3   вперед  Ctrl      все
Все форумы / Delphi Ответить