I have a WinForms application. Just before creating the actual form in Program.cs, I instantiate an Autoplay class. Registration is successful, after the obligatory first return value of 65536, but I never get any calls to AllowAutoPlay().
Am I missing something?
Here is the code:
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
}
The cookie on the second call is fine, consistent, but fine at 131073 or 0x00020001.
I used the following articles: Prevent Autoplay, 65536 error, and CodeProject.
Neither a breakpoint or the message box shows.
I am running on Windows 10 using Visual Studio 2017.
Thoughts?
My first answer is the technical answer, which answers the specific question, however the first answer does NOT address the problem.
I struggled and finally found a real solution, which I wanted to share.
My test application with the solution DOES receive the
QueryCancelAutoPlaymessage, however my real application does NOT. I used theWindows SDK Inspectutility, added theWndProc()to every form and nothing.I also do not like the only the active window gets
QueryCancelAutoPlaymessage. If the user happens to shift to another application momentarily, then this talked method will not work.I once started down the path of the answer mentioned here and for whatever reason abandoned it.
I now have 2 ComboBox controls in the setup area. One holds the Windows default, while the other is for the application. I then set the application upon launch to the application version, and upon application exit I reset to the Windows default option, which I stored in the ComboBox.
Works great.