I'm processing a large number of files, therefore, I don't want to wait until the whole search is finished before the array is returned. So I don't want to use Directory.GetFiles()
According to this answer , I need to use EnumerateFiles() in order to get results during the search process. However, I'm using NET2.0 and this function seems to be introduced starting from NET 4.0
What is the equivalent of EnumerateFiles() in Net 2.0 ?
Any hints would be highly appreciated
What you need are the WinAPI calls for FindFirstFile and FindNextFile.
Here's some code that uses the wrapped api calls.
IEnumerable<string> EnumerateFiles(string path)
{
APIWrapper.FindData findData = new APIWrapper.FindData();
APIWrapper.SafeFindHandle handle = APIWrapper.SafeNativeMethods.FindFirstFile(System.IO.Path.Combine(path, "*"), findData);
if(!handle.IsInvalid && !handle.IsClosed)
{
yield return findData.fileName;
while(!APIWrapper.SafeNativeMethods.FindNextFile(handle, findData))
yield return findData.fileName;
handle.Close();
}
}
I just hand typed EnumerateFiles so treat it as pseudo code, but the class it relies on is production ready, this is it here
internal class APIWrapper
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal sealed class FILETIME
{
public int Low;
public int High;
public Int64 ToInt64()
{
Int64 h = High;
h = h << 32;
return h + Low;
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal sealed class FindData
{
public int fileAttributes;
public FILETIME CreationTime;
public FILETIME LastAccessTime;
public FILETIME LastWriteTime;
public int FileSizeHigh;
public int FileSizeLow;
public int dwReserved0;
public int dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public String fileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public String alternateFileName;
}
internal sealed class SafeFindHandle : Microsoft.Win32.SafeHandles.SafeHandleMinusOneIsInvalid
{
/// <summary>
/// Constructor
/// </summary>
public SafeFindHandle()
: base(true)
{
}
/// <summary>
/// Release the find handle
/// </summary>
/// <returns>true if the handle was released</returns>
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
protected override bool ReleaseHandle()
{
return SafeNativeMethods.FindClose(handle);
}
}
internal enum SearchOptions
{
NameMatch,
LimitToDirectories,
LimitToDevices
}
[SecurityPermissionAttribute(SecurityAction.Assert, UnmanagedCode = true)]
internal static class SafeNativeMethods
{
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern SafeFindHandle FindFirstFile(String fileName, [In, Out] FindData findFileData);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern SafeFindHandle FindFirstFileEx(
String fileName, //__in LPCTSTR lpFileName,
[In] int infoLevel, //__in FINDEX_INFO_LEVELS fInfoLevelId,
[In, Out] FindData findFileData, //__out LPVOID lpFindFileData,
[In, Out] SearchOptions SerchOps, //__in FINDEX_SEARCH_OPS fSearchOp,
[In] int SearchFilter, //__reserved LPVOID lpSearchFilter,
[In] int AdditionalFlags); //__in DWORD dwAdditionalFlags
[DllImport("kernel32", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FindNextFile(SafeFindHandle hFindFile, [In, Out] FindData lpFindFileData);
[DllImport("kernel32", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FindClose(IntPtr hFindFile);
}
}
Specially added as a new answer..
Since .NET 2.0 There is IENumerable and yield keyword does Lazy Initialization/deferred execution..With these, you can get your wants.
public IEnumerable<string> GetFiles(string rootPath, string [] fileNameStartChars, string[] extensionsFilter)
{
FileSystemInfo[] fsi = null;
for(int i = 0; i < fileNameStartChars.Length; i++)
{
for(int k = 0; k<extensionsFilter.Length; k++)
{
fsi = new DirectoryInfo(rootPath).GetFileSystemInfos(fileNameStartChars[i]+extensionsFilter[k]);
if (fsi.Length > 0)
{
for (int j = 0; j < fsi.Length; j++)
{
/// .Name returns the filename with extension..if you need, please implement here a substring for eliminate the extension of the file
yield return fsi[j].Name;
}
}
}
}
}
And usage :
possible filenames startsWithChar table
public string[] table = new string[]
{
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"1","2","3","4","5","6","7","8","9","0","#","_","-",".","#","+",",","%","&","(",")","[","]","{","}","*",
"<",">","^"," ","|",";","`"
};
And extensions :
string[] Exts = new string[] { ".mp3", ".midi", ".wav"};
with this method, you can filter your data within small parts as such as using startswithchar filtering, so you won't get Memory problem which depends to your files count..This is the tricky part of trying to imitate .net v4's EnumerateFiles method with 100% .net v2 managed code..
IEnumerable<string> strNumerable = GetFiles(#"D:\Music", table, Exts);
///Since its deferred execution, method didn't get any memory alloc for your data till now..Memory Alloc will start within this foreach..
foreach (string s in strNumerable)
{
//do your work
}
Since .NET 2.0 There is IENumerable and yield keyword does Lazy Initialization..With these, you can get your wants.
With a pseudo :
public IENumerable GetFiles(string Path, string FileExtension)
{
// Create a new IENumerable instance
// Get FileCount with DirectoryInfo or some similar
// Implement a for-loop with File count
// If DirectoryFiles [ indexOfForLoop ] .Extension == FileExtension
yield return DirectoryFiles [indexOfForLoop ]
}
In this pseudo the yield keyword take responsibility of the filtering..If filtering returns true the yield return immediately return the result to the IENumerable instance / callee..
And IEnumerable takes responsibility of Lazy Loading..
Depends to your needs, Also you can use yield break keyword in loop to not include the result..
And with a simple call :
List<string> FilesInDirectory = GetFiles( path, "*.txt").ToList();
Hope this helps..
Related
Below console application in c#.net shows only folder names in D drive without full path name but I want to have iterate recursively inside the folders , subfolder and files within those subfolders with help of win32 API because of I have to scan folders having thousands of files.
Win32 API works fast so I m not using regular c#.net function for getting directories and its files.
class Program
{
// Kernal32.dll import for File management operations.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr FindFirstFileEx(
string lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
out WIN32_FIND_DATA lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
IntPtr lpSearchFilter,
int dwAdditionalFlags);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll")]
static extern bool FindClose(IntPtr hFindFile);
public const int FIND_FIRST_EX_CASE_SENSITIVE = 1;
public const int FIND_FIRST_EX_LARGE_FETCH = 2;
static void Main()
{
WIN32_FIND_DATA findData;
FINDEX_INFO_LEVELS findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
int additionalFlags = 0;
if (Environment.OSVersion.Version.Major >= 6)
{
findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
//additionalFlags = FIND_FIRST_EX_LARGE_FETCH;
}
string pattern = "D:\\*.*";
IntPtr hFile = FindFirstFileEx(
pattern,
findInfoLevel,
out findData,
FINDEX_SEARCH_OPS.FindExSearchNameMatch,
IntPtr.Zero,
additionalFlags);
int error = Marshal.GetLastWin32Error();
if (hFile.ToInt32() != -1)
{
do
{
Console.WriteLine("Found file {0}, & Attribute is {1}", findData.cFileName, findData.dwFileAttributes);
}
while (FindNextFile(hFile, out findData));
FindClose(hFile);
}
Console.ReadLine();
}
}
Below is required enum and struct for program.
public enum FINDEX_SEARCH_OPS
{
FindExSearchNameMatch = 0,
FindExSearchLimitToDirectories = 1,
FindExSearchLimitToDevices = 2
}
public enum FINDEX_INFO_LEVELS
{
FindExInfoStandard = 0,
FindExInfoBasic = 1
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
I have got code from Pinvoke.net and website is http://pinvoke.net/
You can put your code into a rekursive function, and call it for each folder:
static void Main()
{
WIN32_FIND_DATA findData;
FINDEX_INFO_LEVELS findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
int additionalFlags = 0;
if (Environment.OSVersion.Version.Major >= 6)
{
findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
//additionalFlags = FIND_FIRST_EX_LARGE_FETCH;
}
string startFolder = #"D:\";
var watch = new Stopwatch();
watch.Start();
IterateFolder(findInfoLevel, additionalFlags, startFolder);
watch.Stop();
Console.WriteLine();
Console.WriteLine($"{watch.Elapsed.TotalSeconds}");
Console.ReadLine();
}
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x10;
private static void IterateFolder(FINDEX_INFO_LEVELS findInfoLevel, int additionalFlags, string folder)
{
WIN32_FIND_DATA findData;
IntPtr hFile = FindFirstFileEx(
$"{folder}\\*.*",
findInfoLevel,
out findData,
FINDEX_SEARCH_OPS.FindExSearchNameMatch,
IntPtr.Zero,
additionalFlags);
int error = Marshal.GetLastWin32Error();
if (hFile.ToInt32() != -1)
{
do
{
if (findData.cFileName == "." || findData.cFileName == "..") continue;//ignore folder navigation
var foundFolder = $"{folder}\\{findData.cFileName}";
Console.WriteLine("Found file {0}, & Attribute is {1}", foundFolder, findData.dwFileAttributes);
if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0)
{
//Recursive for directories
IterateFolder(findInfoLevel, additionalFlags, foundFolder);
}
}
while (FindNextFile(hFile, out findData));
FindClose(hFile);
}
}
I was looking a lot for method to get parent process in .NET, but found only P/Invoke way.
Here is a solution. It uses p/invoke, but seems to work well, 32 or 64 cpu:
/// <summary>
/// A utility class to determine a process parent.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ParentProcessUtilities
{
// These members must match PROCESS_BASIC_INFORMATION
internal IntPtr Reserved1;
internal IntPtr PebBaseAddress;
internal IntPtr Reserved2_0;
internal IntPtr Reserved2_1;
internal IntPtr UniqueProcessId;
internal IntPtr InheritedFromUniqueProcessId;
[DllImport("ntdll.dll")]
private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ParentProcessUtilities processInformation, int processInformationLength, out int returnLength);
/// <summary>
/// Gets the parent process of the current process.
/// </summary>
/// <returns>An instance of the Process class.</returns>
public static Process GetParentProcess()
{
return GetParentProcess(Process.GetCurrentProcess().Handle);
}
/// <summary>
/// Gets the parent process of specified process.
/// </summary>
/// <param name="id">The process id.</param>
/// <returns>An instance of the Process class.</returns>
public static Process GetParentProcess(int id)
{
Process process = Process.GetProcessById(id);
return GetParentProcess(process.Handle);
}
/// <summary>
/// Gets the parent process of a specified process.
/// </summary>
/// <param name="handle">The process handle.</param>
/// <returns>An instance of the Process class.</returns>
public static Process GetParentProcess(IntPtr handle)
{
ParentProcessUtilities pbi = new ParentProcessUtilities();
int returnLength;
int status = NtQueryInformationProcess(handle, 0, ref pbi, Marshal.SizeOf(pbi), out returnLength);
if (status != 0)
throw new Win32Exception(status);
try
{
return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
}
catch (ArgumentException)
{
// not found
return null;
}
}
}
This code provides a nice interface for finding the Parent process object and takes into account the possibility of multiple processes with the same name:
Usage:
Console.WriteLine("ParentPid: " + Process.GetProcessById(6972).Parent().Id);
Code:
public static class ProcessExtensions {
private static string FindIndexedProcessName(int pid) {
var processName = Process.GetProcessById(pid).ProcessName;
var processesByName = Process.GetProcessesByName(processName);
string processIndexdName = null;
for (var index = 0; index < processesByName.Length; index++) {
processIndexdName = index == 0 ? processName : processName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int) processId.NextValue() == pid) {
return processIndexdName;
}
}
return processIndexdName;
}
private static Process FindPidFromIndexedProcessName(string indexedProcessName) {
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
return Process.GetProcessById((int) parentId.NextValue());
}
public static Process Parent(this Process process) {
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
}
}
This way:
public static Process GetParent(this Process process)
{
try
{
using (var query = new ManagementObjectSearcher(
"SELECT * " +
"FROM Win32_Process " +
"WHERE ProcessId=" + process.Id))
{
return query
.Get()
.OfType<ManagementObject>()
.Select(p => Process.GetProcessById((int)(uint)p["ParentProcessId"]))
.FirstOrDefault();
}
}
catch
{
return null;
}
}
Here's my try at a managed solution.
It polls the performance counters for all processes and returns a dictionary of child PID to parent PID. Then you can check the dictionary with your current PID to see your parent, grandparent, etc.
It is overkill in how much info it gets, for sure. Feel free to optimize.
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace PidExamples
{
class ParentPid
{
static void Main(string[] args)
{
var childPidToParentPid = GetAllProcessParentPids();
int currentProcessId = Process.GetCurrentProcess().Id;
Console.WriteLine("Current Process ID: " + currentProcessId);
Console.WriteLine("Parent Process ID: " + childPidToParentPid[currentProcessId]);
}
public static Dictionary<int, int> GetAllProcessParentPids()
{
var childPidToParentPid = new Dictionary<int, int>();
var processCounters = new SortedDictionary<string, PerformanceCounter[]>();
var category = new PerformanceCounterCategory("Process");
// As the base system always has more than one process running,
// don't special case a single instance return.
var instanceNames = category.GetInstanceNames();
foreach(string t in instanceNames)
{
try
{
processCounters[t] = category.GetCounters(t);
}
catch (InvalidOperationException)
{
// Transient processes may no longer exist between
// GetInstanceNames and when the counters are queried.
}
}
foreach (var kvp in processCounters)
{
int childPid = -1;
int parentPid = -1;
foreach (var counter in kvp.Value)
{
if ("ID Process".CompareTo(counter.CounterName) == 0)
{
childPid = (int)(counter.NextValue());
}
else if ("Creating Process ID".CompareTo(counter.CounterName) == 0)
{
parentPid = (int)(counter.NextValue());
}
}
if (childPid != -1 && parentPid != -1)
{
childPidToParentPid[childPid] = parentPid;
}
}
return childPidToParentPid;
}
}
}
In other news, I learned how many performance counters there were on my machine: 13401. Holy cow.
If accepting P/Invoke, there's a better way, which is more documented than
NtQueryInformationProcess: namely PROCESSENTRY32 (CreateToolhelp32Snapshot, Process32First, Process32Next). It's shown in this post.
Pay attention to the subtle details
and note that parent PID is not necessarily the creator PID, in fact these may be completely unrelated, as pointed out by the community comments at
PROCESSENTRY32.
If you've ever digged the BCL, you shall find that the ways to find parent process are deliberately avoided, take this for example:
https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/ProcessManager.cs,327
As you can see in the source code, it contains comprehensive structures and imported native methods which are absolutely sufficient to get the job done. However, even if you access them via reflection(this is possible), you would not find a method for doing it directly. I can't answer why, yet this phenomenon causes questions like yours are asked for somewhat repeatedly; for example:
How can I get the PID of the parent process of my application
For there is no answer along with some code using CreateToolhelp32Snapshot in this thread, I'd add it -- part of the structure definitions and names I steal from the MS' reference source :)
Code
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System;
public static class Toolhelp32 {
public const uint Inherit = 0x80000000;
public const uint SnapModule32 = 0x00000010;
public const uint SnapAll = SnapHeapList|SnapModule|SnapProcess|SnapThread;
public const uint SnapHeapList = 0x00000001;
public const uint SnapProcess = 0x00000002;
public const uint SnapThread = 0x00000004;
public const uint SnapModule = 0x00000008;
[DllImport("kernel32.dll")]
static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32.dll")]
static extern IntPtr CreateToolhelp32Snapshot(uint flags, int processId);
public static IEnumerable<T> TakeSnapshot<T>(uint flags, int id) where T : IEntry, new() {
using(var snap = new Snapshot(flags, id))
for(IEntry entry = new T { }; entry.TryMoveNext(snap, out entry);)
yield return (T)entry;
}
public interface IEntry {
bool TryMoveNext(Toolhelp32.Snapshot snap, out IEntry entry);
}
public struct Snapshot:IDisposable {
void IDisposable.Dispose() {
Toolhelp32.CloseHandle(m_handle);
}
public Snapshot(uint flags, int processId) {
m_handle=Toolhelp32.CreateToolhelp32Snapshot(flags, processId);
}
IntPtr m_handle;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct WinProcessEntry:Toolhelp32.IEntry {
[DllImport("kernel32.dll")]
public static extern bool Process32Next(Toolhelp32.Snapshot snap, ref WinProcessEntry entry);
public bool TryMoveNext(Toolhelp32.Snapshot snap, out Toolhelp32.IEntry entry) {
var x = new WinProcessEntry { dwSize=Marshal.SizeOf(typeof(WinProcessEntry)) };
var b = Process32Next(snap, ref x);
entry=x;
return b;
}
public int dwSize;
public int cntUsage;
public int th32ProcessID;
public IntPtr th32DefaultHeapID;
public int th32ModuleID;
public int cntThreads;
public int th32ParentProcessID;
public int pcPriClassBase;
public int dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public String fileName;
//byte fileName[260];
//public const int sizeofFileName = 260;
}
public static class Extensions {
public static Process Parent(this Process p) {
var entries = Toolhelp32.TakeSnapshot<WinProcessEntry>(Toolhelp32.SnapAll, 0);
var parentid = entries.First(x => x.th32ProcessID==p.Id).th32ParentProcessID;
return Process.GetProcessById(parentid);
}
}
And we can use it like:
Test
public class TestClass {
public static void TestMethod() {
var p = Process.GetCurrentProcess().Parent();
Console.WriteLine("{0}", p.Id);
}
}
For alternative ending ..
According to the documentation, there are a pair of iteration methods per type of the entries such as Process32First and Process32Next are for the iteration of processes; but I found the `xxxxFirst' methods are unnecessary, and then I thought why not put the iteration method with its corresponding entry type? It'd be easier to implement and be understood(I guess so ..).
Just as Toolhelp32 suffixed with help, I think a static helper class is proper, so that we can have the clear qualified names such as Toolhelp32.Snapshot or Toolhelp32.IEntry though it'd be irrelevant here ..
Once the parent process is obtained, if you further want to get some detailed infos, you can extend with this easily, for example, iterate on its modules, then add:
Code - WinModuleEntry
[StructLayout(LayoutKind.Sequential)]
public struct WinModuleEntry:Toolhelp32.IEntry { // MODULEENTRY32
[DllImport("kernel32.dll")]
public static extern bool Module32Next(Toolhelp32.Snapshot snap, ref WinModuleEntry entry);
public bool TryMoveNext(Toolhelp32.Snapshot snap, out Toolhelp32.IEntry entry) {
var x = new WinModuleEntry { dwSize=Marshal.SizeOf(typeof(WinModuleEntry)) };
var b = Module32Next(snap, ref x);
entry=x;
return b;
}
public int dwSize;
public int th32ModuleID;
public int th32ProcessID;
public int GlblcntUsage;
public int ProccntUsage;
public IntPtr modBaseAddr;
public int modBaseSize;
public IntPtr hModule;
//byte moduleName[256];
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string moduleName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string fileName;
//byte fileName[260];
//public const int sizeofModuleName = 256;
//public const int sizeofFileName = 260;
}
and some test ..
public class TestClass {
public static void TestMethod() {
var p = Process.GetCurrentProcess().Parent();
Console.WriteLine("{0}", p.Id);
var formatter = new CustomFormatter { };
foreach(var x in Toolhelp32.TakeSnapshot<WinModuleEntry>(Toolhelp32.SnapModule, p.Id)) {
Console.WriteLine(String.Format(formatter, "{0}", x));
}
}
}
public class CustomFormatter:IFormatProvider, ICustomFormatter {
String ICustomFormatter.Format(String format, object arg, IFormatProvider formatProvider) {
var type = arg.GetType();
var fields = type.GetFields();
var q = fields.Select(x => String.Format("{0}:{1}", x.Name, x.GetValue(arg)));
return String.Format("{{{0}}}", String.Join(", ", q.ToArray()));
}
object IFormatProvider.GetFormat(Type formatType) {
return typeof(ICustomFormatter)!=formatType ? null : this;
}
}
In case you want a code example ..
https://github.com/kenkins/ToolHelp32ConsoleApp
Unfortunately, there is still no managed cross-platform solution to get a parent process as of .NET 6.
This issue is being discussed (since Dec 12, 2017) on the .NET repository: Expose Parent/Child Process Information via System.Diagnostics.Process (#24423).
I was looking a lot for method to get parent process in .NET, but found only P/Invoke way.
Here is a solution. It uses p/invoke, but seems to work well, 32 or 64 cpu:
/// <summary>
/// A utility class to determine a process parent.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ParentProcessUtilities
{
// These members must match PROCESS_BASIC_INFORMATION
internal IntPtr Reserved1;
internal IntPtr PebBaseAddress;
internal IntPtr Reserved2_0;
internal IntPtr Reserved2_1;
internal IntPtr UniqueProcessId;
internal IntPtr InheritedFromUniqueProcessId;
[DllImport("ntdll.dll")]
private static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, ref ParentProcessUtilities processInformation, int processInformationLength, out int returnLength);
/// <summary>
/// Gets the parent process of the current process.
/// </summary>
/// <returns>An instance of the Process class.</returns>
public static Process GetParentProcess()
{
return GetParentProcess(Process.GetCurrentProcess().Handle);
}
/// <summary>
/// Gets the parent process of specified process.
/// </summary>
/// <param name="id">The process id.</param>
/// <returns>An instance of the Process class.</returns>
public static Process GetParentProcess(int id)
{
Process process = Process.GetProcessById(id);
return GetParentProcess(process.Handle);
}
/// <summary>
/// Gets the parent process of a specified process.
/// </summary>
/// <param name="handle">The process handle.</param>
/// <returns>An instance of the Process class.</returns>
public static Process GetParentProcess(IntPtr handle)
{
ParentProcessUtilities pbi = new ParentProcessUtilities();
int returnLength;
int status = NtQueryInformationProcess(handle, 0, ref pbi, Marshal.SizeOf(pbi), out returnLength);
if (status != 0)
throw new Win32Exception(status);
try
{
return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
}
catch (ArgumentException)
{
// not found
return null;
}
}
}
This code provides a nice interface for finding the Parent process object and takes into account the possibility of multiple processes with the same name:
Usage:
Console.WriteLine("ParentPid: " + Process.GetProcessById(6972).Parent().Id);
Code:
public static class ProcessExtensions {
private static string FindIndexedProcessName(int pid) {
var processName = Process.GetProcessById(pid).ProcessName;
var processesByName = Process.GetProcessesByName(processName);
string processIndexdName = null;
for (var index = 0; index < processesByName.Length; index++) {
processIndexdName = index == 0 ? processName : processName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int) processId.NextValue() == pid) {
return processIndexdName;
}
}
return processIndexdName;
}
private static Process FindPidFromIndexedProcessName(string indexedProcessName) {
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
return Process.GetProcessById((int) parentId.NextValue());
}
public static Process Parent(this Process process) {
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
}
}
This way:
public static Process GetParent(this Process process)
{
try
{
using (var query = new ManagementObjectSearcher(
"SELECT * " +
"FROM Win32_Process " +
"WHERE ProcessId=" + process.Id))
{
return query
.Get()
.OfType<ManagementObject>()
.Select(p => Process.GetProcessById((int)(uint)p["ParentProcessId"]))
.FirstOrDefault();
}
}
catch
{
return null;
}
}
Here's my try at a managed solution.
It polls the performance counters for all processes and returns a dictionary of child PID to parent PID. Then you can check the dictionary with your current PID to see your parent, grandparent, etc.
It is overkill in how much info it gets, for sure. Feel free to optimize.
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace PidExamples
{
class ParentPid
{
static void Main(string[] args)
{
var childPidToParentPid = GetAllProcessParentPids();
int currentProcessId = Process.GetCurrentProcess().Id;
Console.WriteLine("Current Process ID: " + currentProcessId);
Console.WriteLine("Parent Process ID: " + childPidToParentPid[currentProcessId]);
}
public static Dictionary<int, int> GetAllProcessParentPids()
{
var childPidToParentPid = new Dictionary<int, int>();
var processCounters = new SortedDictionary<string, PerformanceCounter[]>();
var category = new PerformanceCounterCategory("Process");
// As the base system always has more than one process running,
// don't special case a single instance return.
var instanceNames = category.GetInstanceNames();
foreach(string t in instanceNames)
{
try
{
processCounters[t] = category.GetCounters(t);
}
catch (InvalidOperationException)
{
// Transient processes may no longer exist between
// GetInstanceNames and when the counters are queried.
}
}
foreach (var kvp in processCounters)
{
int childPid = -1;
int parentPid = -1;
foreach (var counter in kvp.Value)
{
if ("ID Process".CompareTo(counter.CounterName) == 0)
{
childPid = (int)(counter.NextValue());
}
else if ("Creating Process ID".CompareTo(counter.CounterName) == 0)
{
parentPid = (int)(counter.NextValue());
}
}
if (childPid != -1 && parentPid != -1)
{
childPidToParentPid[childPid] = parentPid;
}
}
return childPidToParentPid;
}
}
}
In other news, I learned how many performance counters there were on my machine: 13401. Holy cow.
If accepting P/Invoke, there's a better way, which is more documented than
NtQueryInformationProcess: namely PROCESSENTRY32 (CreateToolhelp32Snapshot, Process32First, Process32Next). It's shown in this post.
Pay attention to the subtle details
and note that parent PID is not necessarily the creator PID, in fact these may be completely unrelated, as pointed out by the community comments at
PROCESSENTRY32.
If you've ever digged the BCL, you shall find that the ways to find parent process are deliberately avoided, take this for example:
https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/ProcessManager.cs,327
As you can see in the source code, it contains comprehensive structures and imported native methods which are absolutely sufficient to get the job done. However, even if you access them via reflection(this is possible), you would not find a method for doing it directly. I can't answer why, yet this phenomenon causes questions like yours are asked for somewhat repeatedly; for example:
How can I get the PID of the parent process of my application
For there is no answer along with some code using CreateToolhelp32Snapshot in this thread, I'd add it -- part of the structure definitions and names I steal from the MS' reference source :)
Code
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System;
public static class Toolhelp32 {
public const uint Inherit = 0x80000000;
public const uint SnapModule32 = 0x00000010;
public const uint SnapAll = SnapHeapList|SnapModule|SnapProcess|SnapThread;
public const uint SnapHeapList = 0x00000001;
public const uint SnapProcess = 0x00000002;
public const uint SnapThread = 0x00000004;
public const uint SnapModule = 0x00000008;
[DllImport("kernel32.dll")]
static extern bool CloseHandle(IntPtr handle);
[DllImport("kernel32.dll")]
static extern IntPtr CreateToolhelp32Snapshot(uint flags, int processId);
public static IEnumerable<T> TakeSnapshot<T>(uint flags, int id) where T : IEntry, new() {
using(var snap = new Snapshot(flags, id))
for(IEntry entry = new T { }; entry.TryMoveNext(snap, out entry);)
yield return (T)entry;
}
public interface IEntry {
bool TryMoveNext(Toolhelp32.Snapshot snap, out IEntry entry);
}
public struct Snapshot:IDisposable {
void IDisposable.Dispose() {
Toolhelp32.CloseHandle(m_handle);
}
public Snapshot(uint flags, int processId) {
m_handle=Toolhelp32.CreateToolhelp32Snapshot(flags, processId);
}
IntPtr m_handle;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct WinProcessEntry:Toolhelp32.IEntry {
[DllImport("kernel32.dll")]
public static extern bool Process32Next(Toolhelp32.Snapshot snap, ref WinProcessEntry entry);
public bool TryMoveNext(Toolhelp32.Snapshot snap, out Toolhelp32.IEntry entry) {
var x = new WinProcessEntry { dwSize=Marshal.SizeOf(typeof(WinProcessEntry)) };
var b = Process32Next(snap, ref x);
entry=x;
return b;
}
public int dwSize;
public int cntUsage;
public int th32ProcessID;
public IntPtr th32DefaultHeapID;
public int th32ModuleID;
public int cntThreads;
public int th32ParentProcessID;
public int pcPriClassBase;
public int dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public String fileName;
//byte fileName[260];
//public const int sizeofFileName = 260;
}
public static class Extensions {
public static Process Parent(this Process p) {
var entries = Toolhelp32.TakeSnapshot<WinProcessEntry>(Toolhelp32.SnapAll, 0);
var parentid = entries.First(x => x.th32ProcessID==p.Id).th32ParentProcessID;
return Process.GetProcessById(parentid);
}
}
And we can use it like:
Test
public class TestClass {
public static void TestMethod() {
var p = Process.GetCurrentProcess().Parent();
Console.WriteLine("{0}", p.Id);
}
}
For alternative ending ..
According to the documentation, there are a pair of iteration methods per type of the entries such as Process32First and Process32Next are for the iteration of processes; but I found the `xxxxFirst' methods are unnecessary, and then I thought why not put the iteration method with its corresponding entry type? It'd be easier to implement and be understood(I guess so ..).
Just as Toolhelp32 suffixed with help, I think a static helper class is proper, so that we can have the clear qualified names such as Toolhelp32.Snapshot or Toolhelp32.IEntry though it'd be irrelevant here ..
Once the parent process is obtained, if you further want to get some detailed infos, you can extend with this easily, for example, iterate on its modules, then add:
Code - WinModuleEntry
[StructLayout(LayoutKind.Sequential)]
public struct WinModuleEntry:Toolhelp32.IEntry { // MODULEENTRY32
[DllImport("kernel32.dll")]
public static extern bool Module32Next(Toolhelp32.Snapshot snap, ref WinModuleEntry entry);
public bool TryMoveNext(Toolhelp32.Snapshot snap, out Toolhelp32.IEntry entry) {
var x = new WinModuleEntry { dwSize=Marshal.SizeOf(typeof(WinModuleEntry)) };
var b = Module32Next(snap, ref x);
entry=x;
return b;
}
public int dwSize;
public int th32ModuleID;
public int th32ProcessID;
public int GlblcntUsage;
public int ProccntUsage;
public IntPtr modBaseAddr;
public int modBaseSize;
public IntPtr hModule;
//byte moduleName[256];
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string moduleName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string fileName;
//byte fileName[260];
//public const int sizeofModuleName = 256;
//public const int sizeofFileName = 260;
}
and some test ..
public class TestClass {
public static void TestMethod() {
var p = Process.GetCurrentProcess().Parent();
Console.WriteLine("{0}", p.Id);
var formatter = new CustomFormatter { };
foreach(var x in Toolhelp32.TakeSnapshot<WinModuleEntry>(Toolhelp32.SnapModule, p.Id)) {
Console.WriteLine(String.Format(formatter, "{0}", x));
}
}
}
public class CustomFormatter:IFormatProvider, ICustomFormatter {
String ICustomFormatter.Format(String format, object arg, IFormatProvider formatProvider) {
var type = arg.GetType();
var fields = type.GetFields();
var q = fields.Select(x => String.Format("{0}:{1}", x.Name, x.GetValue(arg)));
return String.Format("{{{0}}}", String.Join(", ", q.ToArray()));
}
object IFormatProvider.GetFormat(Type formatType) {
return typeof(ICustomFormatter)!=formatType ? null : this;
}
}
In case you want a code example ..
https://github.com/kenkins/ToolHelp32ConsoleApp
Unfortunately, there is still no managed cross-platform solution to get a parent process as of .NET 6.
This issue is being discussed (since Dec 12, 2017) on the .NET repository: Expose Parent/Child Process Information via System.Diagnostics.Process (#24423).
I want to findout how many audio input and output devices connected to my system in C#.net
Please suggest any.
Thanks.
You need to access the necessary Windows API functions.
This class should get you started - Win32.GetSoundDevices returns a list of device names. Look up WAVEOUTCAPS in the Windows SDK for details of the other information you can get.
public class Win32 {
[DllImport("winmm.dll", SetLastError=true)]
static extern uint waveOutGetNumDevs();
[DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint waveOutGetDevCaps(uint hwo,ref WAVEOUTCAPS pwoc,uint cbwoc);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WAVEOUTCAPS {
public ushort wMid;
public ushort wPid;
public uint vDriverVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string szPname;
public uint dwFormats;
public ushort wChannels;
public ushort wReserved1;
public uint dwSupport;
}
public static string [] GetSoundDevices() {
uint devices = waveOutGetNumDevs();
string [] result = new string[devices];
WAVEOUTCAPS caps = new WAVEOUTCAPS();
for(uint i = 0; i < devices; i++) {
waveOutGetDevCaps(i, ref caps, (uint)Marshal.SizeOf(caps));
result[i] = caps.szPname;
}
return result;
}
}
Programmatic solution of course...
http://www.daveamenta.com/2008-05/c-delete-a-file-to-the-recycle-bin/
From above:
using Microsoft.VisualBasic;
string path = #"c:\myfile.txt";
FileIO.FileSystem.DeleteDirectory(path,
FileIO.UIOption.OnlyErrorDialogs,
RecycleOption.SendToRecycleBin);
You need to delve into unmanaged code. Here's a static class that I've been using:
public static class Recycle
{
private const int FO_DELETE = 3;
private const int FOF_ALLOWUNDO = 0x40;
private const int FOF_NOCONFIRMATION = 0x0010;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
public struct SHFILEOPSTRUCT
{
public IntPtr hwnd;
[MarshalAs(UnmanagedType.U4)]
public int wFunc;
public string pFrom;
public string pTo;
public short fFlags;
[MarshalAs(UnmanagedType.Bool)]
public bool fAnyOperationsAborted;
public IntPtr hNameMappings;
public string lpszProgressTitle;
}
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp);
public static void DeleteFileOperation(string filePath)
{
SHFILEOPSTRUCT fileop = new SHFILEOPSTRUCT();
fileop.wFunc = FO_DELETE;
fileop.pFrom = filePath + '\0' + '\0';
fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
SHFileOperation(ref fileop);
}
}
Addendum:
Tsk tsk # Jeff for "using Microsoft.VisualBasic" in C# code.
Tsk tsk # MS for putting all the goodies in VisualBasic namespace.
The best way I have found is to use the VB function FileSystem.DeleteFile.
Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(file.FullName,
Microsoft.VisualBasic.FileIO.UIOption.OnlyErrorDialogs,
Microsoft.VisualBasic.FileIO.RecycleOption.SendToRecycleBin);
It requires adding Microsoft.VisualBasic as a reference, but this is part of the .NET framework and so isn't an extra dependency.
Alternate solutions require a P/Invoke to SHFileOperation, as well as defining all the various structures/constants. Including Microsoft.VisualBasic is much neater by comparison.