Отделите сборку взаимодействия .NET COM от процесса ее размещения

StackOverflow https://stackoverflow.com/questions/1492869

Вопрос

Когда ActiveXObject размещен на рабочем столе / боковой панели Windows, этот ActiveXObject как бы кэшируется, а DLL-файл для него заблокирован (что означает, что его нельзя переместить, удалить или переименовать).Проблема заключается в следующем;когда гаджет впоследствии закрывается, библиотека DLL по-прежнему блокируется боковой панелью Windows, и ее невозможно удалить.Это вызывает серьезную проблему, из-за которой новая версия гаджета не может быть установлена поверх предыдущей версии гаджета, происходит сбой в процессе его удаления без каких-либо сообщений об ошибках.

Это не очень удобно для пользователя, поэтому я ищу способ каким-то образом "разорвать" связи с элементом управления ActiveX во время события выгрузки гаджета.Я надеюсь, что кто-нибудь сможет сказать мне, возможно ли это, и если да, то дать мне несколько идей о том, как это реализовать.

К вашему сведению, устройства боковой панели Windows на самом деле являются просто серверными окнами Internet Explorer, поэтому, вероятно, можно с уверенностью предположить, что IE демонстрирует такое же поведение.

Редактировать: Разблокировщик кажется, я делаю почти все, что мне нужно, так как же я могу добиться того же программно в .NET?

Это было полезно?

Решение

Ладно, это довольно сложная проблема.Я уже видел такое поведение раньше, я не знаком с гаджетом Windows Desktop / Sidebar, поскольку я им не пользуюсь.Однако мне удалось придумать три возможных метода атаки

1. Ручка из TechNet

Это была не моя идея, существует еще один поток StackOverflow который рекомендует этот метод.Однако я скептически отношусь к тому, сработает это или нет.Существует разница между блокировкой файла (то, что обрабатывает эта утилита) и блокировкой "загруженной библиотеки", которая, как я предполагаю, является проблемой, с которой вы сталкиваетесь с ActiveX.

Я немного изменил код из этого потока, там они используют Process .Kill() чтобы снять блокировку, я бы подумал, что лучше использовать handle.exe для снятия блокировки.

public struct LockInfo
{
    public int PID;
    public string Handle;

    public LockInfo(int pid, string handle)
    {
        this.PID = pid;
        this.Handle = handle;
    }
}      

static List<LockInfo> getLockingInfo(string fileName)
{            
    List<LockInfo> lockingProcesses = new List<LockInfo>();

    Process tool = new Process();
    tool.StartInfo.FileName = "handle.exe";
    tool.StartInfo.Arguments = fileName;
    tool.StartInfo.UseShellExecute = false;
    tool.StartInfo.RedirectStandardOutput = true;
    tool.Start();
    tool.WaitForExit();
    string outputTool = tool.StandardOutput.ReadToEnd();

    // I;m not so hot with regex, so a bit of regex and a bit of manual splitting
    string matchPattern = @"(?<=\s+pid:\s+)\b(\d+)\b(\s+)\b(\S+:)";
    foreach (Match match in Regex.Matches(outputTool, matchPattern))
    {
        string[] temp = match.Value.Replace(":", "").Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        if (temp.Length == 2)
        {
            lockingProcesses.Add(new LockInfo(int.Parse(temp[0].Trim()), temp[1].Trim()));
        }
    }
    return lockingProcesses.Count > 0 ? lockingProcesses : null;
}

static bool closeFileHandle(List<LockInfo> lockingInfo)
{
    if ((lockingInfo == null) || (lockingInfo.Count == 0))
    {
        throw new ArgumentException("lockingProcesses cannot be null or empty");
    }

    bool fileClosed = true;

    foreach (LockInfo lockInfo in lockingInfo)
    {
        Process tool = new Process();
        tool.StartInfo.FileName = "handle.exe";
        tool.StartInfo.Arguments = string.Format("-c {0} -y -p {1}", lockInfo.Handle, lockInfo.PID.ToString());
        tool.StartInfo.UseShellExecute = false;
        tool.StartInfo.RedirectStandardOutput = true;
        tool.Start();
        tool.WaitForExit();
        string outputTool = tool.StandardOutput.ReadToEnd();
        if (outputTool.IndexOf("Handle closed") == -1)
        {
            fileClosed = false;
        }
    }
    return fileClosed;
}

public static void Main()
{            
    //Path to locked file, make sure the full path is in quotes
    string fileName = "\"" + @"C:\Your_Path\To_The\ActiveX.ocx" + "\"";
    List<LockInfo> lockInfo = getLockingInfo(fileName);
    if ((lockInfo != null) && (lockInfo.Count > 0))
    {
        closeFileHandle(lockInfo);
    }                                 
}

