Skip to content

Commit f834f44

Browse files
committed
fix: 为 WGC 添加异常保护
1 parent 22024cc commit f834f44

1 file changed

Lines changed: 135 additions & 47 deletions

File tree

source/MaaWin32ControlUnit/Screencap/FramePoolScreencap.cpp

Lines changed: 135 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -44,28 +44,45 @@ std::optional<cv::Mat> FramePoolScreencap::screencap()
4444

4545
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame frame = nullptr;
4646

47-
// 先清空 FramePool 中可能残留的旧帧
48-
while (auto old_frame = cap_frame_pool_.TryGetNextFrame()) {
49-
old_frame.Close();
50-
}
51-
52-
// 等待新帧到来
53-
using namespace std::chrono_literals;
54-
auto start_time = std::chrono::steady_clock::now();
55-
while (duration_since(start_time) < 2000ms) {
56-
std::this_thread::sleep_for(2ms);
57-
frame = cap_frame_pool_.TryGetNextFrame();
58-
if (frame) {
59-
break;
47+
try {
48+
// 先清空 FramePool 中可能残留的旧帧
49+
while (auto old_frame = cap_frame_pool_.TryGetNextFrame()) {
50+
old_frame.Close();
6051
}
52+
53+
// 等待新帧到来
54+
using namespace std::chrono_literals;
55+
auto start_time = std::chrono::steady_clock::now();
56+
while (duration_since(start_time) < 2000ms) {
57+
std::this_thread::sleep_for(2ms);
58+
frame = cap_frame_pool_.TryGetNextFrame();
59+
if (frame) {
60+
break;
61+
}
62+
}
63+
}
64+
catch (const winrt::hresult_error& e) {
65+
LogError << "Failed to get frame" << VAR(e.code()) << VAR(winrt::to_string(e.message()));
66+
uninit();
67+
return std::nullopt;
6168
}
6269

6370
if (!frame) {
6471
LogError << "Failed to get frame after timeout";
6572
return std::nullopt;
6673
}
6774

68-
auto access = frame.Surface().as<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>();
75+
auto surface = frame.Surface();
76+
if (!surface) {
77+
LogError << "frame.Surface() is null";
78+
return std::nullopt;
79+
}
80+
81+
auto access = surface.try_as<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>();
82+
if (!access) {
83+
LogError << "Failed to get IDirect3DDxgiInterfaceAccess";
84+
return std::nullopt;
85+
}
6986

7087
winrt::com_ptr<ID3D11Texture2D> texture = nullptr;
7188
HRESULT ret = access->GetInterface(winrt::guid_of<ID3D11Texture2D>(), texture.put_void());
@@ -197,12 +214,22 @@ bool FramePoolScreencap::init()
197214
return false;
198215
}
199216

200-
auto activation_factory = winrt::get_activation_factory<winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
201-
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
202-
ret = interop_factory->CreateForWindow(
203-
hwnd_,
204-
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
205-
winrt::put_abi(cap_item_));
217+
try {
218+
auto activation_factory = winrt::get_activation_factory<winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
219+
auto interop_factory = activation_factory.try_as<IGraphicsCaptureItemInterop>();
220+
if (!interop_factory) {
221+
LogError << "Failed to get IGraphicsCaptureItemInterop";
222+
return false;
223+
}
224+
ret = interop_factory->CreateForWindow(
225+
hwnd_,
226+
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
227+
winrt::put_abi(cap_item_));
228+
}
229+
catch (const winrt::hresult_error& e) {
230+
LogError << "Failed to create GraphicsCaptureItem" << VAR(e.code()) << VAR(winrt::to_string(e.message()));
231+
return false;
232+
}
206233
if (FAILED(ret)) {
207234
LogError << "CreateForWindow GraphicsCaptureItem failed" << VAR(ret);
208235
return false;
@@ -224,7 +251,11 @@ bool FramePoolScreencap::init()
224251
return false;
225252
}
226253

227-
winrt::com_ptr<IDXGIDevice> dxgi_device = d3d_device_.as<IDXGIDevice>();
254+
auto dxgi_device = d3d_device_.try_as<IDXGIDevice>();
255+
if (!dxgi_device) {
256+
LogError << "Failed to get IDXGIDevice";
257+
return false;
258+
}
228259

229260
winrt::com_ptr<IInspectable> inspectable = nullptr;
230261
ret = CreateDirect3D11DeviceFromDXGIDevice(dxgi_device.get(), inspectable.put());
@@ -233,27 +264,51 @@ bool FramePoolScreencap::init()
233264
return false;
234265
}
235266

236-
cap_frame_pool_ = winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::Create(
237-
inspectable.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>(),
238-
winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized,
239-
1,
240-
cap_item_.Size());
267+
auto d3d_device_interop = inspectable.try_as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
268+
if (!d3d_device_interop) {
269+
LogError << "Failed to get IDirect3DDevice";
270+
return false;
271+
}
272+
273+
try {
274+
cap_frame_pool_ = winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::Create(
275+
d3d_device_interop,
276+
winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized,
277+
1,
278+
cap_item_.Size());
279+
}
280+
catch (const winrt::hresult_error& e) {
281+
LogError << "Direct3D11CaptureFramePool::Create failed" << VAR(e.code()) << VAR(winrt::to_string(e.message()));
282+
return false;
283+
}
241284

