简体   繁体   中英

How to programmatically restart windows explorer process

I'm working on a windows shell extension, and unfortunately, when making changes to the DLL, I must restart windows explorer (since it keeps the DLL in memory).

I found this program from Dino Esposito, but it doesn't work for me.

void SHShellRestart(void)
{
    HWND hwnd;
    hwnd = FindWindow("Progman", NULL );
    PostMessage(hwnd, WM_QUIT, 0, 0 );
    ShellExecute(NULL, NULL, "explorer.exe", NULL, NULL, SW_SHOW );
    return;
}

Does any one have something they can share to do this?

PS I realize that I can go to task manager and kill the explorer process, but I just want to do it the lazy way. Besides, this enables automation.

PPS I am using .NET for the development, but the shell restart functionality could be in C, C++ or a .NET language. It will simply be a small stand-alone executable.

After parsing some of the earlier answers and doing a bit of research, I've created a little complete example in C#. This closes the explorer shell then waits for it to completely shut down and restarts it. Hope this helps, there's a lot of interesting info in this thread.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;

namespace RestartExplorer
{
class Program
{
    [DllImport("user32.dll", SetLastError = true)]
    static extern bool PostMessage(IntPtr hWnd, [MarshalAs(UnmanagedType.U4)] uint Msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    const int WM_USER = 0x0400; //http://msdn.microsoft.com/en-us/library/windows/desktop/ms644931(v=vs.85).aspx

    static void Main(string[] args)
    {
        try
        {
            var ptr = FindWindow("Shell_TrayWnd", null);
            Console.WriteLine("INIT PTR: {0}", ptr.ToInt32());
            PostMessage(ptr, WM_USER + 436, (IntPtr)0, (IntPtr)0);

            do
            {
                ptr = FindWindow("Shell_TrayWnd", null);
                Console.WriteLine("PTR: {0}", ptr.ToInt32());

                if (ptr.ToInt32() == 0)
                {
                    Console.WriteLine("Success. Breaking out of loop.");
                    break;
                }

                Thread.Sleep(1000);
            } while (true);
        }
        catch (Exception ex)
        {
            Console.WriteLine("{0} {1}", ex.Message, ex.StackTrace);
        }
        Console.WriteLine("Restarting the shell.");
        string explorer = string.Format("{0}\\{1}", Environment.GetEnvironmentVariable("WINDIR"), "explorer.exe");
        Process process = new Process();           
        process.StartInfo.FileName = explorer;
        process.StartInfo.UseShellExecute = true;
        process.Start();

        Console.ReadLine();

    }
}
}

I noticed no one addressed the issue of starting explorer.exe as the shell, rather than it just opening an explorer window. Took me a while to figure this out, turns out it was something simple:

string explorer = string.Format("{0}\\{1}", Environment.GetEnvironmentVariable("WINDIR"), "explorer.exe");
        Process process = new Process();
        process.StartInfo.FileName = explorer;
        process.StartInfo.UseShellExecute = true;
        process.Start();

You have to set the StartInfo.UseshellExecute as true to get it to restart as the shell.

A fool-proof solution:

foreach (Process p in Process.GetProcesses())
{
    // In case we get Access Denied
    try
    {
        if (p.MainModule.FileName.ToLower().EndsWith(":\\windows\\explorer.exe"))
        {
            p.Kill();
            break;
        }
    }
    catch
    { }
}
Process.Start("explorer.exe");

FindWindow 之后使用 GetWindowThreadProcessId,然后是 OpenProcess,然后是 TerminateProcess。

After some more googling, I came up with the following C# solution:


using System.Diagnostics;
...
static public void RestartExplorer()
{
    foreach(Process p in Process.GetProcesses())  {
       if(p.MainModule.ModuleName.contains("explorer") == true)
         p.Kill();
    }
    Process.Start("explorer.exe");
}

This works for me on Vista:

DWORD dwPID;
HANDLE hExp;
HWND hSysTray = ::FindWindow (TEXT("Shell_TrayWnd"), NULL) ;
GetWindowThreadProcessId (hSysTray, &dwPID);
hExp = OpenProcess (PROCESS_TERMINATE, FALSE, dwPID);

if (hExp)
{
   TerminateProcess (hExp, 0);
}
Sleep (2000);
ShellExecute (NULL, NULL, TEXT("explorer.exe"), NULL, NULL, SW_HIDE);

But I can't find any way to suppress the explore window that opens (I tried, hence the SW_HIDE). On Vista, running explorer.exe without parameters seems to be the same as running "explorer.exe /e" on earlier systems. You'll have to try it for yourself on XP, I don't have it here.

Note: Using TerminateProcess does seem extreme, but posting a WM_CLOSE to explorer provokes a windows shutdown dialog.

public static void RestartExplorer()
{
    using(Process process = new Process())
    {
        //有些系统有tskill.exe可以直接调用tskill explorer命令
        process.StartInfo = new ProcessStartInfo
        {
            FileName = "taskkill.exe",
            Arguments = "-f -im explorer.exe",
            WindowStyle = ProcessWindowStyle.Hidden
        };
        process.Start();
        process.WaitForExit();
        process.StartInfo = new ProcessStartInfo("explorer.exe");
        process.Start();
    }
}

This is for Windows 7/8 (and need testing, maybe even works on Vista).

Since there is a proper way to close Explorer (progman) included in Windows 7 & 8 - by right clicking the taskbar (Shell_TrayWnd in Win8 or StartMenu on Win7) while pressing Ctrl-Shift , it shows in the popup menu a hidden option to close Explorer , and digging it using Spy++ it is triggered by message WM_USER+436 .

So I tested and doing the following it works great.

PostMessage(FindWindow('Shell_TrayWnd'),nil),WM_USER+436,0,0);

It closes Explorer, with all the opened instances. And to relaunch explorer, use the methods provided above.

So, please confirm in comments if this works on 32bit/64bit editions of your windows vista/7/8 or any other.

AC# solution that provides more certainty that the "right" explorer processes get killed.

using System;
using System.Diagnostics;

...............

public static void RestartExplorer()
 {
 const string explorer = "explorer.exe";
 string explorerPath = string.Format("{0}\\{1}", Environment.GetEnvironmentVariable("WINDIR"), explorer);
 foreach (Process process in Process.GetProcesses())
  {
  // In case we get Access Denied
  try
   {
   if (string.Compare(process.MainModule.FileName, explorerPath, StringComparison.OrdinalIgnoreCase) == 0)
    {
    process.Kill();
    }
   }
  catch
   {
   }
  }
 Process.Start(explorer);
 }

Rather than trying to restart the explorer, you can quickly refresh it, and it should have a similar effect.

[DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);

public static void RefreshExplorer()
{
    SHChangeNotify(0x08000000, 0x00000000, IntPtr.Zero, IntPtr.Zero);
}

Just call RefreshExplorer() and it will refresh the explorer.

SHChangeNotify accept eventId as first parameter. In above example, it notifies that file association is changed, but you can do it for specific event. All the eventId constants are mentioned in the docs here . You can find their values in magnumdb .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM