Service Program, Can't detect notebook lid status - c#

I wrote a little program to close my notebook screen when the lid closes and turn the screen on when the lid opens.
I tried a lot, but there is still something wrong in my code. The function "OnLidPowerEvent" doesn't work.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Runtime.InteropServices;
namespace DeviceWatcher
{
public partial class Service1 : ServiceBase
{
[DllImport("User.dll", CharSet = CharSet.Unicode)]
public static extern int SendMessage(int hWnd,int Msg,int wParam,int lParam);
public const int HWND_BROADCAST = 0xffff;
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_MONITORPOWER = 0xf170;
// Broadcast Msg to close screen
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
public static extern int RegisterPowerSettingNotification(IntPtr Recipient, Guid guid, int flags);
static Guid GUID_LIDSWITCH_STATE_CHANGE = new Guid(0xBA3E0F4D, 0xB817, 0x4094, 0xA2, 0xD1, 0xD5, 0x63, 0x79, 0xE6, 0xA0, 0xF3);
public const int DEVICE_NOTIFY_SERVICE_HANDLE = 1;
// using DEVICE_NOTIFY_SERVICE_HANDLE as flag
public delegate int callbackEx(int control, int eventType, IntPtr eventData, IntPtr context);
callbackEx hamster = OnLidPowerEvent;
[DllImport("Advapi32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr RegisterServiceCtrlHandlerEx(string lpServiceName, callbackEx lpHandlerProc, IntPtr lpContext);
internal struct POWERBROADCAST_SETTING
{
public Guid PowerSetting;
public uint DataLength;
public byte Data;
}
private static POWERBROADCAST_SETTING pos = new POWERBROADCAST_SETTING();
public Service1()
{
InitializeComponent();
//this.CanHandlePowerEvent = true;
// C# Service can not deal GUID_LIDSWITCH_STATE_CHANGE
this.ServiceName = "DeviceWatcher";
IntPtr serviceControlHandle = RegisterServiceCtrlHandlerEx("DeviceWatcher", hamster, IntPtr.Zero);
RegisterPowerSettingNotification(serviceControlHandle,GUID_LIDSWITCH_STATE_CHANGE,DEVICE_NOTIFY_SERVICE_HANDLE);
// should I using this.serviceHandle or this.ServiceHandle
using (System.IO.StreamWriter sw = new System.IO.StreamWriter("C:\\log.txt", true))
{
sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "Service Init.");
}
}
private static int OnLidPowerEvent(int control, int eventType, IntPtr eventData, IntPtr context)
{
using (System.IO.StreamWriter sw = new System.IO.StreamWriter("C:\\log.txt", true))
{
sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "PowerFunc Called.");
}
// never reach this Function when my Notebook Lid Closes. :-(
// below need some check on pos data
pos = (POWERBROADCAST_SETTING)Marshal.PtrToStructure(eventData, typeof(POWERBROADCAST_SETTING));
if (pos.PowerSetting == GUID_LIDSWITCH_STATE_CHANGE)
{
if (pos.Data == 0)
{
SendMessage(HWND_BROADCAST,WM_SYSCOMMAND,SC_MONITORPOWER,2);
// when lid Closes,turn screen off
}
else if (pos.Data == 1)
{
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, -1);
// when lid opens ,turn screen on
}
}
return 0;
}
protected override void OnStart(string[] args)
{
using (System.IO.StreamWriter sw = new System.IO.StreamWriter("C:\\log.txt", true))
{
sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "Service Start Func.");
}
}
protected override void OnStop()
{
using (System.IO.StreamWriter sw = new System.IO.StreamWriter("C:\\log.txt", true))
{
sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "Service Stop Func.");
}
}
}
}

The answer is simple:
1st : Windows Service can't send message,using a common application will working fine.
2nd : C# service maybe somehow different from C/C++ service,by using RegisterPowerSettingNotification() will never make it work.
After I find the reasons,I wrote a pure C and Winapi program.
It works perfect.
Thanks a lot for those people concerned about this question!

