Próbuję pisać, używając metody wtrysku DLL, aplikacji, która wyświetla mapy bitowe używane przez inny program i chcę uzyskać dla tej specyficznej listy procesów uchwytów GDI, których używa (lista w narzędziu GDIView.exe). Znalazłem artykuł o funkcji NtQuerySystemInformation, ale ten opis działa tylko z uchwytami do Kernel Objects. Czy ktoś może pomóc?Jak uzyskać listę uchwytów GDI
Odpowiedz
Oto kod aplikacji konsoli, który zrzuca wszystkie uchwyty GDI dla danego identyfikatora procesu. Powinien się skompilować i działać poprawnie w przypadku aplikacji 32- lub 64-bitowych, a także 32-bitowej aplikacji działającej w 64-bitowych systemach operacyjnych. Korzysta z wielu nieudokumentowanych funkcji, więc nie należy polegać na tym :-) Kredyty na strukturach dla wspólnej tabeli GDI idą do oryginalnej pracy z Feng Yuan. (Musiałem to zaadaptować na WOW64).
Oto przykładowe dane wyjściowe po ran na schowek (64 bity) procesu:
[C:\Temp\EnumGdi\Debug]EnumGdi.exe 5916
DC handle:0xF20105DB
Bitmap handle:0xDF05118B
Font handle:0xDC0A19E0
Font handle:0xAB0A1A62
DC handle:0xA3011A63
Region handle:0xAF041B7B
Brush handle:0x11101C5B
Font handle:0x280A1CA1
Font handle:0xBB0A1D13
Bitmap handle:0xA3051DD8
Font handle:0xB40A1DDC
Region handle:0x3A041EE4
Brush handle:0x0B101F04
Region handle:0xC6041F3D
Font handle:0x2C0A2384
Brush handle:0xBA1024DA
EnumGdi.cpp:
#include "stdafx.h"
#include "enumgdi.h"
int _tmain(int argc, _TCHAR* argv[])
{
if (argc < 2)
{
printf("Format is EnumGdi <process id>\n");
return 0;
}
// get process identifier
DWORD dwId = _wtoi(argv[1]);
// open the process
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwId);
DWORD err = 0;
if (hProcess == NULL)
{
printf("OpenProcess %u failed\n", dwId);
err = GetLastError();
return -1;
}
// determine if 64 or 32-bit processor
SYSTEM_INFO si;
GetNativeSystemInfo(&si);
// NOTE: as this is undocumented, it *may vary* depending on bitness (32/64) and on Windows version.
// use WinDbg "dt ntdll!_PEB" command and search for GdiSharedHandleTable offset to find the truth out
DWORD GdiSharedHandleTableOffset = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 0xF8 : 0x94;
DWORD tableCount = 16384; // count of GDI table cells
// determine if this process is running on WOW64
BOOL wow;
IsWow64Process(GetCurrentProcess(), &wow);
// read basic info to get PEB address, we only need the beginning of PEB
DWORD pebSize = GdiSharedHandleTableOffset + 8;
LPBYTE peb = (LPBYTE)malloc(pebSize);
ZeroMemory(peb, pebSize);
if (wow)
{
// we're running as a 32-bit process in a 64-bit process
PROCESS_BASIC_INFORMATION_WOW64 pbi;
ZeroMemory(&pbi, sizeof(pbi));
// get process information from 64-bit world
_NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64QueryInformationProcess64");
err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
if (err != 0)
{
printf("NtWow64QueryInformationProcess64 failed\n");
CloseHandle(hProcess);
return -1;
}
// read PEB from 64-bit address space
_NtWow64ReadVirtualMemory64 read = (_NtWow64ReadVirtualMemory64)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64ReadVirtualMemory64");
err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
if (err != 0)
{
printf("NtWow64ReadVirtualMemory64 PEB failed\n");
CloseHandle(hProcess);
return -1;
}
// get GDI table ptr from PEB
GDICELL_WOW64* gdiTable = (GDICELL_WOW64*)*(LPVOID*)(peb + GdiSharedHandleTableOffset); // address in remote process adress space
if (gdiTable == NULL)
{
printf("GDI32.DLL is not loaded in the process\n");
CloseHandle(hProcess);
return -1;
}
free(peb);
DWORD tableSize = sizeof(GDICELL_WOW64) * tableCount; // size of GDI table
GDICELL_WOW64* table = (GDICELL_WOW64*)malloc(tableSize); // local table copied over to our address space
// copy GDI table
err = read(hProcess, gdiTable, table, tableSize, NULL);
if (err != 0)
{
printf("NtWow64ReadVirtualMemory64 GdiTable failed\n");
free(table);
CloseHandle(hProcess);
return -1;
}
for(DWORD i = 0; i < tableCount; i++)
{
GDICELL_WOW64 cell = table[i];
if (cell.wProcessId != dwId)
continue;
HGDIOBJ gdiHandle = (HGDIOBJ)((cell.wUpper << 16) + i);
WORD type = cell.wType & 0x7F;
switch(type)
{
case 1:
printf("DC handle:0x%08X\n", gdiHandle);
break;
case 4:
printf("Region handle:0x%08X\n", gdiHandle);
break;
case 5:
printf("Bitmap handle:0x%08X\n", gdiHandle);
break;
case 8:
printf("Palette handle:0x%08X\n", gdiHandle);
break;
case 10:
printf("Font handle:0x%08X\n", gdiHandle);
break;
case 16:
printf("Brush handle:0x%08X\n", gdiHandle);
break;
case 48:
printf("Pen handle:0x%08X\n", gdiHandle);
break;
default:
printf("Unknown type handle:0x%08X\n", gdiHandle);
break;
}
}
free(table);
}
else
{
// we're running as a 32-bit process in a 32-bit OS, or as a 64-bit process in a 64-bit OS
PROCESS_BASIC_INFORMATION pbi;
ZeroMemory(&pbi, sizeof(pbi));
// get process information
_NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
if (err != 0)
{
printf("NtQueryInformationProcess failed\n");
CloseHandle(hProcess);
return -1;
}
// read PEB
_NtReadVirtualMemory read = (_NtReadVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtReadVirtualMemory");
err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
if (err != 0)
{
printf("NtReadVirtualMemory PEB failed\n");
CloseHandle(hProcess);
return -1;
}
// get GDI table ptr
GDICELL* gdiTable = (GDICELL*)*(LPVOID*)(peb + GdiSharedHandleTableOffset); // address in remote process adress space
if (gdiTable == NULL)
{
printf("GDI32.DLL is not loaded in the process\n");
CloseHandle(hProcess);
return -1;
}
free(peb);
DWORD tableSize = sizeof(GDICELL) * tableCount; // size of GDI table
GDICELL* table = (GDICELL*)malloc(tableSize); // local table copied over to our address space
// read GDI table
err = read(hProcess, gdiTable, table, tableSize, NULL);
if (err != 0)
{
printf("NtReadVirtualMemory GdiTable failed\n");
free(table);
CloseHandle(hProcess);
return -1;
}
for(DWORD i = 0; i < tableCount; i++)
{
GDICELL cell = table[i];
if (cell.wProcessId != dwId)
continue;
HGDIOBJ gdiHandle = (HGDIOBJ)((cell.wUpper << 16) + i);
WORD type = cell.wType & 0x7F;
switch(type)
{
case 1:
printf("DC handle:0x%08X\n", gdiHandle);
break;
case 4:
printf("Region handle:0x%08X\n", gdiHandle);
break;
case 5:
printf("Bitmap handle:0x%08X\n", gdiHandle);
break;
case 8:
printf("Palette handle:0x%08X\n", gdiHandle);
break;
case 10:
printf("Font handle:0x%08X\n", gdiHandle);
break;
case 16:
printf("Brush handle:0x%08X\n", gdiHandle);
break;
case 48:
printf("Pen handle:0x%08X\n", gdiHandle);
break;
default:
printf("Unknown type handle:0x%08X\n", gdiHandle);
break;
}
}
free(table);
}
CloseHandle(hProcess);
}
EnumGdi.h:
#pragma once
#include "stdafx.h"
// defines a GDI CELL
typedef struct
{
LPVOID pKernelAddress;
USHORT wProcessId;
USHORT wCount;
USHORT wUpper;
USHORT wType;
LPVOID pUserAddress;
} GDICELL;
// defines a GDI CELL for WOW64
typedef struct
{
PVOID64 pKernelAddress;
USHORT wProcessId;
USHORT wCount;
USHORT wUpper;
USHORT wType;
PVOID64 pUserAddress;
} GDICELL_WOW64;
// NtQueryInformationProcess for pure 32 and 64-bit processes
typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
IN HANDLE ProcessHandle,
ULONG ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
typedef NTSTATUS (NTAPI *_NtReadVirtualMemory)(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
OUT PVOID Buffer,
IN SIZE_T Size,
OUT PSIZE_T NumberOfBytesRead);
// NtQueryInformationProcess for 32-bit process on WOW64
typedef NTSTATUS (NTAPI *_NtWow64ReadVirtualMemory64)(
IN HANDLE ProcessHandle,
IN PVOID64 BaseAddress,
OUT PVOID Buffer,
IN ULONG64 Size,
OUT PULONG64 NumberOfBytesRead);
// PROCESS_BASIC_INFORMATION for pure 32 and 64-bit processes
typedef struct _PROCESS_BASIC_INFORMATION {
PVOID Reserved1;
PVOID PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId;
PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
// PROCESS_BASIC_INFORMATION for 32-bit process on WOW64
// The definition is quite funky, as we just lazily doubled sizes to match offsets...
typedef struct _PROCESS_BASIC_INFORMATION_WOW64 {
PVOID Reserved1[2];
PVOID64 PebBaseAddress;
PVOID Reserved2[4];
ULONG_PTR UniqueProcessId[2];
PVOID Reserved3[2];
} PROCESS_BASIC_INFORMATION_WOW64;
Prawdopodobnie powinieneś udokumentować, jakie wersje systemu Windows działają. –
Do tej pory wiadomo, że działa w systemie Windows 7, jedynym, który mam pod ręką :-). Ale został zaprojektowany do pracy na XP i wyżej. –
Najpierw musisz zdefiniować, co masz na myśli dokładnie według "uchwytów GDI" i dlaczego musisz o nich wiedzieć, ponieważ istnieją różne rodzaje uchwytów.
Technicznie są tam 3 główne rodzaje uchwytów:
- Kernel uchwytami. Przykłady: uchwyty plików, obiekty synchronizacji (zdarzenia, muteksy itp.), Odwzorowania plików itp.
- Uchwyty użytkownika. Przykłady:
HWND
,HDC
,HICON
, uchwyty pulpitu itp. - Uchwyty GDI. Przykłady:
HBITMAP
,HGDIOBJ
(podtypy toHRGN
,HPEN
itd.).
W szczególności niektórzy ludzie mylą użytkownika z uchwytami GDI. Nie każdy wie, że HDC
, który jest używany do rysowania, jest w rzeczywistości , a nie uchwytem GDI.
Z punktu widzenia wdrożenia istnieje duża różnica między uchwytami użytkownika i GDI. Uchwyty użytkowników są systemowe i zarządzane w jądrze. W związku z tym istnieje możliwość zebrania wszystkich informacji o nich w trybie jądra. Uchwyty OTOH GDI są specyficzne dla procesu. Niektóre obiekty GDI są zarządzane wyłącznie w trybie użytkownika (np. Regiony i DIB).
Jestem szczególnie zainteresowany HBITMAP. GDIView wskazuje adres jądra dla tego typu uchwytu, więc myślę, że jest on zarządzany w trybie jądra. – cadaver
Compilable and Buildable
//Lets name it GDIInquiry.cpp
#pragma once
#include "stdafx.h"
#include "StdAfx.h"
#include <tchar.h>
#include <stdio.h>
#include <malloc.h>
#include <dbghelp.h>
#include <shlwapi.h>
#include <ShlObj.h>
#include "GDIInquiry.h"
int _tmain(int argc, _TCHAR* argv[])
{
if (argc < 2)
{
printf("Format is EnumGdi <process id>\n");
system("pause");
return 0;
}
// get process identifier
DWORD dwId = _wtoi(argv[1]);
// open the process
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwId);
DWORD err = 0;
if (hProcess == NULL)
{
printf("OpenProcess %u failed\n", dwId);
err = GetLastError();
system("pause");
return -1;
}
// determine if 64 or 32-bit processor
SYSTEM_INFO si;
GetNativeSystemInfo(&si);
// NOTE: as this is undocumented, it *may vary* depending on bitness (32/64) and on Windows version.
// use WinDbg "dt ntdll!_PEB" command and search for GdiSharedHandleTable offset to find the truth out
DWORD GdiSharedHandleTableOffset = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 0xF8 : 0x94;
DWORD tableCount = 16384; // count of GDI table cells
// determine if this process is running on WOW64
BOOL wow;
IsWow64Process(GetCurrentProcess(), &wow);
// read basic info to get PEB address, we only need the beginning of PEB
DWORD pebSize = GdiSharedHandleTableOffset + 8;
LPBYTE peb = (LPBYTE)malloc(pebSize);
ZeroMemory(peb, pebSize);
int nDCHandle, nRegionHandle, nBitmapHandle, nPaletteHandle, nFontHandle, nPenHandle, nBrushHandle, nOtherHandle;
nDCHandle = nRegionHandle = nBitmapHandle = nPaletteHandle = nFontHandle = nPenHandle = nBrushHandle = nOtherHandle = 0;
if (wow)
{
// we're running as a 32-bit process in a 64-bit process
PROCESS_BASIC_INFORMATION_WOW64 pbi;
ZeroMemory(&pbi, sizeof(pbi));
// get process information from 64-bit world
_NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64QueryInformationProcess64");
err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
if (err != 0)
{
printf("NtWow64QueryInformationProcess64 failed\n");
CloseHandle(hProcess);
system("pause");
return -1;
}
// read PEB from 64-bit address space
_NtWow64ReadVirtualMemory64 read = (_NtWow64ReadVirtualMemory64)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64ReadVirtualMemory64");
err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
if (err != 0)
{
printf("NtWow64ReadVirtualMemory64 PEB failed\n");
CloseHandle(hProcess);
system("pause");
return -1;
}
// get GDI table ptr from PEB
GDICELL_WOW64* gdiTable = (GDICELL_WOW64*)*(LPVOID*)(peb + GdiSharedHandleTableOffset); // address in remote process adress space
if (gdiTable == NULL)
{
printf("GDI32.DLL is not loaded in the process\n");
CloseHandle(hProcess);
system("pause");
return -1;
}
free(peb);
DWORD tableSize = sizeof(GDICELL_WOW64)* tableCount; // size of GDI table
GDICELL_WOW64* table = (GDICELL_WOW64*)malloc(tableSize); // local table copied over to our address space
// copy GDI table
err = read(hProcess, gdiTable, table, tableSize, NULL);
if (err != 0)
{
printf("NtWow64ReadVirtualMemory64 GdiTable failed\n");
free(table);
CloseHandle(hProcess);
system("pause");
return -1;
}
for (DWORD i = 0; i < tableCount; i++)
{
GDICELL_WOW64 cell = table[i];
if (cell.wProcessId != dwId)
continue;
HGDIOBJ gdiHandle = (HGDIOBJ)((cell.wUpper << 16) + i);
WORD type = cell.wType & 0x7F;
switch (type)
{
case 1:
//printf("DC handle:0x%08X\n", gdiHandle);
nDCHandle++;
break;
case 4:
//printf("Region handle:0x%08X\n", gdiHandle);
nRegionHandle++;
break;
case 5:
//printf("Bitmap handle:0x%08X\n", gdiHandle);
nBitmapHandle++;
break;
case 8:
//printf("Palette handle:0x%08X\n", gdiHandle);
nPaletteHandle++;
break;
case 10:
//printf("Font handle:0x%08X\n", gdiHandle);
nFontHandle++;
break;
case 16:
//printf("Brush handle:0x%08X\n", gdiHandle);
nPenHandle++;
break;
case 48:
//printf("Pen handle:0x%08X\n", gdiHandle);
nBrushHandle++;
break;
default:
//printf("Unknown type handle:0x%08X\n", gdiHandle);
nOtherHandle++;
break;
}
}
free(table);
}
else
{
// we're running as a 32-bit process in a 32-bit OS, or as a 64-bit process in a 64-bit OS
PROCESS_BASIC_INFORMATION pbi;
ZeroMemory(&pbi, sizeof(pbi));
// get process information
_NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
if (err != 0)
{
printf("NtQueryInformationProcess failed\n");
CloseHandle(hProcess);
system("pause");
return -1;
}
// read PEB
_NtReadVirtualMemory read = (_NtReadVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtReadVirtualMemory");
err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
if (err != 0)
{
printf("NtReadVirtualMemory PEB failed\n");
CloseHandle(hProcess);
system("pause");
return -1;
}
// get GDI table ptr
GDICELL* gdiTable = (GDICELL*)*(LPVOID*)(peb + GdiSharedHandleTableOffset); // address in remote process adress space
if (gdiTable == NULL)
{
printf("GDI32.DLL is not loaded in the process\n");
CloseHandle(hProcess);
system("pause");
return -1;
}
free(peb);
DWORD tableSize = sizeof(GDICELL)* tableCount; // size of GDI table
GDICELL* table = (GDICELL*)malloc(tableSize); // local table copied over to our address space
// read GDI table
err = read(hProcess, gdiTable, table, tableSize, NULL);
if (err != 0)
{
printf("NtReadVirtualMemory GdiTable failed\n");
free(table);
CloseHandle(hProcess);
system("pause");
return -1;
}
for (DWORD i = 0; i < tableCount; i++)
{
GDICELL cell = table[i];
if (cell.wProcessId != dwId)
continue;
HGDIOBJ gdiHandle = (HGDIOBJ)((cell.wUpper << 16) + i);
WORD type = cell.wType & 0x7F;
switch (type)
{
case 1:
//printf("DC handle:0x%08X\n", gdiHandle);
nDCHandle++;
break;
case 4:
//printf("Region handle:0x%08X\n", gdiHandle);
nRegionHandle++;
break;
case 5:
//printf("Bitmap handle:0x%08X\n", gdiHandle);
nBitmapHandle++;
break;
case 8:
//printf("Palette handle:0x%08X\n", gdiHandle);
nPaletteHandle++;
break;
case 10:
//printf("Font handle:0x%08X\n", gdiHandle);
nFontHandle++;
break;
case 16:
//printf("Brush handle:0x%08X\n", gdiHandle);
nPenHandle++;
break;
case 48:
//printf("Pen handle:0x%08X\n", gdiHandle);
nBrushHandle++;
break;
default:
//printf("Unknown type handle:0x%08X\n", gdiHandle);
nOtherHandle++;
break;
}
}
free(table);
}
CloseHandle(hProcess);
int nTotalGDI = nDCHandle + nRegionHandle + nBitmapHandle + nPaletteHandle + nFontHandle + nPenHandle + nBrushHandle + nOtherHandle;
printf("Bitmap:%d\n", nBitmapHandle);
printf("Brush:%d\n", nPenHandle);
printf("DeviceContext:%d\n", nDCHandle);
printf("Font:%d\n", nFontHandle);
printf("Palette:%d\n", nPaletteHandle);
printf("Pen:%d\n", nBrushHandle);
printf("Region:\%d\n", nRegionHandle);
printf("Unknown:%d\n", nOtherHandle);
printf("GDITotal:%d\n", nTotalGDI);
return 1;
}
i kod GDIInquiry.h jest podany poniżej
#pragma once
#include "stdafx.h"
#include "Winternl.h"
// defines a GDI CELL
typedef struct
{
LPVOID pKernelAddress;
USHORT wProcessId;
USHORT wCount;
USHORT wUpper;
USHORT wType;
LPVOID pUserAddress;
} GDICELL;
// defines a GDI CELL for WOW64
typedef struct
{
PVOID64 pKernelAddress;
USHORT wProcessId;
USHORT wCount;
USHORT wUpper;
USHORT wType;
PVOID64 pUserAddress;
} GDICELL_WOW64;
// NtQueryInformationProcess for pure 32 and 64-bit processes
typedef NTSTATUS(NTAPI *_NtQueryInformationProcess)(
IN HANDLE ProcessHandle,
ULONG ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
typedef NTSTATUS(NTAPI *_NtReadVirtualMemory)(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
OUT PVOID Buffer,
IN SIZE_T Size,
OUT PSIZE_T NumberOfBytesRead);
// NtQueryInformationProcess for 32-bit process on WOW64
typedef NTSTATUS(NTAPI *_NtWow64ReadVirtualMemory64)(
IN HANDLE ProcessHandle,
IN PVOID64 BaseAddress,
OUT PVOID Buffer,
IN ULONG64 Size,
OUT PULONG64 NumberOfBytesRead);
// PROCESS_BASIC_INFORMATION for pure 32 and 64-bit processes
/*typedef struct
{
PVOID Reserved1;
PVOID PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId;
PVOID Reserved3;
}PROCESS_BASIC_INFORMATION;*/
// PROCESS_BASIC_INFORMATION for 32-bit process on WOW64
// The definition is quite funky, as we just lazily doubled sizes to match offsets...
typedef struct PROCESS_BASIC_INFORMATION_WOW64
{
PVOID Reserved1[2];
PVOID64 PebBaseAddress;
PVOID Reserved2[4];
ULONG_PTR UniqueProcessId[2];
PVOID Reserved3[2];
}PROCESS_BASIC_INFORMATION_WOW64;
Finish
Run commands:
..\..\GDIInquiry\Debug GDIInquiry.exe PID
Sample Output:
cmd
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
..\..\GDIInquiry\Debug>GDIInquiry.exe 6772
Bitmap:302
Brush:139
DeviceContext:133
Font:75
Palette:1
Pen:0
Region:11
Unknown:0
GDITotal:661
- 1. Jak uzyskać listę wszystkich uchwytów okien w Javie (przy użyciu JNA)?
- 2. Uchwyty GDI w aplikacji DotNET
- 3. Jak uzyskać liczbę otwartych uchwytów, które należą do określonego procesu?
- 4. Jaka jest różnica między GDI a GDI +?
- 5. Jak zmienić kolor uchwytów EditText?
- 6. Jak uzyskać listę nazw kolumn
- 7. Drupal: Jak uzyskać listę modułów
- 8. Jak obrócić tekst w GDI +?
- 9. Uzyskaj nazwy wszystkich uchwytów w bieżącym procesie
- 10. Lista wszystkich aktualnie otwartych uchwytów plików?
- 11. MATLAB: Scalanie uchwytów funkcji
- 12. uzyskać listę dostępnych wyliczenia
- 13. Obsługa uchwytów null przez wyjątek
- 14. Alternatywa dla GDI +
- 15. Jak utworzyć menedżera uchwytów w Rust?
- 16. SWT - Jak debugować "Nigdy więcej uchwytów"
- 17. Jak wyświetlić toast wewnątrz uchwytów/wątku?
- 18. Jak uzyskać listę wszystkich użytkowników w SharePoint
- 19. Jak uzyskać listę z zestawu i komparatora
- 20. Jak uzyskać listę kategorii z Magento?
- 21. Jak uzyskać listę błędów zgłaszanych przez funkcję?
- 22. Jak uzyskać listę podłączonych klientów na SignalR
- 23. JavaScript: Jak uzyskać listę wszystkich otwartych okien
- 24. Jak uzyskać listę pól w formularzu XFA?
- 25. Clojure: Jak uzyskać listę kombinacji "współrzędnych"?
- 26. Jak mogę uzyskać listę wszystkich właściwości ABRecordRef?
- 27. Jak uzyskać listę zmienionych plików w svn?
- 28. Jak uzyskać listę nieinteraktywnych funkcji Empsa?
- 29. Jak uzyskać listę wszystkich obiektów? - PostgreSQL
- 30. Jak uzyskać listę wszystkich dostępnych poleceń powłoki?
Jeśli chcesz otrzymać dostęp do obiektów GDI innej aplikacji, trzeba skoordynować z tej aplikacji. W przeciwnym razie nie wiesz, czy druga aplikacja zniszczy obiekt, gdy go używasz. Nie ma po prostu niezawodnego sposobu, aby to zrobić bez współpracy z drugą aplikacją. –