private ScreenshotResource CaptureDirectX(IntPtr wnd) { if (!AttachHookToProcess()) { throw new ScreenshotCaptureException("Error attaching to DirectX"); } ScreenshotResource result = null; // Bitmap img = null; // captureProcess.BringProcessWindowToFront(); // Initiate the screenshot of the CaptureInterface, the appropriate event handler within the target process will take care of the rest if (captureConfig.Direct3DVersion == Direct3DVersion.Direct3D9 || captureConfig.Direct3DVersion == Direct3DVersion.Direct3D9Simple) { var start = DateTime.Now.Ticks; var task = Task<Screenshot>.Factory.FromAsync( (rect, timeout, callback, ctxt) => captureProcess.CaptureInterface.BeginGetScreenshot(rect, timeout, callback), captureProcess.CaptureInterface.EndGetScreenshot, emptyRect, waitForScreenshotTimeout, null); Screenshot screen = null; try { task.Wait(); screen = task.Result; var stop = DateTime.Now.Ticks; var proc = TimeSpan.FromTicks(stop - start).Milliseconds; TraceLog.Log("DX Capture speed: {0}", proc); if (screen == null && directxRetryCount == 0) { Log.Debug("No data received from DirectX hook, retrying once."); directxRetryCount++; return CaptureDirectX(wnd); } else if (screen == null) { Log.Debug("No data received from DirectX hook."); return null; } directxRetryCount = 0; task.Dispose(); try { var width = screen.Width; var height = screen.Height; var bitmapData = screen.CapturedBitmap; var img = new Bitmap(width, height, PixelFormat.Format32bppRgb); var bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.WriteOnly, img.PixelFormat); Marshal.Copy(bitmapData, 0, bmpData.Scan0, bitmapData.Length); img.UnlockBits(bmpData); result = new ScreenshotResource(img); } catch (Exception ex) { Log.Debug(ex, "Error decoding DirectX pixels: {0}"); return null; } } finally { if (screen != null) { screen.Dispose(); } } } else if (captureConfig.Direct3DVersion == Direct3DVersion.Direct3D9SharedMem) { try { if (!hookReadyWaitHandle.WaitOne(2000)) { Log.Debug("Waiting for DirectX hook initialization."); return null; } captureDxWaitHandle.Set(); if (copyDataMem == null) { try { copyDataMem = MemoryMappedFile.OpenExisting("CaptureHookSharedMemData", MemoryMappedFileRights.Read); copyDataMemAccess = copyDataMem.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); copyDataMemAccess.SafeMemoryMappedViewHandle.AcquirePointer(ref copyDataMemPtr); } catch (FileNotFoundException) { // not hooked Log.Debug("Shared memory not found"); return null; } } if (copyDataMemAccess == null) { // not hooked Log.Debug("Shared memory not opened yet"); return null; } int lastRendered; var copyData = (*((CopyData*)copyDataMemPtr)); if (copyData.height <= 0 || copyData.textureId == Guid.Empty) { return null; } lastRendered = copyData.lastRendered; if (lastRendered != -1) { if (!sharedMemMutexes[lastRendered].WaitOne(1000)) { Log.Warn("Failed acquiring shared texture lock in time (1000)."); return null; } if (lastKnownTextureId != copyData.textureId) { for (var i = 0; i < 2; i++) { if (sharedTexturesAccess[i] != null) { sharedTexturesAccess[i].SafeMemoryMappedViewHandle.ReleasePointer(); sharedTexturesAccess[i].Dispose(); sharedTexturesAccess[i] = null; } if (sharedTextures[i] != null) { sharedTextures[i].Dispose(); sharedTextures[i] = null; } } } if (sharedTextures[lastRendered] == null) { sharedTextures[lastRendered] = MemoryMappedFile.OpenExisting(copyData.textureId.ToString() + lastRendered, MemoryMappedFileRights.ReadWrite); sharedTexturesAccess[lastRendered] = sharedTextures[lastRendered].CreateViewAccessor( 0, copyData.height * copyData.pitch, MemoryMappedFileAccess.ReadWrite); sharedTexturesAccess[lastRendered].SafeMemoryMappedViewHandle.AcquirePointer(ref sharedTexturesPtr[lastRendered]); } var img = new Bitmap( copyData.width, copyData.height, copyData.pitch, PixelFormat.Format32bppRgb, new IntPtr(sharedTexturesPtr[lastRendered])); lastKnownTextureId = copyData.textureId; result = new DxScreenshotResource(img, sharedMemMutexes[lastRendered]); } } catch (Exception ex) { Log.Error(ex); } } return result; }
private CaptureMethod? DetectCaptureMethod(IntPtr wnd, out ScreenshotResource img) { if (NativeMethods.IsIconic(wnd)) { img = null; return null; } if (ScreenCapture.IsFullScreen(wnd)) { if (dontUseDirectX) { Log.Warn("Auto-detect: full-screen and DirectX errors. Capturing will probably not work."); } else { Log.Info("Auto-detect: window is full-screen, use DirectX"); try { img = CaptureDirectX(wnd); if (img != null && img.Bitmap != null) { Log.Info("Auto-detect: Can use DirectX"); return CaptureMethod.DirectX; } Log.Warn("Auto-detect: DirectX returned empty image"); } catch (Exception ex) { Log.Warn("Auto-detect: DirectX throws errors, investigate: " + ex); } } img = null; return null; } if (!dontUseDirectX) { try { img = CaptureDirectX(wnd); if (img != null && img.Bitmap != null) { Log.Info("Auto-detect: Can use DirectX"); return CaptureMethod.DirectX; } } catch (Exception ex) { Log.Warn("Auto-detect: DirectX throws errors, investigate: " + ex); } } try { img = CaptureWdm(wnd, true, false); if (img != null) { if (!img.Bitmap.IsAllBlack()) { Log.Info("Auto-detect: Can use Wdm"); return CaptureMethod.Wdm; } } } catch (Exception) { } //if (dontUseDirectX) //{ // Log.Info("Auto-detect: DirectX gave too much errors, skipping DirectX."); //} //else //{ // try // { // img = CaptureDirectX(wnd); // if (img != null) // { // if (!img.Bitmap.IsAllBlack()) // { // Log.Info("Auto-detect: Can use DirectX"); // return CaptureMethod.DirectX; // } // } // } // catch (Exception) // { // } //} try { img = CaptureWdm(wnd, false, true); if (img != null && img.Bitmap != null) { if (!img.Bitmap.IsAllBlack()) { Log.Info("Auto-detect: Can use BitBlt"); return CaptureMethod.BitBlt; } } } catch (Exception) { } img = null; return null; }
private CaptureMethod?DetectCaptureMethod(IntPtr wnd, out ScreenshotResource img) { if (NativeMethods.IsIconic(wnd)) { img = null; return(null); } if (ScreenCapture.IsFullScreen(wnd)) { if (dontUseDirectX) { Log.Warn("Auto-detect: full-screen and DirectX errors. Capturing will probably not work."); } else { Log.Info("Auto-detect: window is full-screen, use DirectX"); try { img = CaptureDirectX(wnd); if (img != null && img.Bitmap != null) { Log.Info("Auto-detect: Can use DirectX"); return(CaptureMethod.DirectX); } else { Log.Warn("Auto-detect: DirectX returned empty image"); } } catch (Exception ex) { Log.Warn("Auto-detect: DirectX throws errors, investigate: " + ex); } } img = null; return(null); } if (!dontUseDirectX) { try { img = CaptureDirectX(wnd); if (img != null && img.Bitmap != null) { Log.Info("Auto-detect: Can use DirectX"); return(CaptureMethod.DirectX); } } catch (Exception ex) { Log.Warn("Auto-detect: DirectX throws errors, investigate: " + ex); } } try { img = CaptureWdm(wnd, true, false); if (img != null) { if (!img.Bitmap.IsAllBlack()) { Log.Info("Auto-detect: Can use Wdm"); return(CaptureMethod.Wdm); } } } catch (Exception) { } //if (dontUseDirectX) //{ // Log.Info("Auto-detect: DirectX gave too much errors, skipping DirectX."); //} //else //{ // try // { // img = CaptureDirectX(wnd); // if (img != null) // { // if (!img.Bitmap.IsAllBlack()) // { // Log.Info("Auto-detect: Can use DirectX"); // return CaptureMethod.DirectX; // } // } // } // catch (Exception) // { // } //} try { img = this.CaptureWdm(wnd, false, true); if (img != null && img.Bitmap != null) { if (!img.Bitmap.IsAllBlack()) { Log.Info("Auto-detect: Can use BitBlt"); return(CaptureMethod.BitBlt); } } } catch (Exception) { } img = null; return(null); }
private ScreenshotResource CaptureDirectX(IntPtr wnd) { if (!this.AttachHookToProcess()) { throw new ScreenshotCaptureException("Error attaching to DirectX"); } ScreenshotResource result = null; // Bitmap img = null; // captureProcess.BringProcessWindowToFront(); // Initiate the screenshot of the CaptureInterface, the appropriate event handler within the target process will take care of the rest if (this.captureConfig.Direct3DVersion == Direct3DVersion.Direct3D9 || this.captureConfig.Direct3DVersion == Direct3DVersion.Direct3D9Simple) { var start = DateTime.Now.Ticks; var task = Task <Screenshot> .Factory.FromAsync( (rect, timeout, callback, ctxt) => this.captureProcess.CaptureInterface.BeginGetScreenshot(rect, timeout, callback), this.captureProcess.CaptureInterface.EndGetScreenshot, emptyRect, waitForScreenshotTimeout, null); Screenshot screen = null; try { task.Wait(); screen = task.Result; var stop = DateTime.Now.Ticks; var proc = TimeSpan.FromTicks(stop - start).Milliseconds; TraceLog.Log("DX Capture speed: {0}", proc); if (screen == null && directxRetryCount == 0) { Log.Debug("No data received from DirectX hook, retrying once."); directxRetryCount++; return(CaptureDirectX(wnd)); } else if (screen == null) { Log.Debug("No data received from DirectX hook."); return(null); } directxRetryCount = 0; task.Dispose(); try { var width = screen.Width; var height = screen.Height; var bitmapData = screen.CapturedBitmap; var img = new Bitmap(width, height, PixelFormat.Format32bppRgb); var bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.WriteOnly, img.PixelFormat); Marshal.Copy(bitmapData, 0, bmpData.Scan0, bitmapData.Length); img.UnlockBits(bmpData); result = new ScreenshotResource(img); } catch (Exception ex) { Log.Debug("Error decoding DirectX pixels: {0}", ex); return(null); } } finally { if (screen != null) { screen.Dispose(); } } } else if (this.captureConfig.Direct3DVersion == Direct3DVersion.Direct3D9SharedMem) { try { if (!hookReadyWaitHandle.WaitOne(2000)) { Log.Debug("Waiting for DirectX hook initialization."); return(null); } captureDxWaitHandle.Set(); if (this.copyDataMem == null) { try { this.copyDataMem = MemoryMappedFile.OpenExisting("CaptureHookSharedMemData", MemoryMappedFileRights.Read); this.copyDataMemAccess = this.copyDataMem.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read); this.copyDataMemAccess.SafeMemoryMappedViewHandle.AcquirePointer(ref this.copyDataMemPtr); } catch (FileNotFoundException) { // not hooked Log.Debug("Shared memory not found"); return(null); } } if (copyDataMemAccess == null) { // not hooked Log.Debug("Shared memory not opened yet"); return(null); } int lastRendered; CopyData copyData = (*((CopyData *)this.copyDataMemPtr)); if (copyData.height <= 0 || copyData.textureId == Guid.Empty) { return(null); } lastRendered = copyData.lastRendered; if (lastRendered != -1) { if (!this.sharedMemMutexes[lastRendered].WaitOne(1000)) { Log.Warn("Failed acquiring shared texture lock in time (1000)."); return(null); } if (this.lastKnownTextureId != copyData.textureId) { for (int i = 0; i < 2; i++) { if (this.sharedTexturesAccess[i] != null) { this.sharedTexturesAccess[i].SafeMemoryMappedViewHandle.ReleasePointer(); this.sharedTexturesAccess[i].Dispose(); this.sharedTexturesAccess[i] = null; } if (this.sharedTextures[i] != null) { this.sharedTextures[i].Dispose(); this.sharedTextures[i] = null; } } } if (this.sharedTextures[lastRendered] == null) { this.sharedTextures[lastRendered] = MemoryMappedFile.OpenExisting(copyData.textureId.ToString() + lastRendered, MemoryMappedFileRights.ReadWrite); this.sharedTexturesAccess[lastRendered] = this.sharedTextures[lastRendered].CreateViewAccessor( 0, copyData.height * copyData.pitch, MemoryMappedFileAccess.ReadWrite); this.sharedTexturesAccess[lastRendered].SafeMemoryMappedViewHandle.AcquirePointer(ref sharedTexturesPtr[lastRendered]); } var img = new Bitmap( copyData.width, copyData.height, copyData.pitch, PixelFormat.Format32bppRgb, new IntPtr(sharedTexturesPtr[lastRendered])); this.lastKnownTextureId = copyData.textureId; result = new DxScreenshotResource(img, this.sharedMemMutexes[lastRendered]); } } catch (Exception ex) { Log.Error(ex); } } return(result); }
private void CaptureLoop() { IntPtr wnd; try { wnd = HearthstoneHelper.GetHearthstoneWindow(); } catch (Exception ex) { Log.Error(ex); OnWindowLost(); return; } // Verify window exists if (wnd == IntPtr.Zero) { OnWindowLost(); return; } ScreenshotResource img = null; try { if (currentCaptureMethod == null) { currentCaptureMethod = CaptureMethod; } if (preferredCaptureMethod != CaptureMethod) { // user changed capture method in settings currentCaptureMethod = CaptureMethod; preferredCaptureMethod = CaptureMethod; } if (dontUseDirectX && currentCaptureMethod.Value == CaptureMethod.DirectX) { Log.Info("DirectX gave too much errors, switching to Wdm."); currentCaptureMethod = CaptureMethod.Wdm; } switch (currentCaptureMethod) { case CaptureMethod.AutoDetect: var detectedMethod = DetectCaptureMethod(wnd, out img); if (detectedMethod == null) { OnWindowMinimized(); extraDelay = 200; // small delay, just enough to not be noticable, but less cpu Thread.Sleep(extraDelay); return; } currentCaptureMethod = detectedMethod.Value; break; case CaptureMethod.DirectX: img = CaptureDirectX(wnd); break; case CaptureMethod.Wdm: img = CaptureWdm(wnd); break; case CaptureMethod.BitBlt: img = CaptureWdm(wnd, false, true); break; } if (lastCaptureMethod != currentCaptureMethod.Value) { Log.Debug("Capture method changed from {0} to {1}", lastCaptureMethod, currentCaptureMethod.Value); // Do not detach hook, so we can quickly switch again //if (lastCaptureMethod == CaptureMethod.DirectX) //{ // DettachHookFromProcess(); //} lastCaptureMethod = currentCaptureMethod.Value; } if (img == null || img.Bitmap == null) { TraceLog.Log("No image data found."); OnWindowMinimized(); extraDelay = 200; // small delay, just enough to not be noticable, but less cpu Thread.Sleep(extraDelay); return; } if (CaptureMethod == CaptureMethod.AutoDetect) { if (lastImageHeight > 0 && lastImageHeight != img.Bitmap.Height) { // reset capture method so we detect again on next run Log.Debug("Auto-detect: Image resolution changed from {0} to {1}, reset auto-detect.", lastImageHeight, img.Bitmap.Height); currentCaptureMethod = null; } lastImageHeight = img.Bitmap.Height; } extraDelay = 0; OnWindowFound(); if (this.PublishCapturedWindow) { // Log.Diag("Window captured"); var bmpcpy = new Bitmap(img.Bitmap); this.Publish(new WindowCaptured(bmpcpy), log: false); } var start = DateTime.Now.Ticks; try { // scan areas, publish events; foreach (var scanner in this.imageScanners) { scanner.Run(img.Bitmap, null); } } finally { try { if (img != null) { img.Dispose(); } } catch { } var stop = DateTime.Now.Ticks; var proc = TimeSpan.FromTicks(stop - start).Milliseconds; TraceLog.Log("Scanner speed: {0}", proc); } // wait with passing next capture to scanner and signal scanner thread to process //this.captureWaitHandle.Wait(); //this.captureWaitHandle.Reset(); //if (this.currentImage != null) //{ // this.currentImage.Dispose(); // this.currentImage = null; //} //this.currentImage = img; //this.scannerWaitHandle.Set(); } catch (ScreenshotCaptureException ex) { Log.Debug(ex.ToString()); OnWindowLost(); extraDelay = 2000; Thread.Sleep(extraDelay); } catch (Exception ex) { Log.Error(ex); } }