242285
if (!cap_frame_pool_) {
243-
LogError << "Direct3D11CaptureFramePool::Create failed";
286+
LogError << "Direct3D11CaptureFramePool::Create returned null";
244287
return false;
245288
}
246289

247-
cap_session_ = cap_frame_pool_.CreateCaptureSession(cap_item_);
290+
try {
291+
cap_session_ = cap_frame_pool_.CreateCaptureSession(cap_item_);
292+
}
293+
catch (const winrt::hresult_error& e) {
294+
LogError << "CreateCaptureSession failed" << VAR(e.code()) << VAR(winrt::to_string(e.message()));
295+
return false;
296+
}
248297
if (!cap_session_) {
249-
LogError << "CreateCaptureSession failed";
298+
LogError << "CreateCaptureSession returned null";
250299
return false;
251300
}
252301

253302
// 尝试关闭截图时的黄色边框(Windows 11 及部分 Win10 版本支持)
254303
try_disable_border();
255304

256-
cap_session_.StartCapture();
305+
try {
306+
cap_session_.StartCapture();
307+
}
308+
catch (const winrt::hresult_error& e) {
309+
LogError << "StartCapture failed" << VAR(e.code()) << VAR(winrt::to_string(e.message()));
310+
return false;
311+
}
257312

258313
// 记录初始窗口大小
259314
if (cap_item_) {
@@ -268,13 +323,27 @@ bool FramePoolScreencap::init()
268323
void FramePoolScreencap::uninit()
269324
{
270325
if (cap_session_) {
271-
cap_session_.Close();
326+
try {
327+
cap_session_.Close();
328+
}
329+
catch (const winrt::hresult_error& e) {
330+
LogWarn << "cap_session_.Close() failed" << VAR(e.code()) << VAR(winrt::to_string(e.message()));
331+
}
272332
cap_session_ = nullptr;
273333
}
274334

335+
if (cap_frame_pool_) {
336+
try {
337+
cap_frame_pool_.Close();
338+
}
339+
catch (const winrt::hresult_error& e) {
340+
LogWarn << "cap_frame_pool_.Close() failed" << VAR(e.code()) << VAR(winrt::to_string(e.message()));
341+
}
342+
cap_frame_pool_ = nullptr;
343+
}
344+
275345
readable_texture_ = nullptr;
276-
cap_frame_pool_ = nullptr;
277-
cap_session_ = nullptr;
346+
cap_item_ = nullptr;
278347
texture_desc_ = { 0 };
279348
last_capture_size_ = {};
280349
}
@@ -285,7 +354,20 @@ bool FramePoolScreencap::check_and_handle_size_changed()
285354
return true;
286355
}
287356

288-
auto current_size = cap_item_.Size();
357+
if (!IsWindow(hwnd_)) {
358+
LogError << "Window is no longer valid";
359+
return false;
360+
}
361+
362+
winrt::Windows::Graphics::SizeInt32 current_size {};
363+
try {
364+
current_size = cap_item_.Size();
365+
}
366+
catch (const winrt::hresult_error& e) {
367+
LogError << "Failed to get capture item size" << VAR(e.code()) << VAR(winrt::to_string(e.message()));
368+
uninit();
369+
return false;
370+
}
289371
// 如果窗口大小没有变化,直接返回
290372
if (current_size.Width == last_capture_size_.first && current_size.Height == last_capture_size_.second) {
291373
return true;
@@ -352,21 +434,27 @@ void FramePoolScreencap::try_disable_border()
352434
return;
353435
}
354436

355-
auto op = GraphicsCaptureAccess::RequestAccessAsync(GraphicsCaptureAccessKind::Borderless);
356-
auto status = op.wait_for(std::chrono::seconds(5));
357-
if (status != winrt::Windows::Foundation::AsyncStatus::Completed) {
358-
LogWarn << "RequestAccessAsync did not complete in time";
359-
return;
360-
}
437+
try {
438+
auto op = GraphicsCaptureAccess::RequestAccessAsync(GraphicsCaptureAccessKind::Borderless);
439+
auto status = op.wait_for(std::chrono::seconds(5));
440+
if (status != winrt::Windows::Foundation::AsyncStatus::Completed) {
441+
LogWarn << "RequestAccessAsync did not complete in time";
442+
return;
443+
}
361444

362-
auto access_result = op.GetResults();
363-
if (access_result != winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapabilityAccessStatus::Allowed) {
364-
LogWarn << "Borderless capture access not granted:" << static_cast<int>(access_result);
365-
return;
366-
}
445+
auto access_result = op.GetResults();
446+
if (access_result
447+
!= winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapabilityAccessStatus::Allowed) {
448+
LogWarn << "Borderless capture access not granted:" << static_cast<int>(access_result);
449+
return;
450+
}
367451

368-
cap_session_.IsBorderRequired(false);
369-
LogInfo << "Capture border disabled successfully";
452+
cap_session_.IsBorderRequired(false);
453+
LogInfo << "Capture border disabled successfully";
454+
}
455+
catch (const winrt::hresult_error& e) {
456+
LogWarn << "Failed to disable capture border" << VAR(e.code()) << VAR(winrt::to_string(e.message()));
457+
}
370458
}
371459

372460
MAA_CTRL_UNIT_NS_END

0 commit comments

Comments
 (0)