AllowAutoPlay никогда не вызывается - PullRequest
0 голосов
/ 02 октября 2018

У меня есть приложение WinForms.Непосредственно перед созданием реальной формы в Program.cs я создаю экземпляр класса Autoplay.Регистрация прошла успешно, после обязательного первого возвращаемого значения 65536, но я никогда не получаю никаких звонков на AllowAutoPlay().

Я что-то упускаю?

Вот код:

public class RunningObjectTableEntry : IDisposable
{
    private const int ROTFLAGS_REGISTRATIONKEEPSALIVE = 1;

    private HRESULT cookie;
    private IRunningObjectTable rot = null;
    private IMoniker monkey = null;

    private RunningObjectTableEntry() { }

    public RunningObjectTableEntry(object obj)
    {
        this.AddToROT(obj);
    }

    public void AddToROT(object obj)
    {
        int hr = GetRunningObjectTable(0, out rot);
        if (hr != 0)
        {
            throw new COMException("Could not retrieve running object table!", hr);
        }

        Guid clsid = obj.GetType().GUID;

        hr = CreateClassMoniker(ref clsid, out monkey);

        if (hr != 0)
        {
            Marshal.ReleaseComObject(rot);
            throw new COMException("Could not create moniker for CLSID/IID \"" + clsid + "\"!", hr);
        }

        UInt32 iResult = (UInt32)rot.Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, obj, monkey);   // Weak reference, but allow any user

