-1

当我试图进行测试并了解本机 p/invoke 函数时,我试图仅使用 pinvoke,然后比较使用 .net simple 获取进程信息所需的时间

Process myProc = Process.GetProcessByName("WinRAR");

虽然我觉得我需要真正测量将近 2 页的长度代码,使用 P/invoke 才能得到相同的结果,但这次只使用本机代码,我想它应该更快,我想至少得到对两者进行基准测试,所以请在这里提供帮助。

所以看来我的代码是1)......好吧,我想我可以数到20“枚举”所有它的问题,但主要是:

  • 它没有枚举所有进程,原因很奇怪,例如我没有看到 winrar
  • 其次,它远没有 pinvoke 方法需要的那么短

(我正在使用 Winforms 应用程序,尽管您可以硬编码ProcessName所需的代码以便“搜索”正确的过程)

这里的大部分评论都是作者的好,大部分代码我只修改了一点,以便稍后有枚举,所以你可以选择通过窗口标题或进程名称进行搜索

所以这是代码:

主条目 - 创建类的实例:

        pinvokers Pi = new pinvokers();

        // Find all Internet Explorer instances(i used winrar, as my second task in this project is also test application performance... later on, and again, using only native calls)

        Pi.FindWindows(0, pinvokers.SearchWin.ProcName, null, new Regex(TBX_SelectedWinName.Text), new pinvokers.FoundWindowCallback(pinvokers.foundWindowToPrint));



public class pinvokers
{


    // Win32 constants.
    const int WM_GETTEXT = 0x000D;
    const int WM_GETTEXTLENGTH = 0x000E;
    [DllImport("user32.Dll")]
    private static extern Boolean EnumChildWindows(int hWndParent, PChildCallBack lpEnumFunc, int lParam);
    [DllImport("user32.Dll")]
    private static extern int GetWindowText(int hWnd, StringBuilder text, int count);
    [DllImport("user32.Dll")]
    private static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);
    [DllImport("user32.Dll")]
    private static extern Int32 SendMessage(int hWnd, int Msg, int wParam, StringBuilder lParam);
    [DllImport("user32.Dll")]
    private static extern Int32 SendMessage(int hWnd, int Msg, int wParam, int lParam);
    [DllImport("kernel32.dll")]
    public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern uint GetWindowModuleFileName(IntPtr hwnd,
       StringBuilder lpszFileName, uint cchFileNameMax);
    [DllImport("psapi.dll")]
    private static extern uint GetModuleFileNameEx(IntPtr hWnd, IntPtr hModule, StringBuilder lpFileName, int nSize);


    // The PChildCallBack delegate that we used with EnumWindows.
    private delegate bool PChildCallBack(int hWnd, int lParam);

    // This is an event that is run each time a window was found that matches the search criterias. The boolean
    // return value of the delegate matches the functionality of the PChildCallBack delegate function.
    static event FoundWindowCallback foundWindowCB;
    public delegate bool FoundWindowCallback(int hWnd);
    int parentHandle;
    Regex process;

   #region <<===========  not nedded - search by window title. i am looking to search via process name ===========>>

   /* <- commented all unsuesd
    Regex windowText;
    public static bool foundWindowToPrint(int handle)
    {
        // Print the window info.
        printWindowInfo(handle);

        // Continue on with next window.
        return true;
    }

    static void printWindowInfo(int handle)
    {

        // Get the text.
        int txtLength = SendMessage(handle, WM_GETTEXTLENGTH, 0, 0);
        StringBuilder sbText = new StringBuilder(txtLength + 1);
        SendMessage(handle, WM_GETTEXT, sbText.Capacity, sbText);

        // Now we can write out the information we have on the window.
        MessageBox.Show("Handle: " + handle);
        MessageBox.Show("Text  : " + sbText);

    }
     =====>end of un needed search bywindowtitle1
      */
     #endregion


    // my plan was to use enum instead of if !empty or null value for ither title name or process name so that's how the original code ditermin wich one to execute. 
    public enum SearchWin
    {
        Title, ProcName
    }

     //first method (and that's all i could really tell.. as it is full of callbacks and private extern, and delegates ... so complex

    public void FindWindows(int parentHandle, SearchWin By, Regex windowText, Regex process, FoundWindowCallback fwc)
    {
        this.parentHandle = parentHandle;

        //this.windowText = windowText;
        this.process = process;

        // Add the FounWindowCallback to the foundWindow event.
        foundWindowCB = fwc;

        // Invoke the EnumChildWindows function.
        EnumChildWindows(parentHandle, new PChildCallBack(enumChildWindowsCallback), 0);


    }



    // This function gets called each time a window is found by the EnumChildWindows function. The foun windows here
    // are NOT the final found windows as the only filtering done by EnumChildWindows is on the parent window handle.
    private bool enumChildWindowsCallback(int handle, int lParam)
    {

   #region <<===========  not nedded - search by window title. #2 ===========>>
        /* <--here too window title portion of code commented

        // If a window text was provided, check to see if it matches the window.
        if (windowText != null)
        {
            int txtLength = SendMessage(handle, WM_GETTEXTLENGTH, 0, 0);
            StringBuilder sbText = new StringBuilder(txtLength + 1);
            SendMessage(handle, WM_GETTEXT, sbText.Capacity, sbText);

            // If it does not match, return true so we can continue on with the next window.
            if (!windowText.IsMatch(sbText.ToString()))
                return true;
        }
        */

        #endregion //endr2


        // If a process name was provided, check to see if it matches the window.
        if (process != null)
        {
            int processID;
            GetWindowThreadProcessId(handle, out processID);

            // Now that we have the process ID, we can use the built in .NET function to obtain a process object.
            var ProcessName = GetProcNameByID(processID);

            // If it does not match, return true so we can continue on with the next window.
            if (!process.IsMatch(ProcessName))
                return true;
        }

        // If we get to this point, the window is a match. Now invoke the foundWindow event and based upon
        // the return value, whether we should continue to search for windows.
        return foundWindowCB(handle);
    }

    private string GetProcNameByID(int ProcID)
    {



        IntPtr hProcess = OpenProcess(0x0410, false, ProcID);

        StringBuilder text = new StringBuilder(1000);
        GetWindowModuleFileName(hProcess, text, (uint)text.Capacity);
        //GetModuleFileNameEx(hProcess, IntPtr.Zero, text, text.Capacity);

        //CloseHandle(hProcess); here i am trying to catch what enumeration of windows got in its net , all this code does work just copy and paste it .
        var t =  text.ToString();
        if (t.ToLower().Contains("inra")) 
        MessageBox.Show(t);
        return t;


    }
}

