Я разрабатываю расширение Visual Studio 2022 для извлечения объектов HBITMAP из памяти отлаженного процесса для анализа.
В идеале я хочу получить доступ к необработанным растровым данным и восстановить их напрямую, без необходимости конвертировать каждый формат HBITMAP в формат PNG.
Что я пробовал:
Дублирование дескриптора HBITMAP с помощью DuplicationHandle — но происходит сбой с ошибкой:
Дескриптор неверно.
Чтение данных HBITMAP с помощью ReadProcessMemory, но происходит сбой:
< blockquote>
Выполнена только часть запроса ReadProcessMemory или WriteProcessMemory.
Преобразование HBITMAP в PNG (работает, но дорого).
Мне удалось преобразовать HBITMAP в байтовый массив PNG и прочитать его в своем расширении, но это требует дополнительных затрат для каждого преобразования растрового изображения, чего я хочу избежать.
Вопрос:
Есть ли альтернативные подходы, которые я мог бы попробовать читать и восстанавливать HBITMAP напрямую из памяти отлаживаемого процесса?
Или существует более эффективный способ обработки и передачи данных HBITMAP между процессами, которые могли бы избежать преобразования в PNG?
-- для воспроизведения --
Источник расширения Visual Studio:
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using Task = System.Threading.Tasks.Task;
using System.ComponentModel;
namespace VSIX;
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[Guid(VSIXPackage.PackageGuidString)]
[ProvideAutoLoad(UIContextGuids.NoSolution, PackageAutoLoadFlags.BackgroundLoad)]
public sealed class VSIXPackage : AsyncPackage
{
public const string PackageGuidString = "f9a8aea3-f579-4816-9cb5-4ae3a5d68ef7";
private DebugWatcher _debugWatcher;
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
{
await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
_debugWatcher = new DebugWatcher();
}
}
public class DebugWatcher
{
private DTE _dte;
private DebuggerEvents _debuggerEvents;
public DebugWatcher()
{
_ = InitializeAsync();
}
private async Task InitializeAsync()
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
_dte = await ServiceProvider.GetGlobalServiceAsync(typeof(DTE)) as DTE;
if (_dte == null)
return;
_debuggerEvents = _dte.Events.DebuggerEvents;
_debuggerEvents.OnEnterBreakMode += OnEnterBreakMode;
}
[DllImport("kernel32.dll")]
private static extern uint GetLastError();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, bool bInheritHandle, uint dwOptions);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
private void OnEnterBreakMode(dbgEventReason Reason, ref dbgExecutionAction ExecutionAction)
{
ThreadHelper.ThrowIfNotOnUIThread();
int debuggedProcessId = _dte.Debugger.CurrentProcess.ProcessID;
const uint PROCESS_DUP_HANDLE = 0x0040;
const uint PROCESS_VM_READ = 0x0010;
const uint PROCESS_QUERY_INFORMATION = 0x0400;
var sourceProcessHandle = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, debuggedProcessId);
var targetProcess = System.Diagnostics.Process.GetCurrentProcess();
var targetProcessHandle = targetProcess.Handle;
if (sourceProcessHandle == null || targetProcessHandle == null)
return;
try
{
/*
--- Reading from HBITMAP ---- prints only: Name: unused, Type : , Value :
}
string address = hbm.Value.Replace("0x", "");
if (!long.TryParse(hbm.Value.Replace("0x", ""), System.Globalization.NumberStyles.HexNumber, null, out long hbmLong))
return;
IntPtr bitmapHandle = new IntPtr(hbmLong);
const int BITMAPINFOHEADER_SIZE = 40;
byte[] headerBuffer = new byte[BITMAPINFOHEADER_SIZE];
if (!ReadProcessMemory(sourceProcessHandle, bitmapHandle, headerBuffer, BITMAPINFOHEADER_SIZE, out var bytesReadHeader))
{
uint error = GetLastError();
Debug.WriteLine($"Error: {new Win32Exception((int)error).Message}"); // Error: Only part of a ReadProcessMemory or WriteProcessMemory request was completed
}
const uint DUPLICATE_SAME_ACCESS = 0x00000002;
bool success = DuplicateHandle(
sourceProcessHandle, // Source process (debugged process)
bitmapHandle, // The bitmap handle we want to duplicate
targetProcessHandle, // Target process (our VS extension)
out IntPtr duplicatedBitmapHandle, // Where the new handle will be stored
0, // Access (0 because we're using DUPLICATE_SAME_ACCESS)
false, // Don't inherit handle
DUPLICATE_SAME_ACCESS // Copy same access rights
);
if (!success)
{
uint error = GetLastError();
Debug.WriteLine($"Error: {new Win32Exception((int)error).Message}"); // Error: The handle is invalid
}
/*
--- Reading from std::vector ---- cleanups omitted for brevity
std::vector hBitmapToPngArray(HBITMAP hBitmap, const wchar_t* pngPath)
{
std::vector pngData;
IStream* pStream = nullptr;
if (FAILED(CreateStreamOnHGlobal(NULL, TRUE, &pStream)))
return pngData;
std::unique_ptr bitmap(new Gdiplus::Bitmap(hBitmap, NULL));
if (!bitmap || bitmap->GetLastStatus() != Gdiplus::Ok)
return pngData;
CLSID pngClsid;
UINT num = 0;
UINT size = 0;
GetImageEncodersSize(&num, &size);
if (size == 0)
return pngData;
std::vector buffer(size);
Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*)buffer.data();
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, L"image/png") == 0)
{
pngClsid = pImageCodecInfo[j].Clsid;
break;
}
}
Gdiplus::Status status = bitmap->Save(pStream, &pngClsid, NULL);
if (status != Gdiplus::Ok)
return pngData;
STATSTG statstg = { 0 };
if (FAILED(pStream->Stat(&statstg, STATFLAG_DEFAULT)))
return pngData;
LARGE_INTEGER seekPos = { 0 };
pStream->Seek(seekPos, STREAM_SEEK_SET, NULL);
pngData.resize(statstg.cbSize.LowPart);
ULONG bytesRead;
bitmap->Save(pngPath, &pngClsid, NULL);
pStream->Release();
return pngData;
}
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HWND hwnd = FindWindow(NULL, L"Untitled - Notepad");
if (!hwnd)
{
MessageBox(NULL, L"Window not found!", L"Error", MB_ICONERROR);
return 0;
}
RECT rc;
GetWindowRect(hwnd, &rc);
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;
HDC hdcWindow = GetDC(hwnd);
HDC hdcMemDC = CreateCompatibleDC(hdcWindow);
HBITMAP hbm = CreateCompatibleBitmap(hdcWindow, width, height);
SelectObject(hdcMemDC, hbm);
if (!PrintWindow(hwnd, hdcMemDC, PW_RENDERFULLCONTENT))
{
MessageBox(NULL, L"PrintWindow failed!", L"Error", MB_ICONERROR);
return 0;
}
std::vector pngData = hBitmapToPngArray(hbm, L"C:\\Users\\Cesar\\Downloads\\process.png");
while (true)
{
Sleep(100);
}
return 0;
}
Подробнее здесь: https://stackoverflow.com/questions/791 ... ed-process
Как получить доступ к данным HBITMAP и восстановить их из отлаженного процесса ⇐ C++
Программы на C++. Форум разработчиков
1730686238
Anonymous
Я разрабатываю расширение [b]Visual Studio 2022[/b] для извлечения объектов HBITMAP из памяти отлаженного процесса для анализа.
В идеале я хочу получить доступ к необработанным растровым данным и восстановить их напрямую, без необходимости конвертировать каждый формат HBITMAP в формат PNG.
Что я пробовал:
Дублирование дескриптора HBITMAP с помощью DuplicationHandle — но происходит сбой с ошибкой:
Дескриптор неверно.
Чтение данных HBITMAP с помощью ReadProcessMemory, но происходит сбой:
< blockquote>
Выполнена только часть запроса ReadProcessMemory или WriteProcessMemory.
Преобразование HBITMAP в PNG (работает, но дорого).
Мне удалось преобразовать HBITMAP в байтовый массив PNG и прочитать его в своем расширении, но это требует дополнительных затрат для каждого преобразования растрового изображения, чего я хочу избежать.
[b]Вопрос[/b]:
Есть ли альтернативные подходы, которые я мог бы попробовать читать и восстанавливать HBITMAP напрямую из памяти отлаживаемого процесса?
Или существует более эффективный способ обработки и передачи данных HBITMAP между процессами, которые могли бы избежать преобразования в PNG?
-- для воспроизведения --
Источник расширения Visual Studio:
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using Task = System.Threading.Tasks.Task;
using System.ComponentModel;
namespace VSIX;
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[Guid(VSIXPackage.PackageGuidString)]
[ProvideAutoLoad(UIContextGuids.NoSolution, PackageAutoLoadFlags.BackgroundLoad)]
public sealed class VSIXPackage : AsyncPackage
{
public const string PackageGuidString = "f9a8aea3-f579-4816-9cb5-4ae3a5d68ef7";
private DebugWatcher _debugWatcher;
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
{
await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
_debugWatcher = new DebugWatcher();
}
}
public class DebugWatcher
{
private DTE _dte;
private DebuggerEvents _debuggerEvents;
public DebugWatcher()
{
_ = InitializeAsync();
}
private async Task InitializeAsync()
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
_dte = await ServiceProvider.GetGlobalServiceAsync(typeof(DTE)) as DTE;
if (_dte == null)
return;
_debuggerEvents = _dte.Events.DebuggerEvents;
_debuggerEvents.OnEnterBreakMode += OnEnterBreakMode;
}
[DllImport("kernel32.dll")]
private static extern uint GetLastError();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, bool bInheritHandle, uint dwOptions);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
private void OnEnterBreakMode(dbgEventReason Reason, ref dbgExecutionAction ExecutionAction)
{
ThreadHelper.ThrowIfNotOnUIThread();
int debuggedProcessId = _dte.Debugger.CurrentProcess.ProcessID;
const uint PROCESS_DUP_HANDLE = 0x0040;
const uint PROCESS_VM_READ = 0x0010;
const uint PROCESS_QUERY_INFORMATION = 0x0400;
var sourceProcessHandle = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, debuggedProcessId);
var targetProcess = System.Diagnostics.Process.GetCurrentProcess();
var targetProcessHandle = targetProcess.Handle;
if (sourceProcessHandle == null || targetProcessHandle == null)
return;
try
{
/*
--- Reading from HBITMAP ---- prints only: Name: unused, Type : , Value :
}
string address = hbm.Value.Replace("0x", "");
if (!long.TryParse(hbm.Value.Replace("0x", ""), System.Globalization.NumberStyles.HexNumber, null, out long hbmLong))
return;
IntPtr bitmapHandle = new IntPtr(hbmLong);
const int BITMAPINFOHEADER_SIZE = 40;
byte[] headerBuffer = new byte[BITMAPINFOHEADER_SIZE];
if (!ReadProcessMemory(sourceProcessHandle, bitmapHandle, headerBuffer, BITMAPINFOHEADER_SIZE, out var bytesReadHeader))
{
uint error = GetLastError();
Debug.WriteLine($"Error: {new Win32Exception((int)error).Message}"); // Error: Only part of a ReadProcessMemory or WriteProcessMemory request was completed
}
const uint DUPLICATE_SAME_ACCESS = 0x00000002;
bool success = DuplicateHandle(
sourceProcessHandle, // Source process (debugged process)
bitmapHandle, // The bitmap handle we want to duplicate
targetProcessHandle, // Target process (our VS extension)
out IntPtr duplicatedBitmapHandle, // Where the new handle will be stored
0, // Access (0 because we're using DUPLICATE_SAME_ACCESS)
false, // Don't inherit handle
DUPLICATE_SAME_ACCESS // Copy same access rights
);
if (!success)
{
uint error = GetLastError();
Debug.WriteLine($"Error: {new Win32Exception((int)error).Message}"); // Error: The handle is invalid
}
/*
--- Reading from std::vector ---- cleanups omitted for brevity
std::vector hBitmapToPngArray(HBITMAP hBitmap, const wchar_t* pngPath)
{
std::vector pngData;
IStream* pStream = nullptr;
if (FAILED(CreateStreamOnHGlobal(NULL, TRUE, &pStream)))
return pngData;
std::unique_ptr bitmap(new Gdiplus::Bitmap(hBitmap, NULL));
if (!bitmap || bitmap->GetLastStatus() != Gdiplus::Ok)
return pngData;
CLSID pngClsid;
UINT num = 0;
UINT size = 0;
GetImageEncodersSize(&num, &size);
if (size == 0)
return pngData;
std::vector buffer(size);
Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*)buffer.data();
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, L"image/png") == 0)
{
pngClsid = pImageCodecInfo[j].Clsid;
break;
}
}
Gdiplus::Status status = bitmap->Save(pStream, &pngClsid, NULL);
if (status != Gdiplus::Ok)
return pngData;
STATSTG statstg = { 0 };
if (FAILED(pStream->Stat(&statstg, STATFLAG_DEFAULT)))
return pngData;
LARGE_INTEGER seekPos = { 0 };
pStream->Seek(seekPos, STREAM_SEEK_SET, NULL);
pngData.resize(statstg.cbSize.LowPart);
ULONG bytesRead;
bitmap->Save(pngPath, &pngClsid, NULL);
pStream->Release();
return pngData;
}
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HWND hwnd = FindWindow(NULL, L"Untitled - Notepad");
if (!hwnd)
{
MessageBox(NULL, L"Window not found!", L"Error", MB_ICONERROR);
return 0;
}
RECT rc;
GetWindowRect(hwnd, &rc);
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;
HDC hdcWindow = GetDC(hwnd);
HDC hdcMemDC = CreateCompatibleDC(hdcWindow);
HBITMAP hbm = CreateCompatibleBitmap(hdcWindow, width, height);
SelectObject(hdcMemDC, hbm);
if (!PrintWindow(hwnd, hdcMemDC, PW_RENDERFULLCONTENT))
{
MessageBox(NULL, L"PrintWindow failed!", L"Error", MB_ICONERROR);
return 0;
}
std::vector pngData = hBitmapToPngArray(hbm, L"C:\\Users\\Cesar\\Downloads\\process.png");
while (true)
{
Sleep(100);
}
return 0;
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79153998/how-to-access-and-reconstruct-hbitmap-data-from-a-debugged-process[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия