gpt4 book ai didi

java - 使用上下文菜单捕获 HWND

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:58:54 26 4
gpt4 key购买 nike

我正在使用 WinAPI 在 Java 中捕获窗口。

public static BufferedImage capture(Pointer hWnd2, String wText) {
HWND hWnd = new HWND(hWnd2);
boolean success;
if(User32Extra.INSTANCE.IsIconic(hWnd))
{
success= User32Extra.INSTANCE.ShowWindow(hWnd, 4);
System.err.println(success);
System.err.println(wText+Native.getLastError());
}
HDC hdcWindow2 = User32.INSTANCE.GetDC(hWnd);
HDC hdcMemDC = GDI32.INSTANCE.CreateCompatibleDC(hdcWindow2);
success = User32Extra.INSTANCE.PrintWindow(hWnd,hdcWindow2,1);
RECT bounds = new RECT();
RECT bounds1 = new RECT();
User32Extra.INSTANCE.GetWindowRect(hWnd, bounds);
User32Extra.INSTANCE.GetClientRect(hWnd, bounds1);

int extraGap = (bounds.right-bounds.left-bounds1.right);
int width = bounds.right-bounds.left-extraGap;
int height = bounds.bottom-bounds.top-extraGap ;
HBITMAP hBitmap = GDI32.INSTANCE.CreateCompatibleBitmap(hdcWindow2, width, height);

HANDLE hOld = GDI32.INSTANCE.SelectObject(hdcMemDC, hBitmap);
GDI32Extra.INSTANCE.BitBlt(hdcMemDC,0, 0, width, height, hdcWindow2, bounds.left+bounds1.right-bounds.right+extraGap, bounds.top+bounds1.bottom-bounds.bottom+extraGap, WinGDIExtra.SRCCOPY);
GDI32.INSTANCE.SelectObject(hdcMemDC, hOld);

BITMAPINFO bmi = new BITMAPINFO();
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = WinGDI.BI_RGB;
Memory buffer = new Memory(width * height * 4);
GDI32.INSTANCE.GetDIBits(hdcWindow2, hBitmap, 0, height, buffer, bmi, WinGDI.DIB_RGB_COLORS);

BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

GDI32.INSTANCE.DeleteObject(hBitmap);
GDI32.INSTANCE.DeleteDC(hdcMemDC);
User32.INSTANCE.ReleaseDC(hWnd, hdcWindow2);

return image;
}

此代码捕获 HWND 指定的窗口。但是如果用户打开上下文菜单或菜单栏选项。那些没有被捕获。

如何捕获窗口的上下文菜单?

最佳答案

我会使用 GetDesktopWindow() 捕获整个屏幕,然后将窗口坐标传递给 BitBlt 以仅复制目标窗口和所有叠加层(包括上下文菜单)

public static BufferedImage capture(Pointer hWnd2, String wText) {
HWND hWnd = new HWND(hWnd2);
boolean success;
if(User32Extra.INSTANCE.IsIconic(hWnd))
{
success= User32Extra.INSTANCE.ShowWindow(hWnd, 4);
System.err.println(success);
System.err.println(wText+Native.getLastError());
}
// Pass GetDesktopWindow instead of target HWND to GetDC
HDC hdcWindow2 = User32.INSTANCE.GetDC(User32.INSTANCE.GetDesktopWindow());
HDC hdcMemDC = GDI32.INSTANCE.CreateCompatibleDC(hdcWindow2);

RECT bounds = new RECT();
//RECT bounds1 = new RECT();
User32Extra.INSTANCE.GetWindowRect(hWnd, bounds);
//User32Extra.INSTANCE.GetClientRect(hWnd, bounds1);

//int extraGap = (bounds.right-bounds.left-bounds1.right);
int width = bounds.right-bounds.left;
int height = bounds.bottom-bounds.top;
HBITMAP hBitmap = GDI32.INSTANCE.CreateCompatibleBitmap(hdcWindow2, width, height);

HANDLE hOld = GDI32.INSTANCE.SelectObject(hdcMemDC, hBitmap);
GDI32Extra.INSTANCE.BitBlt(hdcMemDC,0, 0, width, height, hdcWindow2, bounds.left, bounds.top, WinGDIExtra.SRCCOPY);
GDI32.INSTANCE.SelectObject(hdcMemDC, hOld);

BITMAPINFO bmi = new BITMAPINFO();
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = WinGDI.BI_RGB;
Memory buffer = new Memory(width * height * 4);
GDI32.INSTANCE.GetDIBits(hdcWindow2, hBitmap, 0, height, buffer, bmi, WinGDI.DIB_RGB_COLORS);

BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

GDI32.INSTANCE.DeleteObject(hBitmap);
GDI32.INSTANCE.DeleteDC(hdcMemDC);
User32.INSTANCE.ReleaseDC(hWnd, hdcWindow2);

return image;
}