所以这能不能再短一点是一个附带问题,主要问题是:

为什么它不枚举所有进程?

4

1 回答 1

0

我不知道这是否是我能得到的最好的,或者也许有人知道他在用 win api 或 p/invoke 做什么,或者我是否必须尝试让 unmanagedLand 战胜我可能已经推出的内置 .net我袖手旁观,将一些 c++ 代码放在一起(可能需要再花一周时间)并将其编译为 dll 以将所有功能放在一个 DLL 中(如果它可以提高性能),然后我可能会减少一些差距。(顺便说一句,现在它更接近系统诊断结果虽然我认为它会更快,但我错了)

但仍然只是因为知道我可以安全地使用 .net C# 并相信微软比我更了解(:如何制作一种好的编程语言。

这是我用来通过所有这些 dllllls 导入的代码。我应该知道进口任何东西都需要花费(这里可能是进口税很昂贵)

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

    }


    private void But_StartPinvoke_Click(object sender, EventArgs e)
    {
        var userInputOK = TBX_SelectedProcessName.userInput();
        if(!userInputOK)
            MessageBox.Show(RApss.mesgs.EmptyTbx);
        RApss.Strings.UserInput = TBX_SelectedProcessName.Text;
        RApss.Globs.TbxPname = TBX_SelectedProcessName.Text.AddSufixEXE();
        doWarmUp();

        Stopwatch SwGpbn = Stopwatch.StartNew();
        SwGpbn.Start();

        //string _netProcName = Process.GetProcessesByName(RApss.Strings.UserInput)[0].ProcessName;
        Process p = Process.GetProcessesByName(RApss.Strings.UserInput)[0];
        if (p.ProcessName.ResultFetched())
        SwGpbn.Stop();
        var msElps_Net4 = SwGpbn.ElapsedMilliseconds;
        SwGpbn.Reset();
        SwGpbn.Start();
        EnumProcessesV3.GetProcessByName();
        SwGpbn.Stop();
        var msElpsNat = SwGpbn.ElapsedMilliseconds;
        SwGpbn.Reset();



        SwGpbn.Reset();
        if (RApss.Globs.Result.ResultFetched()) MessageBox.Show(string.Concat(RApss.Globs.Result, "\r\nWas Fetched In: ", msElpsNat, " Via PinVoke\r\n Was Fetched In: ", msElps_Net4," Via C#.NET !" ));
    }

    private void doWarmUp()
    {
        List<string> swarm = new List<string>();
        for (int i = 0; i < 50000; i++)
        {
           swarm.Add((i + 1 *500).ToString());
        }
    }
}

public class RApss
{
    public class Globs
    {
        public static string TbxPname;
        public static string Result = string.Empty;
    }
    public class Strings
    {
        public static string intputForProcessName = "Requiered Process Name";
        public static string UserInput = string.Empty;
    }

