7

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 marked it as "under review".

I tried to follow the extension 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(&amp;width)</Exec>
                <Exec>this->GetHeight(&amp;height)</Exec>
                <Exec>Gdiplus::BitmapData bitmapData;</Exec>
                <Exec>Gdiplus::Rect rect(0, 0, width, height);</Exec>
                <Exec>this->LockBits(&amp;rect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, &amp;bitmapData)</Exec>
                <Exec>stride = bitmapData.Stride</Exec>
                <Exec>pixelFormat = bitmapData.PixelFormat</Exec>
                <Exec>scan0 = bitmapData.Scan0</Exec>
                <Exec>this->UnlockBits(&amp;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:

enter image description here

I'm testing on VS22/Image Watch for VS22.

Looking for any help on this issue.

1
  • 3
    Hi,@Carlos. I have reproduced your problem but I'm still looking for the cause. I'll get back to you if I find anything new. Commented Sep 19, 2024 at 9:42

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.