        if (65536 == iResult)
            iResult = (UInt32)rot.Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, obj, monkey);

        cookie = (HRESULT)iResult;
    }

    public void RemoveFromROT()
    {
        if (cookie != 0)
        {
            try
            {
                // Get the running object table and revoke the cookie
                rot.Revoke((int)cookie);
                cookie = 0;
            }
            finally
            {
                if (rot != null) while (Marshal.ReleaseComObject(rot) > 0) ;
            }
        }
    }

    [DllImport("ole32.dll", ExactSpelling = true)]
    private static extern int GetRunningObjectTable([MarshalAs(UnmanagedType.U4)] int reserved, out IRunningObjectTable pprot);

    [DllImport("ole32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
    private static extern int CreateClassMoniker([In] ref Guid g, [Out] out IMoniker ppmk);

    #region IDisposable Members

    public void Dispose()
    {
        if (null != monkey)
            Marshal.ReleaseComObject(monkey);
        rot.Revoke((int)cookie);
        Marshal.ReleaseComObject(rot);
    }

    #endregion
}

[ComVisible(true)]
[Guid("331F1768-05A9-4ddd-B86E-DAE34DDC998A")]
[ClassInterface(ClassInterfaceType.None)]
public class Autoplay : IQueryCancelAutoPlay, IDisposable
{
    private RunningObjectTableEntry rotEntry;

    public Autoplay()
    {
        rotEntry = new RunningObjectTableEntry(this);
    }

    public void RemoveFromROT()
    {
        this.rotEntry?.RemoveFromROT();
    }
    #region IQueryCancelAutoPlay Members

    public int AllowAutoPlay(string pszPath, AutorunContent dwContentType, string pszLabel, int dwSerialNumber)
    {
        String msgUser = $"AllowAutoPlay: Path={pszPath}, ContentType={dwContentType.ToString()}, Label={pszLabel}, SerialNumber={dwSerialNumber.ToString()}";
        System.Diagnostics.Debug.WriteLine(msgUser);
        MessageBox.Show(msgUser);
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    {
        rotEntry.Dispose();
    }

    #endregion
}

Файл cookie при втором вызове - это нормально, непротиворечиво, но с разрешением 131073 или 0x00020001.

Я использовал следующие статьи: Prevent Autoplay , 65536 error и CodeProject .

Не отображается ни точка останова, ни окно сообщения.

Я работаю в Windows 10 с использованием Visual Studio 2017.

Мысли?

Ответы [ 2 ]

0 голосов
/ 03 октября 2018

Мой первый ответ - технический ответ, который отвечает на конкретный вопрос, однако первый ответ НЕ решает проблему.

Я боролся и, наконец, нашел реальное решение, которым я хотел бы поделиться.

Мое тестовое приложение с решением получает сообщение QueryCancelAutoPlay, а мое реальное приложение - нет.Я использовал утилиту Windows SDK Inspect, добавил WndProc() к каждой форме и ничего.

Мне также не нравится, когда только активное окно получает сообщение QueryCancelAutoPlay.Если пользователь случайно переключается на другое приложение, то этот метод разговора не будет работать.

Однажды я пошел по пути ответа, упомянутому здесь, и по какой-то причине отказался от него.

Iтеперь есть 2 элемента управления ComboBox в области настройки.Один содержит Windows по умолчанию, а другой - для приложения.Затем я устанавливаю приложение при запуске на версию приложения, а при выходе из приложения я сбрасываю на параметр Windows по умолчанию, который я сохранил в ComboBox.

Отлично работает.

private const String RegKey_UserChosen_StorageOnArrival = @"Software\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers\UserChosenExecuteHandlers\StorageOnArrival";
private const String RegKey_Event_StorageOnArrival = @"Software\Microsoft\Windows\CurrentVersion\Explorer\AutoplayHandlers\EventHandlersDefaultSelection\StorageOnArrival";
private const String RegValue_NoAction = @"MSTakeNoAction";
private const String RegValue_OpenFolder = @"MSOpenFolder";

public static Boolean SetExplorerAutoplay(String regValue)
{
    try
    {
        // Open first key needed.
        using (RegistryKey oKey = Registry.CurrentUser.OpenSubKey(ExplorerAutoplay.RegKey_UserChosen_StorageOnArrival, true))
        {
            // Set the default value. To set the default value do not use "(Default)", but rather leave blank.
            oKey.SetValue(String.Empty, regValue);
        }

        // Open second key needed.
        using (RegistryKey oKey = Registry.CurrentUser.OpenSubKey(ExplorerAutoplay.RegKey_Event_StorageOnArrival, true))
        {
            // Set the default value. To set the default value do not use "(Default)", but rather leave blank.
            oKey.SetValue(String.Empty, regValue);
        }

        return true;
    }

    catch (Exception)
    {
    }

    return false;
}

public static Boolean GetExplorerAutoplay(out AutoPlayDriveAction action, out String regValue)
{
    action = AutoPlayDriveAction.Invalid;
    regValue = null;
    try
    {
        // Only one of the keys is necessary, as both are the same.
        using (RegistryKey oKey = Registry.CurrentUser.OpenSubKey(ExplorerAutoplay.RegKey_UserChosen_StorageOnArrival, true))
        {
            // Get the default value.
            object oRegValue = oKey.GetValue(String.Empty);
            regValue = oRegValue?.ToString();
            if (true == regValue.Equals(ExplorerAutoplay.RegValue_NoAction))
                action = AutoPlayDriveAction.TakeNoAction;
            else if (true == regValue.Equals(ExplorerAutoplay.RegValue_OpenFolder))
                action = AutoPlayDriveAction.OpenFolder;
        }

        return true;
    }

    catch (Exception)
    {
    }

    return false;
}

public enum AutoPlayDriveAction
{
    Invalid,
    TakeNoAction,
    OpenFolder,
}
0 голосов
/ 02 октября 2018

Эксперт по обмену ответ - это ответ, а именно

dbtoth Автор прокомментировал: 2003-07-30 Выше работает нормально, за исключением одного небольшого сбоя ... потому чтокод работает только тогда, когда окно имеет фокус,

Ключевым элементом, который стоит отметить, является «окно».Оригинал, который я дал в своем вопросе, прекрасно работает только с одной формой.В моих основных приложениях есть несколько упакованных вместе форм, поэтому, если какое-либо из них имеет фокус, тогда код не будет работать.

Приведенный выше код и вариант WndProc полагаются на то, что Windows отправляет сообщение QueryCancelAutoPlay, которое происходит только дляформа верхнего уровня, которая НЕ может быть той формой, о которой вы думаете.

Мои приложения сначала создают FrmMain, но помимо этого у меня есть различные дочерние формы.Только самая верхняя форма (окно) получает сообщение, что означает, что для обеспечения безопасности всем дочерним формам нужна одна из форм QueryCancelAutoPlay.

...