كيفية إنهاء تطبيق "نظيفة" في Win32

ملخص

في عالم مثالي، يمكن أن يطلب العملية عملية أخرى، من خلال شكل من اتصال بين العمليات، لإيقاف تشغيل. ومع ذلك، إذا لم تكن مراقبة مستوى المصدر للتطبيق الذي ترغب في إيقاف تشغيل، ثم قد لا يكون لديك هذا الخيار. على الرغم من أنه ليست هناك طريقة مضمونة "نظيف" لإيقاف تشغيل أحد تطبيقات في Win32، هناك خطوات يمكنك اتخاذها للتأكد من أن التطبيق يستخدم أفضل طريقة لتنظيف الموارد.

مزيد من المعلومات

عمليات 32 بت (والعمليات ذات 16 بت في Windows 95)

ضمن Win32، وعود نظام التشغيل لتنظيف الموارد المملوكة من قبل عملية عند إيقاف. هذا، ومع ذلك، يعني أن العملية ذاتها أتيحت له الفرصة للقيام بأي مسح نهائي من المعلومات إلى القرص، أي اتصال النهائي عبر اتصال بعيد، كما أنه لا يعني أن العملية DLL ستتاح الفرصة لتنفيذ التعليمات البرمجية الخاصة بهم PROCESS_DETACH. هذا السبب في أنه من الأفضل عموما تجنب إنهاء تطبيق ما ضمن نظام التشغيل Windows 95 و Windows NT.


حتما يجب إيقاف عملية، اتبع الخطوات التالية:
  1. ترحيل WM_CLOSE إلى كافة الإطارات الأعلى المملوكة العملية التي تريد إيقاف تشغيل. العديد من تطبيقات Windows الرد على هذه الرسالة عن طريق إيقاف تشغيل.

    ملاحظة: تعتمد على ما إذا كان تم تثبيت معالج تحكم استجابة تطبيق وحدة التحكم WM_CLOSE.


    استخدام EnumWindows() للبحث عن المؤشرات للإطارات الهدف. في دالة رد الاتصال الخاص بك، تحقق لمعرفة إذا كان معرف العملية windows تطابق العملية التي ترغب في إيقاف تشغيل. يمكنك القيام بذلك عن طريق استدعاء GetWindowThreadProcessId(). بمجرد التأكد من مطابقة، استخدم PostMessage() أو SendMessageTimeout() لنشر رسالة WM_CLOSE إلى الإطار.
  2. استخدام WaitForSingleObject() لانتظار مؤشر العملية. تأكد من الانتظار بقيمة مهلة، لأن هناك الكثير من الحالات التي WM_CLOSE سيتم عدم إيقاف تشغيل التطبيق. احرص على أن تكون المهلة طويلة بما يكفي (باستخدام WaitForSingleObject() أو SendMessageTimeout()) حتى يستجيب مستخدم يمكن لأي حوار مربعات التي أنشئت استجابة لرسالة WM_CLOSE.
  3. إذا كانت قيمة الإرجاع WAIT_OBJECT_0، ثم إغلاق التطبيق نفسه نظيفة. إذا كانت قيمة الإرجاع WAIT_TIMEOUT، ثم يجب استخدام TerminateProcess() لإيقاف تشغيل التطبيق.

    ملاحظة: إذا كنت getting3 إرجاع القيمة من WaitForSingleObject() أخرى ثم WAIT_OBJECT_0 أو WAIT_TIMEOUT، استخدم GetLastError() لتحديد السبب.
باتباع الخطوات التالية، يمكنك إعطاء التطبيق أفضل فرصة ممكنة لإيقاف تشغيل نظيفة (وبصرف النظر عن التصنيف الدولي للبراءات أو تدخل من قبل المستخدم).

الإصدار 16 بت (تحت Windows NT)

السابقين تعمل تطبيقات 16 بت في Windows 95، على الرغم من ذلك، العمل تطبيقات Windows NT 16-بت بشكل مختلف تماما.


