كيف يمكنني معرفة ما إذا كان هناك مثيل آخر لبرنامجي قيد التشغيل بالفعل؟

StackOverflow https://stackoverflow.com/questions/459554

سؤال

كيف يمكنني معرفة ما إذا كان أحد مثيلات برنامجي قيد التشغيل؟اعتقدت أنه يمكنني القيام بذلك باستخدام ملف بيانات ولكنه سيكون فوضويًا :(

أريد أن أفعل هذا لأنني أريد فقط أن يتم فتح مثيل واحد عند نقطة واحدة.

هل كانت مفيدة؟

المحلول

يمكنك إنشاء إشارة وإيقاف التنفيذ (وضع الكود في ملف *.dpr الخاص بك) وإحضار التطبيق قيد التشغيل إلى الشاشة.

var
  Semafor: THandle;

begin
  { Don't start twice ... if already running bring this instance to front }
  Semafor := CreateSemaphore(nil, 0, 1, 'MY_APPLICATION_IS_RUNNING');
  if ((Semafor <> 0) and { application is already running }
     (GetLastError = ERROR_ALREADY_EXISTS)) then 
  begin
    RestoreWindow('TMyApplication');
    CloseHandle(Semafor);
    Halt;
  end;

  Application.CreateForm(....);    
  Application.Initialize;
  Application.Run;
  CloseHandle(Semafor);
end;

يحرر (أضاف RestoreWindow طريقة):

ال aFormName هو اسم فئة النموذج الرئيسية الخاصة بك في التطبيق الخاص بك.

procedure RestoreWindow(aFormName: string);
var
  Wnd,
  App: HWND;    
begin
  Wnd := FindWindow(PChar(aFormName), nil);
  if (Wnd <> 0) then 
  begin { Set Window to foreground }
    App := GetWindowLong(Wnd, GWL_HWNDPARENT);
    if IsIconic(App) then 
      ShowWindow(App, SW_RESTORE);

    SetForegroundwindow(App);
  end;
end;

نصائح أخرى

وكما اقترح جون أولا، يمكنك محاولة خلق كائن مزامنة. استدعاء CreateMutex . إذا كنت تحصل على مقبض غير الصفرية إلى الوراء، ثم استدعاء GetLastError . وسوف اقول لكم ما اذا كنتم الذي خلق مزامنة أو ما إذا كان مزامنة مفتوحة قبل (Error_Already_Exists) بالفعل. لاحظ أنه لا ضروري للحصول على ملكية كائن مزامنة. لا يتم استخدام مزامنة للاستبعاد المتبادل. يتم استخدامه لأنه هو كائن نواة اسمه. حدث أو إشارة يمكن أن تعمل أيضا.

وتقنية مزامنة يعطيك إجابة منطقية: نعم، هناك حالة أخرى، أو لا، ليس هناك

.

وترغب كثيرا في معرفة المزيد من ذلك تماما. على سبيل المثال، قد ترغب في معرفة مقبض النافذة الرئيسية على سبيل المثال الآخر لذلك يمكن أن أقول لكم أن تأتي إلى المقدمة في مكان المثال الأخرى الخاصة بك. حيث ان ملف الذاكرة المعنونة يمكن أن تأتي في متناول اليدين. ويمكن ان تعقد المعلومات حول المقام الأول حتى الحالات في وقت لاحق يمكن أن يشير إلى ذلك.

كن حذرا عند اختيار اسم كائن مزامنة. قراءة الوثائق بعناية، ونضع في اعتبارنا أنه لا يسمح لبعض الشخصيات (مثل مائل) في بعض إصدارات نظام التشغيل، ولكن هناك حاجة لبعض الميزات في إصدارات نظام التشغيل الأخرى.

وتذكر أيضا المشكلة من المستخدمين الآخرين. إذا يمكن تشغيل البرنامج عن طريق سطح المكتب البعيد أو التبديل السريع بين المستخدمين، ثم يمكن أن يكون هناك مستخدمين آخرين قيد التشغيل بالفعل البرنامج الخاص بك، وأنك قد لا تريد حقا أن تقييد المستخدم الحالي من تشغيل البرنامج. في هذه الحالة، لا تستخدم اسما عالميا. إذا كنت <م> قيام ترغب في تقييد الوصول لكافة المستخدمين، ثم تأكد من سمات الأمان الكائن كائن مزامنة هي هذه أن الجميع سوف تكون قادرة على فتح مؤشر على ذلك. باستخدام مؤشر فارغة للمعلمة lpSecurityAttributes ليست كافية لذلك؛ و"واصف الأمان الافتراضية" التي MSDN يذكر يعطي حق الوصول الكامل إلى المستخدم الحالي وعدم الوصول إلى الآخرين.

وكنت يسمح لتحرير الملف الشمالية من البرنامج. وهذا عادة ما يكون مكان جيد للقيام بهذا النوع من الشيء. إذا انتظرت حتى حدث OnCreate واحد من النماذج الخاصة بك، ثم البرنامج بالفعل قليلا من الزخم نحو يعمل بشكل طبيعي، لذلك فمن أخرق في محاولة لإنهاء البرنامج في تلك المرحلة. أفضل لإنهاء قبل الكثير من العمل UI تم القيام به. على سبيل المثال:

var
  mutex: THandle;
  mutexName: string;
begin
  mutexName := ConstructMutexName();

  mutex := CreateMutex(nil, False, PChar(mutexName));

  if mutex = 0 then
    RaiseLastOSError; // Couldn't open handle at all.

  if GetLastError = Error_Already_Exists then begin
    // We are not the first instance.
    SendDataToPreviousInstance(...);
    exit;
  end;
  // We are the first instance.

  // Do NOT close the mutex handle here. It must
  // remain open for the duration of your program,
  // or else later instances won't be able to
  // detect this instance.

  Application.Initialize;
  Application.CreateForm(...);
  Application.Run;
end.

وهناك مسألة متى لإغلاق مقبض مزامنة. لم يكن لديك لإغلاقه. عند إنهاء عملية الخاص بك في نهاية المطاف (حتى لو كان تعطل)، فإن نظام التشغيل تلقائيا إغلاق أي مقابض المعلقة، وعندما لا يكون هناك المزيد من مقابض مفتوحة، وسيتم تدمير الكائن كائن مزامنة (مما يسمح مثيل آخر من البرنامج لبدء وتعتبر نفسها ل أن تكون في المقام الأول).

ولكن قد ترغب في إغلاق المؤشر على أي حال. لنفترض أنك اخترت لتنفيذ وظيفة SendDataToPreviousInstance ذكرتها في التعليمات البرمجية. إذا كنت ترغب في الحصول على الهوى، ثم هل يمكن أن تمثل الحالة أن المثال السابق يتم إيقاف تشغيل بالفعل إلى أسفل وغير قادر على قبول البيانات الجديدة. ثم سوف لا تريد حقا أن إغلاق الدرجة الثانية. المقام الأول أن إغلاق المؤشر مزامنة بمجرد أن يعرف انها اغلاق، في الواقع يصبح "بطة عرجاء" مثلا. والحالة الثانية محاولة إنشاء مقبض مزامنة، النجاح، وتعتبر نفسها في المقام الأول الحقيقي. فإن المثال السابق دون انقطاع. استخدام CloseHandle لإغلاق مزامنة. يطلق عليه من OnClose معالج الحدث النموذج الرئيسي الخاص بك، أو في أي مكان آخر استدعاء Application.Terminate، على سبيل المثال.

وجميع الجبابرة JVCL لديها عنصر لهذا الغرض. انظر "TJvAppInstances".

قمت بإنشاء نظام كائن المزامنة.

ليس لدي كود دلفي، لكن إليك كود C++:

HANDLE Mutex;

const char MutexName[] = "MyUniqueProgramName";

Mutex = OpenMutex(MUTEX_ALL_ACCESS, false, MutexName);

if (Mutex)
     throw Exception("Program is already running.");
else
     Mutex = CreateMutex(NULL, true, MutexName);

الحل الطبيعي هو إنشاء ملف اسمه، على مستوى النظام كائن المزامنة.

  • إذا تمكنت من إنشائه، فأنت الشخص الذي يقوم بتشغيل التطبيق.
  • إذا لم تقم بذلك، فأنت تعلم أن هناك شيئًا مختلفًا.

يحرر:

لم أقدم الكود لأنني لا أعرف دلفي.يمكنني تقديم كود C# إذا كان ذلك مفيدًا.

وأود أن أضيف نقطة واحدة لفي الجواب ممتازة روب كينيدي (وبصرف النظر عن حقيقة أنه سيكون من الأفضل لجعل وظيفة من كود له بدلا من نسخ كل شيء في ملف الشمالية. ما عليك سوى معلمتين، و اسم كائن مزامنة، ومنطقية ما إذا كان mutext يجب أن يكون لكل مستخدم أو على نطاق المنظومة).

والجواب لا يعطي الكثير من الاعتبار لتسمية مزامنة. إذا كنت تتوقع البرنامج ليتم تثبيتها عن طريق إعداد ابتكارات (وأدوات الإعداد ربما غيرها أيضا) يجب اختيار اسم بعناية، حيث أن مزامنة يمكن استخدامها لديك الاختيار برنامج الإعداد ما إذا كان التطبيق قيد التشغيل حاليا، وتنبيه المستخدم بأن وينبغي إغلاق كافة مثيلات التطبيق. إذا اخترت السماح مثيل واحد من برنامج لكل مستخدم قد تحتاج إلى إنشاء كائن مزامنة على نطاق المنظومة الثاني أيضا، كما قد يحتاج الإعداد لديك أي حالات تشغيل التطبيق على على الإطلاق في ل تكون قادرة على استبدال الملفات. الاسم الذي هو لاستخدامها في تزامن مع يجب الثابت تلوينها المثبت InnoSetup.

وأود أن أقول أن هناك العديد من الاستراتيجيات المختلفة التي يمكنك استخدامها. ولكن أسهل واحد (ومنصة محددة لا) هو واحد أنت نفسك المقترحة، وهي ل، في بداية الشيك برنامج لمعرفة ما إذا كان هناك ملف قفل تم إنشاؤها في مجموعة، موقع معين. إذا كان هذا الملف موجودا القفل، ثم حالة أخرى قيد التشغيل بالفعل، إذا لم يكن موجودا، ثم ليس هناك حالة أخرى قيد التشغيل. عند إنهاء البرنامج، يمكنك حذف ملف القفل.

ولكن، وتوظيف هذه الإستراتيجية لديك مشكلة أخرى، ماذا يحدث إذا تعطل البرنامج؟ ملف القفل لا يزال، وهذه الحالة المحددة تحتاج إلى التعامل معها.

وهناك استراتيجية أخرى هي الحل مزامنة على نطاق المنظومة، حيث تسجل وجودكم داخل نظام التشغيل (أو كما انها المعقول أن يتم ذلك التلقائى). عندما ثم يحاول الدرجة الثانية لبدء، فإنه يتحقق إذا كان هناك بالفعل عملية نشطة مع هوية محددة. إذا كان موجودا بالفعل، والعملية الثانية يختار عدم بدء، واختياريا يجلب الأول "نافذة العملية في التركيز (إذا كانت العملية في مسألة تملك نافذة وهذا هو).

ولكن، هذه الاستراتيجية هو منهاج محدد، وسيكون التنفيذ تختلف من منصة الى منصة.

ويمكنك ببساطة استخدام FindWindow وظيفة ويندوز API. في دلفي اسم الفئة من الإطار هو نفس اسم الفئة، يمكنك تعريف اسم الفئة التي تجاوز وظيفة CreateParams. للتحقق مما إذا نافذة موجود إضافة رمز قبل إنشاء الإطار الرئيسي، قبل Application.Initialize؛

Program test
var 
  handle :HWND;
begin
  handle := FindWindow('TMySuperApp', nil);

  if IsWindow(handle) then
  begin 
       //app is running
       exit;
  end.

  Application.Initialize;
  Application.CreateForm(TMySuperApp, SuperApp);
  Application.Run;
end;

والسيطرة على عدد من الحالات التطبيق:

http://delphi.about.com/od/windowsshellapi/l/ aa100703a.htm

وانظر هذه الوحدة (باستخدام CreateMutex): UiApp

وبالإضافة إلى ذلك في هذه الصفحة، يمكنك قراءة مزايا وعيوب لهذا العمل مع وسائل بمختلف (مزامنة، FindWindows، ...).

وهذه الوحدة لديها حل لتفعيل المثال previos من التطبيق عندما تم الكشف عن هذا.

والتحيات وعذر لي لبلدي سيئة الانجليزية.


ونفتالي -الألماني استيفيز -

أذا أردت وقف التنفيذ التطبيق الخاص بك أكثر من مرة في نفس الوقت (ضع الكود في * ملف .dpr للمشروع).سيظهر رسالة بعد تشغيل التطبيق الثاني وإيقافه على الفور.

Forms,
  Unit1 in 'Unit1.pas' {Form1},
// add this units ....
TlHelp32,SysUtils,Windows,Dialogs;

{$R *.res}


function ProcessCount(const ExeName: String): Integer;
var
  ContinueLoop: BOOL;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
begin
  FSnapshotHandle:= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize:= SizeOf(FProcessEntry32);
  ContinueLoop:= Process32First(FSnapshotHandle, FProcessEntry32);
  Result:= 0;
  while Integer(ContinueLoop) <> 0 do begin
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
      UpperCase(ExeName)) or (UpperCase(FProcessEntry32.szExeFile) =
      UpperCase(ExeName))) then Inc(Result);
    ContinueLoop:= Process32Next(FSnapshotHandle, FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
end;


begin
  if ProcessCount(ExtractFileName(Application.ExeName)) > 1 then begin
    MessageDlg('Application is already running!', mtError, [mbOK], 0);
    Application.Terminate;
  end else begin

  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
  end;

end.

في الماضي، لقد استعملت مقبس لمنع حالات متعددة من يشغل في الوقت نفسه. إذا كان مأخذ قيد الاستخدام، لا الاستمرار في البرنامج، إذا كان متوفرا ترك كل شيء تشغيل كالمعتاد.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top