    public class mesgs
    {
        public static string EmptyTbx = string.Concat("please fill ", Strings.intputForProcessName, " field");
    }
} 
   public class EnumProcessesV3
   {


    #region APIS
    [DllImport("psapi")]
    private static extern bool EnumProcesses(
        [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U4)] [In][Out] IntPtr[] processIds,
        UInt32 arraySizeBytes, 
        [MarshalAs(UnmanagedType.U4)] out UInt32 bytesCopied);

    [DllImport("kernel32.dll")]
    static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, IntPtr dwProcessId);

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool CloseHandle(IntPtr hObject);

    [DllImport("psapi.dll")]
    static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);

    [DllImport("psapi.dll", SetLastError = true)]
    public static extern bool EnumProcessModules(IntPtr hProcess,
    [Out] IntPtr lphModule,
    uint cb,
    [MarshalAs(UnmanagedType.U4)] out uint lpcbNeeded);

    [DllImport("psapi.dll")]
    static extern uint GetModuleBaseName(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);
    #endregion

    #region ENUMS

    [Flags]
    enum ProcessAccessFlags : uint
    {
        All = 0x001F0FFF,
        Terminate = 0x00000001,
        CreateThread = 0x00000002,
        VMOperation = 0x00000008,
        VMRead = 0x00000010,
        VMWrite = 0x00000020,
        DupHandle = 0x00000040,
        SetInformation = 0x00000200,
        QueryInformation = 0x00000400,
        Synchronize = 0x00100000
    }
    #endregion

    public static void GetProcessByName()
    {
        UInt32 arraySize = 120;
        UInt32 arrayBytesSize = arraySize * sizeof(UInt32);
        IntPtr[] processIds = new IntPtr[arraySize];
        UInt32 bytesCopied;

        bool success = EnumProcesses(processIds, arrayBytesSize, out bytesCopied);
        #region <<=========== some cleanUps  ============>>


        // trying to check what could have been taking extra mssssnds


        //Console.WriteLine("success={0}", success);
        //Console.WriteLine("bytesCopied={0}", bytesCopied);

        //if (!success)
        //{
        //    MessageBox.Show("Boo!");
        //    return;
        //}
        //if (0 == bytesCopied)
        //{
        //    MessageBox.Show("Nobody home!");
        //    return;
        //}
        #endregion
        UInt32 numIdsCopied = bytesCopied >> 2;
        #region <<===========same here commenting anything that might cost nerowing the options   ============>>





        //if (0 != (bytesCopied & 3))
        //{
        //    UInt32 partialDwordBytes = bytesCopied & 3;

        //    MessageBox.Show(String.Format("EnumProcesses copied {0} and {1}/4th DWORDS...  Please ask it for the other {2}/4th DWORD",
        //        numIdsCopied, partialDwordBytes, 4 - partialDwordBytes));
        //    return;
        //}
        //taking initialisation of SB out of loop was a winning thought but nada no change maybe in nanos

        #endregion



        for (UInt32 index = numIdsCopied; index> 1 ; index--) // reversing from last process id(chitting) to erlier process id did not help to win the contest
        {
            StringBuilder szProcessName = new StringBuilder(1000);
            int x = szProcessName.Capacity;
            string sName = PrintProcessName(processIds[index-1],szProcessName,x);
            if (sName.Equals(RApss.Globs.TbxPname)) // tryng hardcoded value instead of reading from a variable.(GlobalsClass)
            {
                RApss.Globs.Result = sName; 
                break;
            }
            ////////IntPtr PID = processIds[index];
            ////////Console.WriteLine("Name '" + sName + "' PID '" + PID + "'");
        }
    }


    static string PrintProcessName(IntPtr processID, StringBuilder sb, int Cpcty)
    {
        string sName = "";
        //bool bFound = false;
        IntPtr hProcess = OpenProcess(ProcessAccessFlags.QueryInformation | ProcessAccessFlags.VMRead, false, processID);
        if (hProcess != IntPtr.Zero)
        {

            IntPtr hMod = IntPtr.Zero;
            uint cbNeeded = 0;
            EnumProcessModules(hProcess, hMod, (uint)Marshal.SizeOf(typeof(IntPtr)), out cbNeeded);
            if (GetModuleBaseName(hProcess, hMod, sb, Cpcty) > 0)
            {
                sName = sb.ToString();

                //bFound = true;
            }

            // Close the process handle
            CloseHandle(hProcess);
        }
        //if (!bFound)
        //{
        //    sName = "<unknown>";
        //}
        return sName;
    }

  }
}
namespace RExt
{

    public static class UserInputs
    {
        public static bool userInput(this TextBox tbxId)
        {
            return tbxId.Text.Length > 1;
        }
    }
    public static class strExt
    {
        public static bool ResultFetched(this string StrToCheck)
        {
            return !string.IsNullOrWhiteSpace(StrToCheck);
        }
        public static string AddSufixEXE(this string StrToAppendEXE)
        {
            return string.Concat(StrToAppendEXE, ".exe");
        }
    }
}

如果那不起作用,请确保项目针对 x86 CPU 并出于某种原因重建我没有检查需要什么才能使其同时适合 x64 和 x86

于 2013-06-22T00:29:09.617 回答