После проведения небольшого исследования ответ на все эти вопросы ... да!
Учитывая, что Microsoft заявляет, что все кусты частных приложений, загруженные из RegLoadAppKey , должны использовать исходный дескриптор, полученный при открытии улья, мне было очень любопытно, как Visual Studio добивалась этого, учитывая, что я нигде не видел ссылок на такой дескриптор, но когда я перешагнул через функции реестра в WinDbg, Process Monitor сообщил, что был осуществлен доступ к частному улью.
После перехода к этим функциям я обнаружил, что на самом деле Visual Studio обходит все функции реестра Win32 своему собственному специальному обработчику, который определяет, нужно ли перенаправлять вызов функции.
Таким образом, мы можем сделать вывод:
- Любые попытки доступа к разделам реестра Visual Studio будут автоматически перенаправлены в куст частного приложения Visual Studio, если требуется
Это по-прежнему оставляет нам проблему о необходимости создания ключа root Visual Studio в первую очередь. Как оказалось, вы можете использовать метод VSRegistry.RegistryRoot
, чтобы получить ссылку на различные хранилища конфигурации (хотя на самом деле поддерживаются только типы пользователей и конфигурации)
Итак, мы можем получить ссылку на конфигурацию пользователя key via
VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration);
Это по-прежнему оставляет нам проблему фактического создания целевого COM-объекта. Так получилось, что в Visual Studio есть API для этого: ILocalRegistry3 !
Если вы уже знаете CLSID объекта, который нужно sh создать, вы можете создать его прямо с помощью метода CreateInstance
. В моем случае каким-то образом GuidAttribute
на AD7Engine
, декомпилированном из Microsoft.VisualStudio.Debugger.VSCodeDebuggerHost.dll
, на самом деле не соответствовал CLSID, который был определен в разделе AD7Metrics
в реестре. Таким образом, я считаю более безопасным преобразовать GUID идентификатора ядра в CLSID COM-объекта AD7Engine, а затем создать экземпляр.
Используя этот метод, он также упрощает создание экземпляров отладчиков, указанных в класс Microsoft.VisualStudio.ProjectSystem.Debug.DebugEngines
.
private IDebugEngine2 VsCreateDebugEngine(Guid engineId)
{
var config = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration);
var subKeyPath = $"AD7Metrics\\Engine\\{{{engineId}}}";
var engineKey = config.OpenSubKey(subKeyPath);
if (engineKey == null)
throw new ArgumentException($"Could not find an AD7Engine for GUID '{engineId}'");
var clsid = engineKey.GetValue("CLSID")?.ToString();
if (clsid == null)
throw new InvalidOperationException($"GUID '{engineId}' does not have a CLSID value");
var obj = VsCreateComObject(new Guid(clsid));
return (IDebugEngine2) obj;
}
private object VsCreateComObject(Guid guid)
{
var localRegistry = (ILocalRegistry3)Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.GetService(typeof(SLocalRegistry));
Guid riid = VSConstants.IID_IUnknown;
IntPtr ptr;
var result = localRegistry.CreateInstance(
guid,
null,
ref riid,
(uint) Microsoft.VisualStudio.OLE.Interop.CLSCTX.CLSCTX_INPROC_SERVER,
out ptr
);
if (result != VSConstants.S_OK)
throw new ArgumentException($"Failed to retrieve GUID '{guid}', GUID may not exist");
var obj = Marshal.GetObjectForIUnknown(ptr);
return obj;
}
Вы можете легко перечислить все доступные движки с помощью следующего помощника
[DebuggerDisplay("Name = {Name}, EngineID = {EngineID}, {CLSID} = {CLSID}")]
class AD7Info
{
public string Name { get; set; }
public Guid EngineID { get; set; }
public Guid? CLSID { get; set; }
}
private List<AD7Info> GetAD7Infos()
{
var config = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration);
var engineKey = config.OpenSubKey("AD7Metrics\\Engine");
return engineKey.GetSubKeyNames().Select(n =>
{
var entryKey = engineKey.OpenSubKey(n);
var clsid = entryKey.GetValue("CLSID")?.ToString();
return new AD7Info
{
Name = entryKey.GetValue("Name")?.ToString(),
EngineID = new Guid(n),
CLSID = clsid != null ? new Guid(clsid) : (Guid?) null
};
}).ToList();
}
Пример:
//Retrieve the "managed only" debugging engine
var result = VsCreateDebugEngine(DebuggerEngines.ManagedOnlyEngine);
или в моем случае
//Retrieve the VSCodeDebuggerHost debug engine
var result = VsCreateDebugEngine(new Guid("2833D225-C477-4388-9353-544D168F6030"));
Я тестировал это на всех 38 двигателях, установленных на моей машине; большинству из них это удалось, возможно, те, которые потерпели неудачу, должны быть созданы через их ProgramProvider
или что-то в этом роде.
Ура!