AllowAutoPlay никогда не вызывается
У меня есть приложение 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.
Я использовал следующие статьи: предотвращение автозапуска, ошибка 65536 и CodeProject.
Не отображается ни точка останова, ни окно сообщения.
Я работаю на Windows 10 с использованием Visual Studio 2017.
Мысли?
2 ответа
Мой первый ответ - технический ответ, который отвечает на конкретный вопрос, однако первый ответ НЕ решает проблему.
Я изо всех сил и, наконец, нашел реальное решение, которым я хотел поделиться.
Мое тестовое приложение с решением получает 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,
}
Ответ эксперта Exchange - это ответ, а именно
Автор комментария: 2003-07-30 Выше работает нормально, за исключением одного небольшого сбоя... потому что код работает только тогда, когда окно имеет фокус,
Ключевым элементом, который стоит отметить, является "окно". Оригинал, который я дал в своем вопросе, прекрасно работает только с одной формой. В моих основных приложениях есть несколько форм, упакованных вместе, поэтому, если какое-либо из них имеет фокус, код не будет работать.
Приведенный выше код и вариант WndProc полагаются на то, что Windows отправляет сообщение QueryCancelAutoPlay, которое встречается только в форме верхнего уровня, которая НЕ может быть той формой, о которой вы думаете.
Мои приложения сначала создают FrmMain, но помимо этого у меня есть различные дочерние формы. Только самая верхняя форма (окно) получает сообщение, а это означает, что для безопасности всем дочерним формам нужна одна из форм QueryCancelAutoPlay.