تحت Windows NT، يتم تشغيل كافة تطبيقات 16 بت في جهاز DOS ظاهري (VDM). تشغيل VDM هذا كعملية Win32 (NTVDM) تحت Windows NT. عملية NTVDM يحتوي على معرف عملية. يمكنك الحصول على مؤشر للعملية من خلال OpenProcess()، تماما كما يمكنك القيام بأي عملية Win32. وبالرغم من ذلك، أي من التطبيقات ذات 16 بت التي تعمل في VDM لدى معرف عملية ولذلك لا يمكنك الحصول على "معالجة عملية" من OpenProcess(). يحتوي كل تطبيق 16-بت في VDM "مؤشر المهمة" ذات 16-بت و 32 بت مؤشر ترابط التنفيذ. يمكن العثور على معرف مؤشر الترابط والتعامل مع خلال استدعاء الدالة VDMEnumTaskWOWEx(). لمزيد من المعلومات، الرجاء مراجعة المقالة التالية في "قاعدة المعارف ل Microsoft":
175030 كيفية تعداد التطبيقات في Win32
لديك الخيار الأول والأكثر وضوحاً عند إيقاف تشغيل أحد تطبيقات 16 بت تحت Windows NT إيقاف عملية NTVDM كاملة. يمكنك القيام بذلك عن طريق اتباع الخطوات الموضحة أعلاه. تحتاج فقط إلى معرفة معرف العملية للعملية NTVDM (راجع مقالة قاعدة المعارف 175030 المشار إليها أعلاه للعثور على معرف عملية NTVDM). أن التأثير السلبي لهذا النهج هو أنه يتم إغلاق كافة التطبيقات ذات 16 بت التي تعمل في ذلك VDM. إذا لم يكن هذا الهدف، يمكنك حينئذ اتباع نهج آخر.


إذا كنت ترغب في إيقاف تشغيل أحد تطبيقات 16 بت واحد في عملية NTVDM، والخطوات التي يجب أن تتخذ فيما يلي:

  1. نشر WM_CLOSE إلى كافة الإطارات ذات المستوى الأعلى التي يملكها العملية، والتي لها نفس معرف مؤشر الترابط المالك كالمهمة 16 بت التي ترغب في إيقاف تشغيل. الطريقة الأكثر فعالية للقيام بذلك باستخدام EnumWindows(). في دالة رد الاتصال الخاص بك، تحقق لمعرفة إذا كان مطابقاً لعملية الإطار معرف مؤشر الترابط والمهمه 16 بت التي ترغب في إيقاف تشغيل. تذكر أن معرف العملية سوف يكون معرف العملية للعملية NTVDM فيه تشغيل التطبيقات ذات 16 بت.
  2. على الرغم من أن لديك معرف مؤشر ترابط، لديه أية طريقة للانتظار على إنهاء عملية ذات 16-بت. وكنتيجة لذلك، يجب الانتظار لفترة عشوائية من الوقت (ﻹتاحة تنظيف إيقاف) ومحاولة إيقاف تشغيل التطبيق على أي حال. إذا تم مسبقاً إيقاف تشغيل التطبيق، ثم هذا لن يفعل شيئا. إذا لم إيقاف تشغيله، ثم أنه سيتم إنهاء التطبيق.
  3. إنهاء التطبيق باستخدام دالة تسمى VDMTerminateTaskWOW()، يمكن الاطلاع عليها في Vdmdbg.dll. يستغرق معرف عملية VDM وعدد المهام المهمة ذات 16-بت.
هذا الأسلوب يتيح إمكانية إيقاف تشغيل أحد تطبيقات 16 بت واحد داخل VDM تحت Windows NT. ومع ذلك، Windows 16 بت غير جيدة جداً في تنظيف موارد مهمة إنهاء، وليس هو WOWExec قيد التشغيل في VDM. إذا كنت تبحث عن نهج ممكن أنظف لإنهاء أحد تطبيقات 16 بت تحت Windows NT، يجب إنهاء عملية VDM كاملة. ملاحظة: إذا تم تشغيل أحد تطبيقات 16 بت يجوز إنهاء لاحقاً، ثم استخدام CREATE_SEPARATE_WOW_VDM مع CreateProcess().

نموذج لتعليمة برمجية

نموذج التعليمات البرمجية تطبيق التقنيات الموضحة أعلاه لتطبيقات 16 بت و 32 بت باستخدام الدالات التالية اثنين: TerminateApp() و Terminate16App(). يأخذ TerminateApp() "معرف عملية 32-بت" و "مهلة (ب miliseconds). Terminate16App(). استخدم دالتين الربط الصريح لدالات DLL حيث أن تكون متوافقة مع ثنائي عبر نظام التشغيل Windows 95 و Windows NT.

   //******************   //Header
//******************

#include <windows.h>

#define TA_FAILED 0
#define TA_SUCCESS_CLEAN 1
#define TA_SUCCESS_KILL 2
#define TA_SUCCESS_16 3

DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout ) ;
DWORD WINAPI Terminate16App( DWORD dwPID, DWORD dwThread,
WORD w16Task, DWORD dwTimeout );

//******************
//Source
//******************

#include "TermApp.h"
#include <vdmdbg.h>

typedef struct
{
DWORD dwID ;
DWORD dwThread ;
} TERMINFO ;

