Buenas
¿hay alguna funciona para que no se pueda finalizar un proceso?
No, no es posible, por motivos obvios de seguridad un proceso no puede evitar su terminación incondicional, pero al menos puedes controlar el cierre voluntario por parte del usuario.
Aplicaciones cómo por ejemplo el Administrador de tareas (taskmgr) llaman a la función
TerminateProcess para matar incondicionalmente un proceso, repito, incondicionalmente:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms686714.aspx?f=255&MSPPError=-2147217396Dicha función no "pregunta" al proceso si puede ser cerrado y no necesita el permiso del proceso para cerrarlo, por algo puedes matar procesos que no estén respondiendo.
He estado invesigando pero no se si es correcto:
e.CloseReason = CloseReason.TaskManagerClosing
La propiedad
e.CloseReason que has mostrado se usa en la plataforma .Net, ¿debemos suponer que estás intentando hacerlo en VB.Net o C#?, especifica que lenguaje utilizas al menos.
CloseReason.TaskManagerClosing es simplemente un valor de una enumeración para determinar el motivo del cierre, nada más:
https://msdn.microsoft.com/en-us/library/system.windows.forms.closereason.aspx?f=255&MSPPError=-2147217396La ventana recibe el mensaje de windows
WM_CLOSE (0x10) para solictar la terminación de la aplicación:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms632617%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396Te muestro un ejemplo escrito en Vb.Net de cómo puedes interceptar el mensaje recibido y cancelarlo:
''' <summary>
''' Specifies A Windows Message Identifier.
''' </summary>
Public Enum WindowsMessages As Integer
WM_CLOSE = &H10
End Enum
''' <summary>
''' Invokes the default window procedure associated with this window to process messages.
''' </summary>
''' <param name="m">
''' A <see cref="T:System.Windows.Forms.Message"/> that is associated with the current Windows message.
''' </param>
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WindowsMessages.WM_CLOSE Then
' Do Nothing
Else
MyBase.WndProc(m)
End If
End Sub
Pero eso solo sirve cuando "el usuario cierra la aplicación",
el evento de cierre no se puede cancelar una vez que la terminación incondicional del proceso ha sido enviada,
puedes comprobarlo tu mismo con el código de arriba y también con el siguiente código, verás que el código del interior del bloque de la condicional jamás se llegará a procesar (ni el evento a cancelar, claro está) si matas la aplicación desde el administrador de tareas puesto que la ejecución termina de forma inminente:
''' <summary>
''' Handles the FormClosing event of the Form1 control.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The <see cref="FormClosingEventArgs"/> instance containing the event data.</param>
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs) _
Handles Me.FormClosing
If e.CloseReason <> CloseReason.UserClosing Then
e.Cancel = True
IO.
File.
Create("C:\Test.txt").
Dispose() End If
End Sub
Si alguien me explica estaría muy agradecido.
Hay dos soluciones viables:
· Mantener dos procesos distintos en ejecución, el segundo proceso podría ser un servicio de windows que monitorizase el estado del primer proceso para detectar el cierre y volver a ejecutar una nueva instancia del proceso
después de haber sido cerrado. Obviamente esto no evita que ambos procesos puedan ser matados incondicionalmente.
· Hookear la API
TerminateProcess y opcionalmente
ExitProcess para interceptar las llamadas a dicha función, y cancelarlas. Aparte de estar controlando adicionalmente el cierre voluntario en tu aplicación.
Te muestro un ejemplo de API hooking escrito en VB.Net, utilizo la librería Deviare (también puedes utilizar Microsoft Detours en C++), el hook se adjunta al proceso activo
taskmgr.exe para evitar que éste pueda terminar ningún proceso, interceptando las llamadas que dicho proceso hace a la función
TerminateProcess para cancelarlo (en la pre-llamada):
Imports Nektra.Deviare2
Public NotInheritable Class Form1
Public WithEvents SpyMgr As NktSpyMgr
Public Hook As NktHook
' TerminateProcess API reference:
' https://msdn.microsoft.com/es-es/library/windows/desktop/ms686714%28v=vs.85%29.aspx
ReadOnly libName As String = "kernel32.dll"
ReadOnly funcName As String = "TerminateProcess"
ReadOnly hookFlags As eNktHookFlags = eNktHookFlags.flgOnlyPreCall ' Or eNktHookFlags.flgAutoHookChildProcess
' Processes to attach the hook.
ReadOnly processesToAttach As IEnumerable(Of Process) =
Process.GetProcessesByName("taskmgr")
Private Sub Test() Handles MyBase.Load
If Me.processesToAttach.Count = 0 Then
MsgBox("No se encontró ningún proceso al que adjuntar.")
Else
Me.SpyMgr = New NktSpyMgr()
Me.SpyMgr.Initialize()
Me.Hook = SpyMgr.CreateHook(String.Format("{0}!{1}", libName, funcName), hookFlags)
Me.Hook.Hook(sync:=True)
For Each proc As Process In processesToAttach
Debug.
WriteLine("Attaching to: " & proc.
ProcessName) Me.Hook.Attach(procOrId:=proc.Id, sync:=True)
Next proc
End If
End Sub
<MTAThread>
Private Sub OnTerminateProcess_Called(ByVal hook As NktHook,
ByVal proc As NktProcess,
ByVal callInfo As NktHookCallInfo) Handles SpyMgr.OnFunctionCalled
' Signature params.
Dim hProcessParam As NktParam = DirectCast(callInfo.Params(0), NktParam)
Dim uExitCodeParam As NktParam = DirectCast(callInfo.Params(1), NktParam)
Dim hProcessValue As IntPtr = New IntPtr(CInt(hProcessParam.Value))
Dim uExitCodeValue As UInteger = CUInt(uExitCodeParam.Value)
Trace.WriteLine(String.Format("hProcess : '{0}'", hProcessValue))
Trace.WriteLine(String.Format("uExitCode: '{0}'", uExitCodeValue))
If callInfo.IsPreCall Then ' Skip precall. Avoid process termination.
callInfo.Result.Value = 1
callInfo.SkipCall()
End If
End Sub
End Class
PD: El primer parámetro de
TerminateProcess especifica el handle del proceso que se va a matar, con eso puedes determinar que proceso es, pero lee bien la documentación de MSDN que te he dejado arriba por que me parece que es más complicado que una simple verificación de handles... quizás puedas necesitar la siguiente enumeración para la verificación:
Public Enum ProcessAccessFlags As UInteger
All = &H1F0FFF
Terminate = &H1
CreateThread = &H2
VirtualMemoryOperation = &H8
VirtualMemoryRead = &H10
VirtualMemoryWrite = &H20
DuplicateHandle = &H40
CreateProcess = &H80
SetQuota = &H100
SetInformation = &H200
QueryInformation = &H400
QueryLimitedInformation = &H1000
Synchronize = &H100000
End Enum
Saludos