...


2.Стиль Win32

В Интернете не так много информации об этом, и, похоже, есть несколько недокументированных вызовов api, которые необходимы для того, чтобы все прошло гладко.

I Эти примеры c ++ могли бы помочь.

К сожалению, я не смог заставить это работать без проблем.Я протестировал этот метод, используя ActiveX, загруженный в MS Word.Затем я попытался разблокировать ActiveX, но он не очень стабилен и часто приводил к сбою word.Я предполагаю, что у меня нет необходимых боевых навыков c ++, чтобы правильно расшифровать вышеупомянутые программы.

Наряду с этим примером CreateRemoteThread в C# Я действительно собрал этот код воедино.

public struct ProcessInfo
{
    public Process Process;
    public ProcessModule Module;

    public ProcessInfo(Process process, ProcessModule module)
    {
        this.Process = process;
        this.Module = module;
    }
}

private static List<ProcessInfo> getProcessInfo(string fileName, bool partialMatch)
{
    List<ProcessInfo> myProcesses = new List<ProcessInfo>();

    Process[] runningProcesses = Process.GetProcesses();
    int i = 0;
    for (i = 0; i < runningProcesses.Length; i++)
    {
        Process currentProcess = runningProcesses[i];
        try
        {
            if (!currentProcess.HasExited)
            {
                try
                {
                    ProcessModuleCollection modules = currentProcess.Modules;
                    int j = 0;
                    for (j = 0; j < modules.Count; j++)
                    {
                        if (partialMatch)
                        {
                            if ((modules[j].FileName.ToLower().IndexOf(fileName.ToLower()) != -1))
                            {
                                myProcesses.Add(new ProcessInfo(currentProcess, modules[j]));
                                break;
                            }
                        }
                        else
                        {
                            if ((modules[j].FileName.ToLower().CompareTo(fileName.ToLower()) == 0))
                            {
                                myProcesses.Add(new ProcessInfo(currentProcess, modules[j]));
                                break;
                            }
                        }
                    }
                }
                catch (NotSupportedException)
                {
                    // You are attempting to access the Modules property for a process that is running on a remote computer. 
                    // This property is available only for processes that are running on the local computer. 
                }
                catch (InvalidOperationException)
                {
                    // The process Id is not available.
                }
                catch (Win32Exception)
                {
                    // You are attempting to access the Modules property for either the system process or the idle process. 
                    // These processes do not have modules.
                }
            }
        }
        catch (InvalidOperationException)
        {
            // There is no process associated with the object. 
        }
        catch (Win32Exception)
        {
            // The exit code for the process could not be retrieved. 
        }
        catch (NotSupportedException)
        {
            // You are trying to access the HasExited property for a process that is running on a remote computer.
            // This property is available only for processes that are running on the local computer.

        }
    }
    return myProcesses.Count > 0 ? myProcesses : null;
}

private static void forceRemoteCloseHandle(ProcessInfo processInfo)
{
    // Open remote process for write
    IntPtr hProcess = NativeMethods.OpenProcess(NativeMethods.PROCESS_CREATE_THREAD | NativeMethods.PROCESS_VM_OPERATION |
            NativeMethods.PROCESS_VM_WRITE | NativeMethods.PROCESS_VM_READ, false, processInfo.Process.Id);

    // Get the handle to CloseHandle in kernel32.dll
    IntPtr hKernel32 = NativeMethods.LoadLibrary("kernel32.dll");
    IntPtr hCloseHandle = NativeMethods.GetProcAddress(hKernel32, "CloseHandle");
    uint temp = 0;

    // Create the remote thread and point it to CloseHandle
    IntPtr hCreateRemoteThread = NativeMethods.CreateRemoteThread((IntPtr)hProcess, (IntPtr)0, 0, hCloseHandle, (IntPtr)processInfo.Module.BaseAddress, 0, out temp);

    // Wait for thread to end
    NativeMethods.WaitForSingleObject(hCreateRemoteThread, 2000);

    //Closes the remote thread handle
    NativeMethods.CloseHandle(hCreateRemoteThread);

    //Free up the kernel32.dll
    if (hKernel32 != null)
        NativeMethods.FreeLibrary(hKernel32);

    //Close the process handle
    NativeMethods.CloseHandle(hProcess);
}

