1

我有一个 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 Autoplay65536 errorCodeProject

断点或消息框均不显示。

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

想法?

4

2 回答 2

1

Exchange Expert的回复就是答案,即

dbtoth 作者评论:2003-07-30 以上工作正常,除了一个小故障......因为代码只在窗口有焦点时工作,

一个值得注意的关键元素是“窗口”。我在问题中给出的原件仅适用于一种形式。我的主要应用程序有几个表单打包在一起,所以如果其中任何一个有焦点,那么代码将不起作用。

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

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

于 2018-10-02T17:57:17.127 回答
0

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

我苦苦挣扎,终于找到了一个真正的解决方案,我想分享一下。

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

我也不喜欢唯一的活动窗口获取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,
}
于 2018-10-03T20:04:45.027 回答