// Declare Callback Enum Functions.
BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam ) ;

BOOL CALLBACK Terminate16AppEnum( HWND hwnd, LPARAM lParam ) ;

/*----------------------------------------------------------------
DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout )

Purpose:
Shut down a 32-Bit Process (or 16-bit process under Windows 95)

Parameters:
dwPID
Process ID of the process to shut down.

dwTimeout
Wait time in milliseconds before shutting down the process.

Return Value:
TA_FAILED - If the shutdown failed.
TA_SUCCESS_CLEAN - If the process was shutdown using WM_CLOSE.
TA_SUCCESS_KILL - if the process was shut down with
TerminateProcess().
NOTE: See header for these defines.
----------------------------------------------------------------*/
DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout )
{
HANDLE hProc ;
DWORD dwRet ;

// If we can't open the process with PROCESS_TERMINATE rights,
// then we give up immediately.
hProc = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE,
dwPID);

if(hProc == NULL)
{
return TA_FAILED ;
}

// TerminateAppEnum() posts WM_CLOSE to all windows whose PID
// matches your process's.
EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM) dwPID) ;

// Wait on the handle. If it signals, great. If it times out,
// then you kill it.
if(WaitForSingleObject(hProc, dwTimeout)!=WAIT_OBJECT_0)
dwRet=(TerminateProcess(hProc,0)?TA_SUCCESS_KILL:TA_FAILED);
else
dwRet = TA_SUCCESS_CLEAN ;

CloseHandle(hProc) ;

return dwRet ;
}

/*----------------------------------------------------------------
DWORD WINAPI Terminate16App( DWORD dwPID, DWORD dwThread,
WORD w16Task, DWORD dwTimeout )

Purpose:
Shut down a Win16 APP.

Parameters:
dwPID
Process ID of the NTVDM in which the 16-bit application is
running.

dwThread
Thread ID of the thread of execution for the 16-bit
application.

w16Task
16-bit task handle for the application.

dwTimeout
Wait time in milliseconds before shutting down the task.

Return Value:
If successful, returns TA_SUCCESS_16
If unsuccessful, returns TA_FAILED.
NOTE: These values are defined in the header for this
function.

NOTE:
You can get the Win16 task and thread ID through the
VDMEnumTaskWOW() or the VDMEnumTaskWOWEx() functions.
----------------------------------------------------------------*/
DWORD WINAPI Terminate16App( DWORD dwPID, DWORD dwThread,
WORD w16Task, DWORD dwTimeout )
{
HINSTANCE hInstLib ;
TERMINFO info ;

// You will be calling the functions through explicit linking
// so that this code will be binary compatible across
// Win32 platforms.
BOOL (WINAPI *lpfVDMTerminateTaskWOW)(DWORD dwProcessId,
WORD htask) ;

hInstLib = LoadLibraryA( "VDMDBG.DLL" ) ;
if( hInstLib == NULL )
return TA_FAILED ;

// Get procedure addresses.
lpfVDMTerminateTaskWOW = (BOOL (WINAPI *)(DWORD, WORD ))
GetProcAddress( hInstLib, "VDMTerminateTaskWOW" ) ;

if( lpfVDMTerminateTaskWOW == NULL )
{
FreeLibrary( hInstLib ) ;
return TA_FAILED ;
}

// Post a WM_CLOSE to all windows that match the ID and the
// thread.
info.dwID = dwPID ;
info.dwThread = dwThread ;
EnumWindows((WNDENUMPROC)Terminate16AppEnum, (LPARAM) &info) ;

// Wait.
Sleep( dwTimeout ) ;

// Then terminate.
lpfVDMTerminateTaskWOW(dwPID, w16Task) ;

FreeLibrary( hInstLib ) ;
return TA_SUCCESS_16 ;
}

BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam )
{
DWORD dwID ;

GetWindowThreadProcessId(hwnd, &dwID) ;

if(dwID == (DWORD)lParam)
{
PostMessage(hwnd, WM_CLOSE, 0, 0) ;
}

return TRUE ;
}

BOOL CALLBACK Terminate16AppEnum( HWND hwnd, LPARAM lParam )
{
DWORD dwID ;
DWORD dwThread ;
TERMINFO *termInfo ;

termInfo = (TERMINFO *)lParam ;

dwThread = GetWindowThreadProcessId(hwnd, &dwID) ;

if(dwID == termInfo->dwID && termInfo->dwThread == dwThread )
{
PostMessage(hwnd, WM_CLOSE, 0, 0) ;
}

return TRUE ;
}

خصائص

رقم الموضوع: 178893 - آخر مراجعة: 12‏/01‏/2017 - المراجعة: 1

تعليقات