Как написать собственный DLL для визуализатора для Visual Studio 2012 отладчик?
Что нужно для написания собственной библиотеки DLL визуализатора на C++ для отладчика Visual Studio 2012? Я хочу отобразить значение, которое может быть вычислено только из класса / структуры по требованию, следовательно, требуется встроенная библиотека визуализатора. Visual Studio 2012 использует новый метод для реализации нативных визуализаторов под названием Natvis. На данный момент очень мало правильной информации о Natvis и особенно об использовании Natvis для вызова DLL-библиотеки визуализатора. DLL вычислит отображаемую строку на основе значений членов класса / структуры.
2 ответа
Вот код C++, который включает в себя DLL AddIn. Я назвал файл NatvisAddIn.cpp, и проект создал NatvisAddIn.dll.
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#define ADDIN_API __declspec(dllexport)
typedef struct tagDEBUGHELPER
{
DWORD dwVersion;
HRESULT (WINAPI *ReadDebuggeeMemory)( struct tagDEBUGHELPER *pThis, DWORD dwAddr, DWORD nWant, VOID* pWhere, DWORD *nGot );
// from here only when dwVersion >= 0x20000
DWORDLONG (WINAPI *GetRealAddress)( struct tagDEBUGHELPER *pThis );
HRESULT (WINAPI *ReadDebuggeeMemoryEx)( struct tagDEBUGHELPER *pThis, DWORDLONG qwAddr, DWORD nWant, VOID* pWhere, DWORD *nGot );
int (WINAPI *GetProcessorType)( struct tagDEBUGHELPER *pThis );
} DEBUGHELPER;
typedef HRESULT (WINAPI *CUSTOMVIEWER)( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved );
extern "C" ADDIN_API HRESULT MyClassFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved );
extern "C" ADDIN_API HRESULT MyStructFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved );
class MyClass
{
public:
int publicInt;
};
struct MyStruct { int i; };
ADDIN_API HRESULT MyClassFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved )
{
MyClass c;
DWORD nGot;
pHelper->ReadDebuggeeMemory(pHelper,dwAddress,sizeof(MyClass),&c,&nGot);
sprintf_s(pResult,max,"Dll MyClass: max=%d nGot=%d MyClass=%x publicInt=%d",max,nGot,dwAddress,c.publicInt);
return S_OK;
}
ADDIN_API HRESULT MyStructFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved )
{
MyStruct s;
DWORD nGot;
pHelper->ReadDebuggeeMemory(pHelper,dwAddress,sizeof(MyStruct),&s,&nGot);
sprintf_s(pResult,max,"Dll MyStruct: max=%d nGot=%d MyStruct=%x i=%d",max,nGot,dwAddress,s.i);
return S_OK;
}
Вот файл.natvis, который используется отладчиком Visual Studio 2012 для отображения значения. Поместите его в файл.natvis. Я назвал это NatvisAddIn.natvis. Этот файл указывает отладчику VS 2012 вызвать NatvisAddIn.dll. DLL содержит два вызова метода визуализатора; MyClassFormatter для форматирования MyClass и MyStructFormatter для форматирования MyStruct. Отладчик покажет отформатированное значение метода на экране Авто, Наблюдение или всплывающая подсказка для каждого экземпляра указанного типа (MyClass, MyStruct).
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="MyClass">
<DisplayString LegacyAddin="NatvisAddIn.dll" Export="MyClassFormatter"></DisplayString>
</Type>
<Type Name="MyStruct">
<DisplayString LegacyAddin="NatvisAddIn.dll" Export="MyStructFormatter"></DisplayString>
</Type>
</AutoVisualizer>
Поместите скомпилированный файл NatvisAddIn.dll и файлы NatvisAddIn.natvis в одно из следующих трех мест:
%VSINSTALLDIR%\Common7\Packages\Debugger\Visualizers (requires admin access)
%USERPROFILE%\My Documents\Visual Studio 2012\Visualizers\
VS extension folders
Вам нужно убедиться, что существует следующий раздел реестра и значение равно 1:
[HKEY_CURRENT_USER \ Software \ Microsoft \ VisualStudio \ 11.0_Config \ Debugger]
"EnableNatvisDiagnostics" = DWORD: 00000001
Если все пойдет хорошо, вы увидите сообщения natvis в окне вывода отладчика Visual Studio. В сообщениях будет показано, удалось ли Natvis проанализировать файлы.natvis. Результаты разбора каждого файла.natvis отображаются в окне вывода. Если что-то не так, используйте команду "dumpbin/exports ", чтобы дважды проверить, что имена методов DLL в точности соответствуют Type= файла.navis. Также убедитесь, что текущие файлы.dll и.natvis были скопированы в соответствующий каталог.
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\atlmfc.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\atlmfc.natvis.
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\concurrency.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\concurrency.natvis.
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\NatvisAddIn.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\NatvisAddIn.natvis.
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\stl.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\stl.natvis.
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\windows.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\windows.natvis.
Natvis: Parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\winrt.natvis.
Natvis: Done parsing natvis xml file: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\Packages\Debugger\Visualizers\winrt.natvis.
Тестовая программа:
#include "stdafx.h"
#include <iostream>
class MyClass
{
public:
int publicInt;
};
struct MyStruct { int i; };
int _tmain(int argc, _TCHAR* argv[])
{
struct MyStruct s = {1234};
std::cout << s.i << std::endl;
MyClass *c = new MyClass;
c->publicInt = 1234;
std::cout << c->publicInt << std::endl;
return 0;
}
Информационные ресурсы:
\ Xml \ Schemas \ natvis.xsd
http://code.msdn.microsoft.com/windowsdesktop/Writing-type-visualizers-2eae77a2
http://blogs.msdn.com/b/vcblog/archive/2012/07/12/10329460.aspx
Для отладки 64-битной версии следует использовать следующие строки:
auto realAddress = pHelper->GetRealAddress(pHelper);
pHelper->ReadDebuggeeMemoryEx(pHelper, realAddress, sizeof(MyClass), &c, &nGot );
Для предыдущего примера 64-битная версия может выглядеть так:
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#define ADDIN_API __declspec(dllexport)
typedef struct tagDEBUGHELPER
{
DWORD dwVersion;
HRESULT (WINAPI *ReadDebuggeeMemory)( struct tagDEBUGHELPER *pThis, DWORD dwAddr, DWORD nWant, VOID* pWhere, DWORD *nGot );
// from here only when dwVersion >= 0x20000
DWORDLONG (WINAPI *GetRealAddress)( struct tagDEBUGHELPER *pThis );
HRESULT (WINAPI *ReadDebuggeeMemoryEx)( struct tagDEBUGHELPER *pThis, DWORDLONG qwAddr, DWORD nWant, VOID* pWhere, DWORD *nGot );
int (WINAPI *GetProcessorType)( struct tagDEBUGHELPER *pThis );
} DEBUGHELPER;
typedef HRESULT (WINAPI *CUSTOMVIEWER)( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved );
extern "C" ADDIN_API HRESULT MyClassFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved );
class MyClass
{
public:
int publicInt;
};
ADDIN_API HRESULT MyClassFormatter( DWORD dwAddress, DEBUGHELPER *pHelper, int nBase, BOOL bUniStrings, char *pResult, size_t max, DWORD reserved )
{
MyClass c;
DWORD nGot;
auto realAddress = pHelper->GetRealAddress(pHelper);
pHelper->ReadDebuggeeMemoryEx(pHelper, realAddress, sizeof(MyClass), &c, &nGot );
sprintf_s(pResult,max,"Dll MyClass: max=%d nGot=%d MyClass=%llx publicInt=%d",max, nGot, realAddress, c.publicInt);
return S_OK;
}
Мне нужно одно разъяснение, связанное с путями поиска, которые ищутся для загрузки вышеупомянутого "NatvisAddIn.dll". Позвольте мне попытаться объяснить, расширив приведенный выше пример
Чтобы визуализировать мой пользовательский объект класса C++ (скажем, MyCustomeType), мне нужно вызвать некоторые дополнительные API (для вычисления отображаемой строки для MyCustomeType) в реализации функции (скажем, MyClassFormatter), которая упоминается в атрибуте "Export" объекта "DisplayString". XML.
Вызов этих дополнительных API создает зависимость библиотеки / DLL от вышеупомянутого "NatvisAddIn.dll". Если эта дополнительная зависимость - только одна или две библиотеки, я могу поместить эту библиотеку в то же место, где присутствует NatvisAddIn.dll. Однако в моем случае зависимость содержит длинную цепочку библиотек.
Кто-нибудь может мне предложить какой-нибудь элегантный способ разрешения зависимости без перетаскивания полной цепочки библиотек в папку%USERPROFILE%\My Documents\Visual Studio 2012\Visualizers?
Для демонстрации моего варианта использования давайте рассмотрим следующее дерево зависимостей: NatvisAddIn.dll a.dll
a1.dll a2.dll a3.dll
Я скопировал a.dll в то же место, где NatvisAddIn.dll. Это зависимые dll (a1, a2 и a3) присутствуют в местоположении, которые добавляются в переменную PATH. Когда я пытаюсь визуализировать объект MyCustomeType в отладчике Visual Studio, диагональная диаграмма natvis выдает следующую ошибку в окне вывода.
Natvis: C:\Users\myUser\Documents\Visual Studio 2017\Visualizers\mydata.natvis(9,6): Ошибка: не удалось загрузить надстройку из C:\Users\myuser\Documents\Visual Studio 2017\Visualizers\NatvisAddIn.dll для типа MyCustomeType::: указанный модуль не найден.
В моем понимании вышеуказанной ошибки визуальный отладчик студии не смог разрешить зависимость a.dll (a1, a2 и a3) и, следовательно, не смог загрузить NatvisAddIn.dll.
Когда я пытался использовать a.dll в своем тестовом приложении и вычислять DisplayString для MyCustomeType, разрешается получение зависимости, загружается a.dll, и я получаю ожидаемую строку без копирования a1.dll, a2.dll и a3.dll. зависимые dll разрешаются / выбираются из переменной PATH окна. Но, в случае визуального студийного отладчика, зависимые DLL НЕ разрешаются из переменной PATH.
Как определено средством зависимости, некоторые dll, которые не разрешаются отладчиком:
api-ms-win-core-errorhandling-l1-1-0.dll api-ms-win-crt-time-l1-1-0.dll api-ms-win-crt-heap-l1-1-0. длл
Некоторые из этих dll присутствуют в Visual Studio, другие находятся в c:\windows\WinSxS
Я попробовал свой сценарий использования на Visual Studio 2012 и Visual Studio 2017. Я столкнулся с одной и той же проблемой с обеими визуальными студиями.