private static void forceRemoteFreeLibrary(ProcessInfo processInfo)
{
    // Open remote process for write
    IntPtr hProcess = NativeMethods.OpenProcess(NativeMethods.PROCESS_CREATE_THREAD | NativeMethods.PROCESS_VM_OPERATION |
            NativeMethods.PROCESS_VM_WRITE | NativeMethods.PROCESS_VM_READ, false, processInfo.Process.Id);

    // Get the handle to FreeLibrary in kernel32.dll
    IntPtr hKernel32 = NativeMethods.LoadLibrary("kernel32.dll");
    IntPtr hFreeHandle = NativeMethods.GetProcAddress(hKernel32, "FreeLibrary");

    // Create the remote thread and point it to FreeLibrary
    uint temp = 0;
    IntPtr hCreateRemoteThread = NativeMethods.CreateRemoteThread((IntPtr)hProcess, (IntPtr)0, 0, hFreeHandle, (IntPtr)processInfo.Module.BaseAddress, 0, out temp);

    // Wait for thread to end
    NativeMethods.WaitForSingleObject(hCreateRemoteThread, 2000);

    //Closes the remote thread handle
    NativeMethods.CloseHandle(hCreateRemoteThread);

    //Free up the kernel32.dll
    if (hKernel32 != null)
        NativeMethods.FreeLibrary(hKernel32);

    // Close the process handle
    NativeMethods.CloseHandle(hProcess);        
}

public static void Main()
{
    string strFile = @"C:\Program Files\Microsoft Office\OFFICE11\MSCAL.OCX";
    List<ProcessInfo> lockingProcesses = getProcessInfo(strFile, false);

    foreach (ProcessInfo processInfo in lockingProcesses)
    {
        forceRemoteCloseHandle(processInfo);
        // OR
        forceRemoteFreeLibrary(processInfo);
    }

    // OR
    foreach (ProcessInfo procInfo in lockingProcesses)
    {
        procInfo.Process.Kill();
    }

}

internal static class NativeMethods
{
    internal const int PROCESS_TERMINATE = 0x0001;
    internal const int PROCESS_CREATE_THREAD = 0x0002;
    internal const int PROCESS_VM_OPERATION = 0x0008;
    internal const int PROCESS_VM_READ = 0x0010;
    internal const int PROCESS_VM_WRITE = 0x0020;

    internal const int PROCESS_QUERY_INFORMATION = 0x0400;

    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    internal static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
    internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    internal static extern int WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);

    [DllImport("kernel32", SetLastError = true)]
    internal static extern IntPtr LoadLibrary(string lpFileName);


    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern bool FreeLibrary(IntPtr hModule);

    [DllImport("kernel32")]
    public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);

    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    internal static extern int CloseHandle(IntPtr hPass);
}

...


3.Просто используй Разблокировщик

Это моя лучшая рекомендация.Дескриптор из technet не может обрабатывать загруженные блокировки dll / ocx (в моих тестах).Win32 является беспорядочным и недокументированным.

Unlocker предоставляет доступ к командной строке, поэтому вы можете вызвать его точно так же, как handle.exe.Просто чокнутый /?после unlocker.exe в командной строке, чтобы увидеть переключатели.

Существует также доступна портативная версия Unlocker таким образом, вы можете включить его в свое развертывание, не заставляя конечных пользователей устанавливать приложение.

Если все остальное не поможет, вы можете связаться с автором Unlocker, ознакомьтесь с этим на его readme.

Лицензирование

Если вы заинтересованы в распространении Unlocker в оригинальной или модифицированной форме или желаете использовать исходный код Unlocker в продукте, пожалуйста, отправьте электронное письмо по адресу ccollomb@yahoo.com с подробной информацией.

...


4.Используйте общие библиотеки Process Hacker

Я только что открыл для себя этот блестящий инструмент: Процессный Хакер который написан на 100% коде C # (хотя он использует множество функций WinAPI через P / Invoke под капотом).

Самое лучшее в этом: это открытый исходный код (LGPL'd) и предоставляет две библиотеки, на которые разработчики могут ссылаться в своих решениях:ProcessHacker.Обычный ProcessHacker.Родной.

Я скачал исходный код и сразу предупреждаю, что это довольно сложное решение, поэтому может потребоваться немного времени, чтобы точно выяснить, что / как его использовать.

Он использует недокументированные функции API (ntdll.dl), о которых я говорил в варианте 2, и может делать все, что может Unlocker, и многое другое.

Другие советы

Не уверен, работает ли это для объектов ActiveX, но это определенно работает для .СЕТЕВЫЕ сборки.Я использую его для UnitTesting, где библиотеки DLL должны быть плавно перезаписаны.Используемый механизм является теневое копирование.

Закрытие дескрипторов извне приложения кажется на самом деле не очень безопасным вариантом.

Это не решение, может быть, идея или направление...

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top