Bir alttaki "C# Memory Searcher" ile alakalı bir projede bir processi suspend(askıya almak) ve resume(çözmek falan işte) etmem gerekiyordu. Bu konuda bulabildiğim uygun tek örnek de c++ için konsolda çalışan bir programdı. Aslında bir tane de Hindu bir amcanın c# ile mobil sistemler için yazdığı vardı ama hatalı olduğu gibi gereksizce işimi uzatmaktan başka bir işe yaramadı o da.
Neyse yavaştan başlayalım.
Öncelikle bir programı suspend etmek demek günümüzün multithreading yapısında o processin threadlerini suspend etmek demektir. Başka bir programın threadlerine ulaşıp onlarla oynamak gibi low level bir olayı da c# bize doğrudan izin vermiyor. Hatta Process sınıfında threads bölümü bile non-public. Neyse konuya dönelim. Bunun için bir kaç adet Win32 API kullanıyoruz.
1. CreateToolhelp32Snapshot ile bellekteki çalışan programların snapshotını alıyoruz
2. Thread32first ile ilk thread hakkında bilgilere uaşıyoruz
3. Thread32next ile bir sonraki threade geçiyoruz
4. SuspendThread ile threadi donduruyoruz
5. ResumeThread ile threadi çözüyoruz.
Demin yanlışlıkla sayfayı geri aldım ve son yazdıklarımın büyük çoğunluğu uçtu gitti. Ayıp oldu. Kızdım ben de o güzel açıklamalardan mahrum kaldınız :P:P
[DllImport("kernel32.dll", EntryPoint = "CreateToolhelp32Snapshot", SetLastError = true)]
public static extern IntPtr CreateToolhelp32SnapshotAPI(ulong flags, uint processid);
[DllImport("kernel32.dll", EntryPoint = "Thread32First", SetLastError = true)]
public static extern bool Thread32First(IntPtr handle, ref Patcher.THREADENTRY32 te32);
[DllImport("kernel32.dll", EntryPoint = "Thread32Next", SetLastError = true)]
public static extern bool Thread32Next(IntPtr handle, ref Patcher.THREADENTRY32 te32);
[DllImport("kernel32.dll", EntryPoint = "OpenThread", SetLastError = true)]
public static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll", EntryPoint = "SuspendThread", SetLastError = true)]
public static extern uint SuspendThread(IntPtr handle);
[DllImport("kernel32.dll", EntryPoint = "ResumeThread", SetLastError = true)]
public static extern uint ResumeThread(IntPtr handle);
public struct THREADENTRY32
{
public uint dwSize;
public uint cntUsage;
public uint th32ThreadID;
public uint th32OwnerProcessID;
public int tpBasePri;
public int tpDeltaPri;
public uint dwFlags;
}
public void Suspend(Process ProName)
{
try
{
thandle = Readerapi.CreateToolhelp32SnapshotAPI(TH32CS_SNAPTHREAD, (uint)ProName.Id);
}
catch (Exception hata)
{
System.Windows.Forms.MessageBox.Show("1");
System.Windows.Forms.MessageBox.Show(hata.ToString());
}
THREADENTRY32 te32 = new THREADENTRY32();
ulong size = (ulong)System.Runtime.InteropServices.Marshal.SizeOf(te32);
te32.dwSize = (uint)size;
bool devammi = true;
Stack depo = new Stack();
threadlist = new IntPtr[ProName.Threads.Count];
if (Readerapi.Thread32First(thandle, ref te32))
{
while (devammi)
{
if (te32.th32OwnerProcessID == ProName.Id)
{
IntPtr srhandle = Readerapi.OpenThread(2, false, te32.th32ThreadID);
Readerapi.SuspendThread(srhandle);
threadlist[counter] = srhandle;
counter++;
}
te32.dwSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(THREADENTRY32));
devammi = Readerapi.Thread32Next(thandle, ref te32);
}
}
Readerapi.CloseHandle(thandle);
counter = 0;
}
public void Resume(Process ProName)
{
try
{
thandle = Readerapi.CreateToolhelp32SnapshotAPI(TH32CS_SNAPTHREAD, (uint)ProName.Id);
}
catch (Exception hata)
{
System.Windows.Forms.MessageBox.Show("1");
System.Windows.Forms.MessageBox.Show(hata.ToString());
}
THREADENTRY32 te32 = new THREADENTRY32();
ulong size = (ulong)System.Runtime.InteropServices.Marshal.SizeOf(te32);
te32.dwSize = (uint)size;
bool devammi = true;
Stack depo = new Stack();
counter = 0;
if (Readerapi.Thread32First(thandle, ref te32))
{
while (devammi)
{
if (te32.th32OwnerProcessID == ProName.Id)
{
// IntPtr srhandle = Readerapi.OpenThread(2, false, te32.th32ThreadID);
// IntPtr rhandle = depo.Pop();
Readerapi.ResumeThread(threadlist[counter]);
counter++;
}
te32.dwSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(THREADENTRY32));
devammi = Readerapi.Thread32Next(thandle, ref te32);
}
}
Readerapi.CloseHandle(thandle);
counter = 0;
}
Aslında açıklanacak bir kaç şey var;
- thread32first illa ki boyutun önceden belirtilmesini ister
- CreateToolhelp32Snapshot açıklamaları için www.pinvoke.com ya da www.msdn.com'a bakabilirsiniz.
- Hani biliyorsunuz ama ben yine de söyleyeyim. Böyle API eklemek için projenin referanslarında "System.Runtime.InteropServices" sınıfı da bulunmalıdır.
- Dangıl dungul tanımlanmış böyle bakınca bir şey anlaşılmayan nesneler projede anlamlıdır. Müsterih olunuz hiç birini yersiz yurtsuz koymadık.
- Kodlar daha doğrusu bu yolla bir processi suspend/resume etmek çok da güvenli değil. Birbirine bağlı ve düzensiz sıralanmış threadlerin yanlış suspend edilmesi programın kilitlenmesine yol açabilir. Büyük ihtimalle de kaydedilmemiş verileriniz kaybolacaktır. Hihohaha bu son cümleyi hep kullanmak istemiştim. Öylesine mutluyum ki şu an.
- Windows'larda kullanılan API'ler kernel32.dll'de bulunmaktadır ama Win CE falan mobil işlerde toolhelp mi toolhelp32 mi öyle bir kütüphanededir kendileri. THREADENTRY32 yapısı da daha fazla değer alıyordu.
- C++'da mis gibi headeri varmış THREADENTRY32'nin. C#'ta yok öyle bir şey. Orjinalinde Dword olan zamazingoların c# karşılıklarını ayarlamak çok uğraştırdı.
- Suspend/Resume konusunda nette C# için bir makale bulunmuyor. Aslında bir gün üşenmeyip İngilizce güzel bişiler yazsam bu konuda(böyle dandirik değil elbette. hıh!) iyi olacak.