将Windows 10应用添加到自动播放检测列表失败

时间:2018-06-21 19:21:31

标签: c# windows winforms com

我的WinForms应用程序通过USB与一个硬件对话,该USB已在其中嵌入了两个磁盘。我想防止Windows在我的设备的任何磁盘上显示通常的自动播放(在我的情况下弹出File Explorer)。

我从2009年发现this question,其中引用了this Microsoft article。我实现了该类,并首先从Programs入口点调用它,然后从我的主要WinForms表单类构造函数调用它。它不起作用。我调试了一下,发现从IRunningObjectTable.Register调用中得到-1(65536)。

我正在Visual Studio 2017(调试,X86)中进行测试,尽管我计划将其发布为X64,但两种模式都没有影响。这是故障点调试会话的屏幕截图。

Failure at the IRunningObjectTable.Register() call

这是我如何调用代码。 ComVisible的存在没有区别,只是我上次的调试尝试包括了这一点,因为本文的最后一篇文章提到了调用类应该拥有它。

[ComVisible(true)]
[Guid("9905b600-ee9a-4acf-bad0-5ae09698fec2")]
[ProgId("MyApp.Forms.FrmMain")]
public partial class FrmMain : XtraForm
{
    public FrmMain()
    {
        InitializeComponent();

        Autoplay oAutoplay = new Autoplay();
    }

此事件永远不会被调用。

public int AllowAutoPlay(string pszPath, AutorunContent dwContentType, string pszLabel, int dwSerialNumber)
{
    // This test is the name of my volume that should not call autoplay.
    if (true == "whatever1".Equals(pszLabel, StringComparison.CurrentCultureIgnoreCase) ||
        true == "whatever2".Equals(pszLabel, StringComparison.CurrentCultureIgnoreCase))
        return 1;
    else
        return 0;
}

为了完整起见,这是完整的代码。

[Flags]
public enum AutorunContent : int
{
    AutorunInf = 2,
    AudioCD = 4,
    DVDMovie = 8,
    BlankCD = 16,
    BlankDVD = 32,
    UnknownContent = 64,
    AutoPlayPictures = 128,
    AutoPlayMusics = 256,
    AutoPlayMovies = 512
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("DDEFE873-6997-4e68-BE26-39B633ADBE12")]
public interface IQueryCancelAutoPlay
{
    [PreserveSig]
    int AllowAutoPlay(
      [MarshalAs(UnmanagedType.LPWStr)] string pszPath,
      [MarshalAs(UnmanagedType.U4)] AutorunContent dwContentType,
      [MarshalAs(UnmanagedType.LPWStr)] string pszLabel,
      [MarshalAs(UnmanagedType.U4)] int dwSerialNumber);
}

enum HRESULT : uint
{
    S_FALSE = 0x0001,
    S_OK = 0x0000,
    E_INVALIDARG = 0x80070057,
    E_OUTOFMEMORY = 0x8007000E
}

/// <summary>
/// https://stackoverflow.com/questions/10831114/how-to-prevent-autoplay-and-run-my-own-app-when-inserting-an-usb-flash-drive
/// </summary>
public class RunningObjectTableEntry : IDisposable
{
    private readonly HRESULT cookie;
    private IRunningObjectTable rot = null;
    private readonly IMoniker monkey = null;

    private RunningObjectTableEntry() { }

    /// <summary>
    /// Creates a new entry for the given object
    /// </summary>
    /// <param name="obj">Object to make an entry for. Only one object per class should ever be registered.</param>
    public RunningObjectTableEntry(object obj)
    {
        int hr = GetRunningObjectTable(0, out rot);
        if (hr != 0)
        {
            throw new COMException("Could not retreive 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);
        }

        cookie = (HRESULT)rot.Register(0x01, obj, monkey);   // Weak reference, but allow any user
        switch (cookie)
        {
            case HRESULT.S_FALSE:
                break;
            case HRESULT.S_OK:
                break;
            case HRESULT.E_INVALIDARG:
                break;
            case HRESULT.E_OUTOFMEMORY:
                break;
            default:
                break;
        }
    }

    [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

    /// <summary>
    /// De-registers the object and class from the Running Object Table
    /// </summary>
    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);
    }

    #region IQueryCancelAutoPlay Members

    public int AllowAutoPlay(string pszPath, AutorunContent dwContentType, string pszLabel, int dwSerialNumber)
    {
        // This test is the name of my volume that should not call autoplay.
        if (true == "whatever1".Equals(pszLabel, StringComparison.CurrentCultureIgnoreCase) ||
            true == "whatever2".Equals(pszLabel, StringComparison.CurrentCultureIgnoreCase))
            return 1;
        else
            return 0;
        //Console.WriteLine("QueryCancelAutoPlay:");
        //Console.WriteLine("   " + pszPath);
        //Console.WriteLine("   " + dwContentType.ToString("x"));
        //Console.WriteLine("   " + pszLabel);
        //Console.WriteLine("   " + dwSerialNumber.ToString());
    }

    #endregion

    #region IDisposable Members

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

    #endregion
}

我添加了HRESULT枚举,只是为了查看调试期间的返回值。我从PinVoke中获得了定义。

NOTES & UPDATES

  1. 我遇到了this question,谈到IRunningObjectTable.Register返回65536。我刚刚验证了以下行为:第一个调用返回65536,但随后的调用返回的似乎是cookie句柄。不幸的是,我没有收到对AllowAutoPlay事件处理程序的调用。

  2. Code Project文章说了一些重要的事情,SO问题没有提及,我差点错过了,但遗憾的是它仍然不能解决我的问题,即开发人员必须:

    a)在.Net程序集上签名

    b)使用regasm.exe将.Net程序集注册为系统上的COM对象。语法为:

    "C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe" MyNetAssembly.exe /codebase /tlb

有想法吗?

0 个答案:

没有答案