Related

How to invoke the Windows Permissions dialog for NamedPipes?

I am trying to open a Windows Permissions dialog for Named Pipes.
I saw that Process Hacker does it like that on \Device\NamedPipe\InitShutdown:
I used this answer on named pipe but it only works for file paths.
The code (see down below), failed when running:
hr = SHILCreateFromPath(Path.GetDirectoryName(path), out folderPidl, IntPtr.Zero);
The function throws an exception:
The network path was not found
I supposed I need to find a way to generate folderPidl (PIDL) from a named pipe but I am not sure how to do it.
Notice that I can get the named pipe permissions through the NtObjectManager library so this is not the problem, I am searching for a way to do it with the Windows permissions dialog.
PS C:\Users\eviatar> $a=Get-NtFile("\Device\NamedPipe\InitShutdown")
PS C:\Users\eviatar> $a
Handle Name NtTypeName Inherit ProtectFromClose
------ ---- ---------- ------- ----------------
3276 InitShutdown File False False
PS C:\Users\eviatar> $a.SecurityDescriptor.Dacl
Type User Flags Mask
---- ---- ----- ----
Allowed Everyone None 0012019B
Allowed NT AUTHORITY\ANONYMOUS LOGON None 0012019B
Allowed BUILTIN\Administrators None 001F01FF
The code:
// Program.cs
string path = #"\\.\pipe\InitShutdown";
//path = #"\Device\NamedPipe\InitShutdown";
//path = #"C:\tmp";
PermissionDialog.Show(IntPtr.Zero, path);
----------------------
// PermissionDialog.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace NamePipeViewer
{
// https://stackoverflow.com/questions/28035464/how-does-one-invoke-the-windows-permissions-dialog-programmatically
public static class PermissionDialog
{
public static bool Show(IntPtr hwndParent, string path)
{
if (path == null)
throw new ArgumentNullException("path");
SafePidlHandle folderPidl;
int hr;
hr = SHILCreateFromPath(Path.GetDirectoryName(path), out folderPidl, IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
SafePidlHandle filePidl;
hr = SHILCreateFromPath(path, out filePidl, IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
IntPtr file = ILFindLastID(filePidl);
System.Runtime.InteropServices.ComTypes.IDataObject ido;
hr = SHCreateDataObject(folderPidl, 1, new IntPtr[] { file }, null, typeof(System.Runtime.InteropServices.ComTypes.IDataObject).GUID, out ido);
if (hr != 0)
throw new Win32Exception(hr);
// if you get a 'no such interface' error here, make sure the running thread is STA
IShellExtInit sei = (IShellExtInit)new SecPropSheetExt();
sei.Initialize(IntPtr.Zero, ido, IntPtr.Zero);
IShellPropSheetExt spse = (IShellPropSheetExt)sei;
IntPtr securityPage = IntPtr.Zero;
spse.AddPages((p, lp) =>
{
securityPage = p;
return true;
}, IntPtr.Zero);
PROPSHEETHEADER psh = new PROPSHEETHEADER();
psh.dwSize = Marshal.SizeOf(psh);
psh.hwndParent = hwndParent;
psh.nPages = 1;
psh.phpage = Marshal.AllocHGlobal(IntPtr.Size);
Marshal.WriteIntPtr(psh.phpage, securityPage);
// TODO: adjust title & icon here, also check out the available flags
psh.pszCaption = "Permissions for '" + path + "'";
IntPtr res;
try
{
res = PropertySheet(ref psh);
}
finally
{
Marshal.FreeHGlobal(psh.phpage);
}
return res == IntPtr.Zero;
}
private class SafePidlHandle : SafeHandle
{
public SafePidlHandle()
: base(IntPtr.Zero, true)
{
}
public override bool IsInvalid
{
get { return handle == IntPtr.Zero; }
}
protected override bool ReleaseHandle()
{
if (IsInvalid)
return false;
Marshal.FreeCoTaskMem(handle);
return true;
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct PROPSHEETHEADER
{
public int dwSize;
public int dwFlags;
public IntPtr hwndParent;
public IntPtr hInstance;
public IntPtr hIcon;
public string pszCaption;
public int nPages;
public IntPtr nStartPage;
public IntPtr phpage;
public IntPtr pfnCallback;
}
[DllImport("shell32.dll")]
private static extern IntPtr ILFindLastID(SafePidlHandle pidl);
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern int SHILCreateFromPath(string pszPath, out SafePidlHandle ppidl, IntPtr rgflnOut);
[DllImport("shell32.dll")]
private static extern int SHCreateDataObject(SafePidlHandle pidlFolder, int cidl, IntPtr[] apidl, System.Runtime.InteropServices.ComTypes.IDataObject pdtInner, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out System.Runtime.InteropServices.ComTypes.IDataObject ppv);
[DllImport("comctl32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr PropertySheet(ref PROPSHEETHEADER lppsph);
private delegate bool AddPropSheetPage(IntPtr page, IntPtr lParam);
[ComImport]
[Guid("1f2e5c40-9550-11ce-99d2-00aa006e086c")] // this GUID points to the property sheet handler for permissions
private class SecPropSheetExt
{
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214E8-0000-0000-C000-000000000046")]
private interface IShellExtInit
{
void Initialize(IntPtr pidlFolder, System.Runtime.InteropServices.ComTypes.IDataObject pdtobj, IntPtr hkeyProgID);
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214E9-0000-0000-C000-000000000046")]
private interface IShellPropSheetExt
{
void AddPages([MarshalAs(UnmanagedType.FunctionPtr)] AddPropSheetPage pfnAddPage, IntPtr lParam);
void ReplacePage(); // not fully defined, we don't use it
}
}
}

How can I move a second opened browser window?

I'm trying to make a program that opens a browser on each multi-display.
When I did it with a notepad, it worked. However, when it's a browser didn't work and showed the error "System.InvalidOperationException: Process must exit before requested information can be determined". I will appreciate your help with this situation.
This is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern int MoveWindow(IntPtr hwnd, int x, int y,
int nWidth, int nHeight, int bRepaint);
private void Form1_Load(object sender, EventArgs e)
{
foreach (Screen item in Screen.AllScreens)
{
//Open a web browser
System.Diagnostics.Process process1 = System.Diagnostics.Process.Start("C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe");
//System.Diagnostics.Process process2 = System.Diagnostics.Process.Start("notepad.exe");
process1.WaitForInputIdle();
//process2.WaitForInputIdle();
//Move the browser window
MoveWindow(process1.MainWindowHandle, item.Bounds.X, item.Bounds.Y, item.Bounds.Width, item.Bounds.Height, 1);
//MoveWindow(process2.MainWindowHandle, item.Bounds.X, item.Bounds.Y, item.Bounds.Width, item.Bounds.Height, 1);
}
}
}
}
It seems that msedge.exe use a host process to start different tabs, so we can't use the created process ID to handle it.
Compare created process ID with processes in the Task Manager, the
process 38756 is missing.
Another tool to browse edge relative processes is the built-in task manager of edge.
We can't find process 38756 as well.
So re-search the target edge process is necessary.
This repo https://github.com/alex-tomin/Tomin.Tools.KioskMode demostrate how to move chrome window into different monitors and set as full screen.
I tried to extract the necessary and modify your code like below, hope it helps:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TestWinFormsApp
{
// Get full definition of SetWindowPosFlags here:
// https://www.pinvoke.net/default.aspx/Enums/SetWindowPosFlags.html
[Flags]
public enum SetWindowPosFlags : uint
{
SWP_NOREDRAW = 0x0008,
SWP_NOZORDER = 0x0004
}
// Get full definition of ShowWindowCommands here:
// https://www.pinvoke.net/default.aspx/Enums/ShowWindowCommand.html
public enum ShowWindowCommands
{
Maximize = 3,
Restore = 9,
}
public static class WinApi
{
[DllImport("user32.dll", SetLastError = true)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load_1(object sender, EventArgs e)
{
var ignoreHandles = new List<IntPtr>();
foreach (Screen item in Screen.AllScreens)
{
this.StartEdgeProcess();
var windowHandle = this.GetWindowHandle("msedge", ignoreHandles);
ignoreHandles.Add(windowHandle);
WinApi.ShowWindow(windowHandle, ShowWindowCommands.Restore);
WinApi.SetWindowPos(windowHandle, IntPtr.Zero, item.Bounds.Left, item.Bounds.Top, 800, 600, SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOREDRAW);
WinApi.ShowWindow(windowHandle, ShowWindowCommands.Maximize);
}
}
private void StartEdgeProcess()
{
var process = new Process();
process.StartInfo.FileName = "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe";
process.StartInfo.Arguments = "about:blank" + " --new-window -inprivate ";
process.Start();
process.WaitForInputIdle();
}
private IntPtr GetWindowHandle(string processName, List<IntPtr> ignoreHandlers)
{
IntPtr? windowHandle = null;
while (windowHandle == null)
{
windowHandle = Process.GetProcesses()
.FirstOrDefault(process => process.ProcessName == processName
&& process.MainWindowHandle != IntPtr.Zero
&& !string.IsNullOrWhiteSpace(process.MainWindowTitle)
&& !ignoreHandlers.Contains(process.MainWindowHandle))
?.MainWindowHandle;
}
return windowHandle.Value;
}
}
}

ReadProcessMemory and WriteProcessMemory Gone Wrong

I am creating this memory editor, but the subroutine ReadProcessMemory fails; so does WriteProcessMemory. I have tried to get the last error by using the GetLastError subroutine but it returns 0 which is ERROR_SUCCESS. Here is the code of both the program and the class.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Memedit;
using System.Runtime.InteropServices;
namespace Memory_Editor
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void textBox1_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
{
e.Effect = DragDropEffects.All;
}
}
private void textBox1_DragDrop(object sender, DragEventArgs e)
{
string[] data = e.Data.GetData(DataFormats.FileDrop, false) as string[];
process.Text = data[0];
}
private void button1_Click(object sender, EventArgs e)
{
MemoryEditor editor = new MemoryEditor(process.Text);
int addr;
if (int.TryParse(address1.Text, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out addr))
{
result.Text = editor.ReadString(addr, 1000, isunicode.Checked);
}
else
{
MessageBox.Show("Error: It is not a real number!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void button2_Click(object sender, EventArgs e)
{
MemoryEditor editor = new MemoryEditor(process.Text);
int addr;
if (int.TryParse(address1.Text, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out addr))
{
result.Text = Convert.ToString(editor.ReadInt32(addr), 16);
}
else
{
MessageBox.Show("Error: It is not a real number!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
MemoryEditor Class
using System;
using System.Text;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Memedit
{
public class MemoryEditor
{
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, UIntPtr nSize, out IntPtr lpNumberOfBytesWritten);
[DllImport("Kernel32.dll")]
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, UInt32 nSize, ref UInt32 lpNumberOfBytesRead);
string pname = "";
IntPtr hand;
public MemoryEditor(string ProcName)
{
pname = ProcName.Replace(".exe", "");
Process[] proclist = Process.GetProcesses();
foreach (Process pr in proclist)
{
if (pr.ToString() == "System.Diagnostics.Process (" + pname + ")")
{
hand = pr.Handle;
}
}
}
public bool Write(int Address, byte[] data)
{
bool success = false;
Process[] proclist = Process.GetProcesses();
IntPtr bytesout;
success = WriteProcessMemory(hand, (IntPtr)Address, data, (UIntPtr)data.Length, out bytesout);
return success;
}
public byte[] Read(int Address, int length)
{
byte[] ret = new byte[length];
uint o = 0;
ReadProcessMemory(hand, (IntPtr)Address, ret, (UInt32)ret.Length, ref o);
return ret;
}
public int ReadInt32(int Address)
{
return BitConverter.ToInt32(Read(Address, 4), 0);
}
public float ReadSingle(int Address)
{
return BitConverter.ToSingle(Read(Address, 4), 0);
}
public string ReadString(int Address, int length, bool isUnicode)
{
if (isUnicode)
{
UnicodeEncoding enc = new UnicodeEncoding();
return enc.GetString(Read(Address, length));
}
else
{
ASCIIEncoding enc = new ASCIIEncoding();
return enc.GetString(Read(Address, length));
}
}
}
}
For ReadProcessMemory you need PROCESS_VM_READ permission and for WriteProcessMemory you need PROCESS_VM_OPERATION permission.
see http://msdn.microsoft.com/en-us/library/ms680553%28v=vs.85%29.aspx
and http://msdn.microsoft.com/en-us/library/ms681674%28v=vs.85%29.aspx
Very likely you need Administrator rights, perhaps even SeDebugPrivilege (which you would need to elevate your process to even with Administrator rights)...

Global keylistener

The program is working at background and listening keyboard(like keylogger)
First I select a string on pdf or etc. Then I pressed the ctrl + rbutton the program must be pop up and it can be get my selected string.
For this;
[DllImport("User32.dll")]
private static extern short GetAsyncKeyState(System.Windows.Forms.Keys vKey);
[DllImport("User32.dll")]
private static extern short GetAsyncKeyState(System.Int32 vKey);
string key = "";
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Interval = 5;
foreach (System.Int32 i in Enum.GetValues(typeof(Keys)))
{
int x = GetAsyncKeyState(i);
if ((x == 1) || (x == -32767))
{
keyBuffer += Enum.GetName(typeof(Keys), i);
}
}
if (keyBuffer != "")
{
keyBuffer = keyBuffer.ToLower();
if (keyBuffer.Contains("lcontrolkeyrbutton"))
{
// do somethings
keyBuffer = "";
}
}
}
But after first performing, ctrl + rbutton it doesn't work. What's the wrong? And how can i get the selected string into my program?
Sounds like you want a clipboard hook (in addition to your global key hook). Take a look here: Clipboard event C#
If you want to capture the string without the user copying it into the clipboard manually, you could send ctrl+c to the application yourself: http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.aspx
You need to SendKeyEvent(Ctrl+C) of MainWindowsHandle First, then Using Clipboard capture your text to your Project.
Here is the program funcional and tested
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CtrLetterCopy
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
const int WM_COMMAND = 0x111;
enum KeyModifier
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
WinKey = 8
}
public Form1()
{
InitializeComponent();
this.ShowInTaskbar = false;
int id_Ctrl = 0; // The id of the hotkey.
RegisterHotKey(this.Handle, id_Ctrl, (int)KeyModifier.Control, Keys.R.GetHashCode());
}
protected override void WndProc(ref Message m)
{
//const int WM_HOTKEY = 0x0312;
if (m.Msg == 0x0312)
{
if (m.WParam.ToInt32() == 0)
{
//do what you want here
SendKeyEvent();
}
}
base.WndProc(ref m);
}
private void SendKeyEvent()
{
SendKeys.SendWait("^c");
Thread.Sleep(500);
string test3 = Clipboard.GetText();
MessageBox.Show(test3);
}
}
}
Download and check out this code here:
http://www.codeproject.com/KB/cs/globalhook.aspx
Pre-tested, well-written and fully functional.
Best of luck!

How can I get the PID of the parent process of my application

My winform application is launched by another application (the parent), I need determine the pid of the application which launch my application using C#.
WMI is the easier way to do this in C#. The Win32_Process class has the ParentProcessId property. Here's an example:
using System;
using System.Management; // <=== Add Reference required!!
using System.Diagnostics;
class Program {
public static void Main() {
var myId = Process.GetCurrentProcess().Id;
var query = string.Format("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {0}", myId);
var search = new ManagementObjectSearcher("root\\CIMV2", query);
var results = search.Get().GetEnumerator();
results.MoveNext();
var queryObj = results.Current;
var parentId = (uint)queryObj["ParentProcessId"];
var parent = Process.GetProcessById((int)parentId);
Console.WriteLine("I was started by {0}", parent.ProcessName);
Console.ReadLine();
}
}
Output when run from Visual Studio:
I was started by devenv
Using Brian R. Bondy's answer as a guide, as well as what is on PInvoke.net, and some Reflector output, I have produced this, for use in LinqPad MyExtensions:
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
static class ProcessExtensions
{
public static int ParentProcessId(this Process process)
{
return ParentProcessId(process.Id);
}
public static int ParentProcessId(int Id)
{
PROCESSENTRY32 pe32 = new PROCESSENTRY32{};
pe32.dwSize = (uint)Marshal.SizeOf(typeof(PROCESSENTRY32));
using(var hSnapshot = CreateToolhelp32Snapshot(SnapshotFlags.Process, (uint)Id))
{
if(hSnapshot.IsInvalid)
throw new Win32Exception();
if(!Process32First(hSnapshot, ref pe32))
{
int errno = Marshal.GetLastWin32Error();
if(errno == ERROR_NO_MORE_FILES)
return -1;
throw new Win32Exception(errno);
}
do {
if(pe32.th32ProcessID == (uint)Id)
return (int)pe32.th32ParentProcessID;
} while(Process32Next(hSnapshot, ref pe32));
}
return -1;
}
private const int ERROR_NO_MORE_FILES = 0x12;
[DllImport("kernel32.dll", SetLastError=true)]
private static extern SafeSnapshotHandle CreateToolhelp32Snapshot(SnapshotFlags flags, uint id);
[DllImport("kernel32.dll", SetLastError=true)]
private static extern bool Process32First(SafeSnapshotHandle hSnapshot, ref PROCESSENTRY32 lppe);
[DllImport("kernel32.dll", SetLastError=true)]
private static extern bool Process32Next(SafeSnapshotHandle hSnapshot, ref PROCESSENTRY32 lppe);
[Flags]
private enum SnapshotFlags : uint
{
HeapList = 0x00000001,
Process = 0x00000002,
Thread = 0x00000004,
Module = 0x00000008,
Module32 = 0x00000010,
All = (HeapList | Process | Thread | Module),
Inherit = 0x80000000,
NoHeaps = 0x40000000
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESSENTRY32
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)] public string szExeFile;
};
[SuppressUnmanagedCodeSecurity, HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort=true)]
internal sealed class SafeSnapshotHandle : SafeHandleMinusOneIsInvalid
{
internal SafeSnapshotHandle() : base(true)
{
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
internal SafeSnapshotHandle(IntPtr handle) : base(true)
{
base.SetHandle(handle);
}
protected override bool ReleaseHandle()
{
return CloseHandle(base.handle);
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
private static extern bool CloseHandle(IntPtr handle);
}
}
If you have control over the parent application, you could modify the parent to pass its PID to the child when it launches the process.
Check the th32ParentProcessID member of a CreateToolhelp32Snapshot enumeration.
For an example of how to do this see my post here.
Since you are using C# though you'll need to use DllImports. In the linked post there are MSDN pages for each for the functions you need. At the bottom of each MSDN page they have the code for DllImport for C#.
There is also an easier way using managed code only but it doesn't work if you have the more than one process name started by different applications.

Categories