How to preview GDI+ images on Image Watch?
Leo Purktz
0
Reputation points
I'm trying to visualize on memory GDI+ images using the Image Watch extension, i requested support on this feature long time ago directly on the developercommunity it got a lot of upvotes but MSFT just market it as "under review".
I tried to follow the documentation and implement the natvis myself, but i'm not suceeding on get it to work.
A reproducible example:
#include <windows.h>
#include <iostream>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib, "Gdiplus.lib")
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0;
UINT size = 0;
ImageCodecInfo* pImageCodecInfo = nullptr;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1;
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == nullptr)
return -1;
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return 1;
}
}
free(pImageCodecInfo);
return 0;
}
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
HDC hScreenDC = GetDC(nullptr);
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, screenWidth, screenHeight);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, 0, 0, screenWidth, screenHeight, hScreenDC, 0, 0, SRCCOPY);
Bitmap bitmap(hBitmap, nullptr);
CLSID clsid;
if (GetEncoderClsid(L"image/png", &clsid) == 1)
{
if (bitmap.Save(L"test.png", &clsid, nullptr) != Gdiplus::Ok)
std::wcerr << L"Failed to save the screenshot." << std::endl;
}
else
std::wcerr << L"Failed to get encoder CLSID." << std::endl;
// ...
// cleanups ignored as this code is just for debugging
}
And my current natvis implementation:
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<UIVisualizer ServiceId="{A452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1"
MenuName="Add to Image Watch"/>
<!-- GDI BITMAP -->
<Type Name="tagBITMAP">
<UIVisualizer ServiceId="{A452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
<Expand>
<Item Name="[width]">bmWidth</Item>
<Item Name="[height]">bmHeight</Item>
<Item Name="[stride]">bmWidthBytes</Item>
<Synthetic Name="[channels]">
<DisplayString Condition="bmBitsPixel == 8">L</DisplayString>
<DisplayString Condition="bmBitsPixel == 24">RGB</DisplayString>
<DisplayString Condition="bmBitsPixel == 32">RGBA</DisplayString>
</Synthetic>
<Synthetic Name="[type]">
<DisplayString>UINT8</DisplayString>
</Synthetic>
<Item Name="[data]">bmBits</Item>
</Expand>
</Type>
<!-- GDI HBITMAP -->
<Type Name="HBITMAP__">
<UIVisualizer ServiceId="{A452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
<Expand>
<CustomListItems>
<Variable Name="pBitmap" InitialValue="0" />
<Exec>pBitmap = (tagBITMAP*)alloca(sizeof(tagBITMAP))</Exec>
<Exec>GetObject((HBITMAP)this, sizeof(tagBITMAP), pBitmap)</Exec>
<Item Name="[width]">pBitmap->bmWidth</Item>
<Item Name="[height]">pBitmap->bmHeight</Item>
<Item Name="[stride]">pBitmap->bmWidthBytes</Item>
<Item Name="[channels]">
pBitmap->bmBitsPixel == 8 ? "L" :
pBitmap->bmBitsPixel == 24 ? "RGB" :
pBitmap->bmBitsPixel == 32 ? "RGBA" : "UNKNOWN"
</Item>
<Item Name="[type]">"UINT8"</Item>
<Item Name="[data]">pBitmap->bmBits</Item>
</CustomListItems>
</Expand>
</Type>
<!-- GDI+ Bitmap -->
<Type Name="Gdiplus::Bitmap">
<UIVisualizer ServiceId="{A452AFEA-3DF6-46BB-9177-C0B08F318025}" Id="1" />
<Expand>
<CustomListItems>
<Variable Name="width" InitialValue="0" />
<Variable Name="height" InitialValue="0" />
<Variable Name="stride" InitialValue="0" />
<Variable Name="pixelFormat" InitialValue="0" />
<Variable Name="scan0" InitialValue="0" />
<Exec>this->GetWidth(&width)</Exec>
<Exec>this->GetHeight(&height)</Exec>
<Exec>Gdiplus::BitmapData bitmapData;</Exec>
<Exec>Gdiplus::Rect rect(0, 0, width, height);</Exec>
<Exec>this->LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, &bitmapData)</Exec>
<Exec>stride = bitmapData.Stride</Exec>
<Exec>pixelFormat = bitmapData.PixelFormat</Exec>
<Exec>scan0 = bitmapData.Scan0</Exec>
<Exec>this->UnlockBits(&bitmapData)</Exec>
<Item Name="[width]">width</Item>
<Item Name="[height]">height</Item>
<Item Name="[stride]">stride</Item>
<Item Name="[channels]">
pixelFormat == PixelFormat24bppRGB ? "RGB" :
pixelFormat == PixelFormat32bppARGB ? "RGBA" :
pixelFormat == PixelFormat8bppIndexed ? "L" : "UNKNOWN"
</Item>
<Item Name="[type]">"UINT8"</Item>
<Item Name="[data]">scan0</Item>
</CustomListItems>
</Expand>
</Type>
</AutoVisualizer>
Using the natvis above the extension is detecting the local images but it always show them as invalid:
I'm testing on VS22/Image Watch for VS22.
Looking for any help on this issue.
Sign in to answer