但是,如果其他窗口与我们的目标窗口重叠,则此方法将捕获其他窗口。如果您知道该窗口处于 Activity 状态或打开了上下文菜单(因为它会强制将其置于前台),这可能就足够了。

另一种方法有点 hacky,但它适用于其他场景。我们可以检查是否有类名称为 #32768 的窗口(弹出菜单的类)。如果有,我们检查我们的目标窗口是否是前景窗口,然后我们可以将它复制到我们窗口顶部的目标位图中。

public static BufferedImage capture(Pointer hWnd2, String wText) {
HWND hWnd = new HWND(hWnd2);
boolean success;
if(User32Extra.INSTANCE.IsIconic(hWnd))
{
success= User32Extra.INSTANCE.ShowWindow(hWnd, 4);
System.err.println(success);
System.err.println(wText+Native.getLastError());
}
HDC hdcWindow2 = User32.INSTANCE.GetDC(hWnd);
final HDC hdcMemDC = GDI32.INSTANCE.CreateCompatibleDC(hdcWindow2);

RECT bounds = new RECT();
RECT bounds1 = new RECT();
User32Extra.INSTANCE.GetWindowRect(hWnd, bounds);
User32Extra.INSTANCE.GetClientRect(hWnd, bounds1);

int extraGap = (bounds.right-bounds.left-bounds1.right);
int width = bounds.right-bounds.left-extraGap;
int height = bounds.bottom-bounds.top-extraGap ;
HBITMAP hBitmap = GDI32.INSTANCE.CreateCompatibleBitmap(hdcWindow2, width, height);

HANDLE hOld = GDI32.INSTANCE.SelectObject(hdcMemDC, hBitmap);
GDI32Extra.INSTANCE.BitBlt(hdcMemDC,0, 0, width, height, hdcWindow2, bounds.left+bounds1.right-bounds.right+extraGap, bounds.top+bounds1.bottom-bounds.bottom+extraGap, WinGDIExtra.SRCCOPY);

// Popup menu doesn't have parent, so we could encounter situation when context menu from another window is overlapping our target window
// Since context menu enforces our window to focus, we can check here if our window is foreground window (otherwise it is not possible to have opened context menu)
if (User32.INSTANCE.GetForegroundWindow() == hWnd)
{
final RECT windowBounds = bounds;
// Enum all windows and check if they have class #32768
User32.INSTANCE.EnumWindows(new WNDENUMPROC() {
@Override
public boolean callback(HWND hMenuWnd, Pointer arg1) {
byte[] windowClass = new byte[8];
User32.INSTANCE.GetClassNameA(hMenuWnd, windowClass, 8);
String wClass = Native.toString(windowClass);
// #32768 is class name for popup menus.
if (wClass.equals("#32768"))
{
HDC hdcWindowMenu = User32.INSTANCE.GetDC(hMenuWnd);
RECT menuBounds = new RECT();
User32Extra.INSTANCE.GetWindowRect(hMenuWnd, menuBounds);
GDI32Extra.INSTANCE.BitBlt(hdcMemDC, menuBounds.left - windowBounds.left, menuBounds.top - windowBounds.top, menuBounds.right-menuBounds.left, menuBounds.bottom-menuBounds.top, hdcWindowMenu, 0, 0, WinGDIExtra.SRCCOPY);
User32.INSTANCE.ReleaseDC(hMenuWnd, hdcWindowMenu);
}
return true;
}
}, null);
}

GDI32.INSTANCE.SelectObject(hdcMemDC, hOld);

BITMAPINFO bmi = new BITMAPINFO();
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = WinGDI.BI_RGB;
Memory buffer = new Memory(width * height * 4);
GDI32.INSTANCE.GetDIBits(hdcWindow2, hBitmap, 0, height, buffer, bmi, WinGDI.DIB_RGB_COLORS);

BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

GDI32.INSTANCE.DeleteObject(hBitmap);
GDI32.INSTANCE.DeleteDC(hdcMemDC);
User32.INSTANCE.ReleaseDC(hWnd, hdcWindow2);

return image;
}

对于 EnumWindows,您必须添加以下导入:

import com.sun.jna.platform.win32.WinUser.WNDENUMPROC;

和定义:

boolean EnumWindows(WinUser.WNDENUMPROC lpEnumFunc, Pointer arg);
int GetClassNameA(HWND hWnd, byte[] lpString, int nMaxCount);

关于java - 使用上下文菜单捕获 HWND,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31043798/

26 4 0