dxgi截屏,多次调用报错
我想使用window上dxgi方式截屏,做成dll,并在java端调用。
但是现在只能截图一次,第一次截图没有问题,获取到的也是当前整个桌面的图像。但是之后每次调用都会失败,并且报错。我看了下官网这个错是DXGI_ERROR_INVALID_CALL。
DXGI_ERROR_INVALID_CALL 0x887A0001
The application provided invalid parameter data; this must be debugged and fixed before the application is released.
下边是我的C++代码
#include "pch.h"
#include <tchar.h>
#include <memory>
#include <windows.h>
#include <atlbase.h>
#include <dxgi.h>
#include <DXGI1_2.h>
#include <d3d11.h>
using namespace std;
IDXGIFactory1* m_spDXGIFactory1;
IDXGIAdapter1* spAdapter;
IDXGIOutput* spDXGIOutput;
IDXGIOutputDuplication* spDXGIOutputDuplication;
//设备接口代表一个虚拟适配器;用于创建资源。
ID3D11Device* spD3D11Device;
//ID3D11DeviceContext接口表示生成渲染命令的设备上下文
ID3D11DeviceContext* spD3D11DeviceContext;
void init() {
HRESULT hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)(&m_spDXGIFactory1));
if (m_spDXGIFactory1->EnumAdapters1(0, &spAdapter) == DXGI_ERROR_NOT_FOUND) {
return;
}
if ((spAdapter)->EnumOutputs(0, &spDXGIOutput) == DXGI_ERROR_NOT_FOUND) {
return;
}
//描述Direct3D设备的一组特性,这里的值没意义,调用D3D11CreateDevice后,会重新给这个指针赋值
D3D_FEATURE_LEVEL fl = D3D_FEATURE_LEVEL_11_0;
// 参阅 https://docs.microsoft.com/zh-cn/windows/win32/api/d3d11/nf-d3d11-d3d11createdevice
// 创建ID3D11Device等一些东西
hr = D3D11CreateDevice(
spAdapter, /***/
D3D_DRIVER_TYPE_UNKNOWN, /***/
NULL, /** 软件栅格化的回调地址,这里不需要*/
0, /** 0代表单线程模式*/
NULL, /** pFeatureLevels ,使用默认特性数组*/
0, /** 上一个参数的长度,这里传0,不知道有啥用*/
D3D11_SDK_VERSION, /** 使用D3D11_SDK_VERSION版本*/
&spD3D11Device, /** 返回ID3D11Device的地址给这个指针*/
&fl, /** 返回从pFeatureLevels中,最终使用的特性,赋值给这个指针,测试返回的为D3D_FEATURE_LEVEL_11_0*/
&spD3D11DeviceContext); /** 返回ID3D11DeviceContext赋值给这个指针*/
IDXGIOutput1* spDXGIOutput1 = (IDXGIOutput1*)spDXGIOutput;
IDXGIDevice1* spDXGIDevice = (IDXGIDevice1*)spD3D11Device;
//关键点,获取IDXGIOutputDuplication,IDXGIOutputDuplication接口用来访问和操作被复制的桌面图像。
// DuplicateOutput函数,从spDXGIOutput1创建一个桌面图像复制接口,
// 根据官方文档,可以通过IDXGIOutput5::DuplicateOutput1 提高性能,待测试
hr = spDXGIOutput1->DuplicateOutput(spDXGIDevice, &spDXGIOutputDuplication);
if (FAILED(hr))
{
return;
}
DXGI_OUTDUPL_DESC dxgi_des;
spDXGIOutputDuplication->GetDesc(&dxgi_des);
if (dxgi_des.DesktopImageInSystemMemory) {
//new DxgiTextureMapping(spDXGIOutputDuplication);
}
else {
}
}
long capture(unsigned char* buffer) {
DXGI_OUTDUPL_FRAME_INFO frame_info;
//IDXGIResource接口允许资源共享,并标识资源所在的内存。
IDXGIResource* desktop_resource;
HRESULT hr = spDXGIOutputDuplication->AcquireNextFrame(0, &frame_info, &desktop_resource);
// Timeout will return when desktop has no chane
if (hr == DXGI_ERROR_WAIT_TIMEOUT) return hr;
if (hr == DXGI_ERROR_INVALID_CALL) return -100;
if (FAILED(hr))
return hr;
ID3D11Texture2D* image;
desktop_resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&image));
desktop_resource->Release();
desktop_resource = nullptr;
D3D11_TEXTURE2D_DESC frame_desc;
image->GetDesc(&frame_desc);
ID3D11Texture2D* new_image = NULL;
frame_desc.Usage = D3D11_USAGE_STAGING;
frame_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
frame_desc.BindFlags = 0;
frame_desc.MiscFlags = 0;
frame_desc.MipLevels = 1;
frame_desc.ArraySize = 1;
frame_desc.SampleDesc.Count = 1;
hr = spD3D11Device->CreateTexture2D(&frame_desc, NULL, &new_image);
spD3D11DeviceContext->CopyResource(new_image, image);
//将图像从GPU映射到内存中
IDXGISurface* dxgi_surface = NULL;
hr = new_image->QueryInterface(__uuidof(IDXGISurface), (void**)(&dxgi_surface));
new_image->Release();
// DXGI_MAPPED_RECT的Pitch代表图像的宽度,假如1920像素,每个像素4通道,则Pitch等于7680
// pBits指向表面的图像缓冲区的指针。
DXGI_MAPPED_RECT rect;
hr = dxgi_surface->Map(&rect, DXGI_MAP_READ);
// int dst_rowpitch = frame_desc.Width * 4;
//for (int h = 0; h < frame_desc.Height; h++) {
// memcpy_s(buffer + h * dst_rowpitch, dst_rowpitch, (BYTE*)rect.pBits + h * rect.Pitch, min(mapped_rect.Pitch, dst_rowpitch));
// }
memcpy(buffer, rect.pBits, 1920 * 1080 * 4);
dxgi_surface->Unmap();
return 123456789;
}
void Release() {
spAdapter->Release();
spDXGIOutput->Release();
}
int main() {
init();
unsigned char* buffer = new unsigned char[1920 * 1080 * 4];
capture(buffer);
Release();
}
JAVA代码使用的jdk16的调用方式
import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.LibraryLookup;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.nio.file.Path;
public class CallScreenShotDLL{
public static void main(String[] args) {
Path paths = Path.of("C:\\Users\\h6706\\source\\repos\\screenShot\\x64\\Debug\\screenShot.dll");
MethodHandle init = CLinker.getInstance().downcallHandle(
LibraryLookup.ofPath(paths).lookup("init").get(),
MethodType.methodType(void.class),
FunctionDescriptor.ofVoid()
);
MethodHandle release = CLinker.getInstance().downcallHandle(
LibraryLookup.ofPath(paths).lookup("Release").get(),
MethodType.methodType(void.class),
FunctionDescriptor.ofVoid()
);
MethodHandle capture = CLinker.getInstance().downcallHandle(
LibraryLookup.ofPath(paths).lookup("capture").get(),
MethodType.methodType(int.class, MemoryAddress.class),
FunctionDescriptor.of(CLinker.C_INT, CLinker.C_POINTER)
);
try {
init.invokeExact();
for (int i = 0; i < 3; i++) {
MemorySegment segment = MemorySegment.allocateNative(1920 * 1080 * 4);
byte[] bytes = segment.toByteArray();
try {
Thread.sleep(1000);
int result = (int) capture.invokeExact(segment.address());
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
release.invokeExact();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
先调用init()初始化,然后调用capture函数获取图片,第一次调用capture没问题。我也测试了调用capture后返回的内容,最终生成的图片就是我当时的屏幕内容。但是从第二次调用开始就一直报错了。
我也尝试过每次调用capture时都去调用init函数,也同样报错了。这份C代码,是我根据网上的例子改出来的,也不知道是不是那些细节没考虑到,
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
解决了,在调用AcquireNextFrame前,应该调用ReleaseFrame方法。