code.pablomurad.com

fast-scripts

28 double-click Windows batch tools for video, audio and files

general fix
author
Pablo Murad
date
commit
b4ee5936e4bad15c700c9b3a9c090d27b29c12d6
parent
d6673f39b7a6c93b749aa800bbef82d5fc2362c1
changes
17 files +554 −27
@@ -10,7 +10,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Comprimir PDF' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Comprimir PDF' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -158,7 +189,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
@@ -10,7 +10,38 @@ #  Arquivo único .cmd (auto-executável como PowerShell) # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Converter Imagens' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Converter Imagens' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -158,7 +189,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar {
@@ -10,7 +10,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Imagens e PDF' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Imagens e PDF' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -158,7 +189,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
@@ -10,7 +10,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = "Marca d'água" } catch {}
+try { $Host.UI.RawUI.WindowTitle = "Marca d'água" } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -158,7 +189,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
@@ -10,7 +10,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Compactar / Extrair' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Compactar / Extrair' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
 function Titulo($t) {
     Write-Host ''
@@ -10,7 +10,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Limpar Metadados' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Limpar Metadados' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -158,7 +189,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
@@ -11,7 +11,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Renomear em lote' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Renomear em lote' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -159,7 +190,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
@@ -11,7 +11,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Baixar Vídeos' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Baixar Vídeos' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
 function Titulo($t) {
@@ -10,7 +10,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Criar GIF / Slideshow' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Criar GIF / Slideshow' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -158,7 +189,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
@@ -10,7 +10,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Extrair Áudio' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Extrair Áudio' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -158,7 +189,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
@@ -10,7 +10,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Info de Mídia' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Info de Mídia' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -158,7 +189,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
@@ -10,7 +10,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Marca d''água em Vídeo' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Marca d''água em Vídeo' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -158,7 +189,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
@@ -10,7 +10,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Normalizar Áudio' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Normalizar Áudio' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -158,7 +189,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
@@ -10,7 +10,38 @@ #  Arquivo único .cmd (auto-executável como PowerShell) # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Transcodificar Vídeos' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Transcodificar Vídeos' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 
 # ============================================================
 #  Executar-EmParalelo - pool de processos externos (PS 5.1)
@@ -158,7 +189,7 @@ try { $a.Proc.Dispose() } catch {}         }
     }
     if (-not $SemProgresso) { Write-Host '' }
-    return $res
+    return ,$res
 }
 
 function Pausar {
@@ -29,6 +29,37 @@ }     exit
 }
 
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
+
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para voltar ao menu...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
 function Titulo($t) {
     Write-Host ''
@@ -29,6 +29,37 @@ }     exit
 }
 
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
+
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para voltar ao menu...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
 function Titulo($t) {
     Write-Host ''
@@ -10,7 +10,38 @@ #  Desenvolvido por Pablo Murad - 2026 # ============================================================
 
 try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
-try { $Host.UI.RawUI.WindowTitle = 'Backup Espelhado' } catch {}
+try { $Host.UI.RawUI.WindowTitle = 'Backup Espelhado' } catch {}

+
+# ------------------------------------------------------------
+# Blindagem de fechamento: amarra este processo a um Job do Windows
+# com KILL_ON_JOB_CLOSE. Se a janela for fechada no X (ou o processo
+# morrer de qualquer jeito), o Windows mata junto todo processo-filho
+# (ffmpeg, yt-dlp, etc.) — nada fica rodando orfao. O 'finally' do
+# pool so' cobre Ctrl+C e erros; o X escapa dele, e este Job cobre o X.
+# ------------------------------------------------------------
+try {
+    Add-Type -Namespace FS -Name Job -MemberDefinition @'
+[DllImport("kernel32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr CreateJobObject(IntPtr a, string n);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetInformationJobObject(IntPtr j, int c, IntPtr i, uint s);
+[DllImport("kernel32.dll", SetLastError=true)] public static extern bool AssignProcessToJobObject(IntPtr j, IntPtr p);
+[DllImport("kernel32.dll")] public static extern IntPtr GetCurrentProcess();
+'@ -ErrorAction Stop
+    if ([IntPtr]::Size -eq 8) {
+        $__job = [FS.Job]::CreateJobObject([IntPtr]::Zero, $null)
+        if ($__job -ne [IntPtr]::Zero) {
+            $__sz = 144   # JOBOBJECT_EXTENDED_LIMIT_INFORMATION em 64 bits
+            $__pi = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($__sz)
+            for ($__i = 0; $__i -lt $__sz; $__i++) { [System.Runtime.InteropServices.Marshal]::WriteByte($__pi, $__i, 0) }
+            [System.Runtime.InteropServices.Marshal]::WriteInt32($__pi, 16, 0x2000)  # KILL_ON_JOB_CLOSE
+            [void][FS.Job]::SetInformationJobObject($__job, 9, $__pi, $__sz)
+            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($__pi)
+            # O handle fica aberto DE PROPOSITO ate o processo morrer: e' o
+            # fechamento dele que dispara a matanca dos filhos. $__job global
+            # impede o coletor de lixo de fechar cedo.
+            [void][FS.Job]::AssignProcessToJobObject($__job, [FS.Job]::GetCurrentProcess())
+        }
+    }
+} catch { Write-Host "[i] Blindagem de fechamento indisponivel: $($_.Exception.Message)" -ForegroundColor DarkGray }
 function Pausar { Write-Host ''; Write-Host 'Pressione Enter para fechar...' -ForegroundColor DarkGray; [void][System.Console]::ReadLine() }
 function Titulo($t) {
     Write-Host ''