允许自动播放永远不会被调用

AllowAutoPlay Never Gets Called

我有一个 WinForms 应用程序。在 Program.cs 中创建实际表单之前,我实例化了一个 Autoplay class。注册成功,在强制性的第一个 return 值 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, and CodeProject

既没有断点也没有显示消息框。

我 运行 在 Windows 10 使用 Visual Studio 2017。

想法?

外汇达人response就是答案,即

dbtoth Author Commented: 2003-07-30 The above is working fine except for one small glitch... because the code only works when the window has focus,

值得注意的一个关键因素是 "window"。我在问题中提供的原件仅适用于一种形式。我的主要应用程序有几个表格打包在一起,所以如果其中任何一个有焦点,那么代码将无法运行。

上面的代码和 WndProc 变体依赖于 Windows 发送 QueryCancelAutoPlay 消息,这只发生在顶层窗体,它可能不是你想的窗体。

我的应用程序首先创建一个 FrmMain,但除此之外我还有各种子窗体。只有最顶层的表单 (window) 会收到消息,这意味着为了安全起见,所有子表单都需要 QueryCancelAutoPlay 的一种表单。

我的第一个答案是技术答案,它回答了具体问题,但是第一个答案没有解决问题。

千辛万苦,终于找到了真正的解决办法,分享给大家。

我的带有解决方案的测试应用程序确实收到了 QueryCancelAutoPlay 消息,但我的真实应用程序没有。我使用了 Windows SDK Inspect 实用程序,将 WndProc() 添加到每个表单,但什么也没有。

我也不喜欢只有活跃的 window 收到 QueryCancelAutoPlay 消息。如果用户恰好暂时切换到另一个应用程序,那么这种方法将不起作用。

我曾经开始沿着这里提到的答案的道路前进,但出于某种原因放弃了它。

我现在在设置区域中有 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,
}