Related
I tried capturing live thumbnail of running applications using these codes:
http://community.bartdesmet.net/blogs/bart/archive/2006/10/05/4495.aspx
I have successfully captured some applications except those borderless fullscreen windows.
I am actually trying to capture a UWP application that plays a video in a fullscreen mode and integrate the thumbnail into my application. I don't need to get a bitmap image of it, I just need to display in real-time.
I tried changing the values of
GWL_STYLE
WS_VISIBLE
WS_BORDER
but none of it works.
When trying to Alt+Tab windows, the Windows 10 DWM can handle it and displays the live thumbnail, so I believe this could work with some little modifications on the code.
Thanks!
I checked the window styles for the example uwp application in full screen with Spy++.
Styles were normal so then i hardcoded window handle and it worked. After a moment of debugging, it turned out that the EnumWindows method did not return this window, so it did not matter what styles were checked.
So I looked for a problem with this method and there are many topics, for example:
EnumWindows function in Win10 enumerates only desktop apps
Instead of using EnumWindows, get all windows using this method and insert your filter logic for your desired window handler
IntPtr thisWindow = IntPtr.Zero ;
IntPtr desktopWindow = GetDesktopWindow();
while (true)
{
if (desktopWindow == IntPtr.Zero)
break;
IntPtr nextWindow = FindWindowEx(desktopWindow, thisWindow, null, null);
if (nextWindow == IntPtr.Zero)
break;
/** your code here **/
thisWindow = nextWindow;
}
I currently have implemented a DirectShow live source screen capture filter in C#. The DShow filter is being consumed by a separate process (ffmpeg) that is live encoding the stream.
I would like to render some UI/Controls on the screen for starting/stopping the capture of ffmpeg, (UI provided by a separate process) but I don't want the UI to be captured by the DShow filter and end up in the screen capture video.
Here is an excerpt, which contains the DirectShow FillBuffer method where I capture the screen frames.
public int FillBuffer(ref IMediaSampleImpl _sample)
{
IntPtr _ptr;
_sample.GetPointer(out _ptr);
IntPtr srcContext = User32.GetWindowDC(IntPtr.Zero);
IntPtr destContext = GDI32.CreateCompatibleDC(srcContext);
IntPtr destBitmap = GDI32.CreateCompatibleBitmap(srcContext, _captureWidth, _captureHeight);
IntPtr hOld = GDI32.SelectObject(destContext, destBitmap);
GDI32.BitBlt(destContext, 0, 0, _captureWidth, _captureHeight, srcContext, _captureX, _captureY, GDI32.SRCCOPY);
//Draw cursor, ommitted for brevity
GDI32.SelectObject(destContext, hOld);
GDI32.GetDIBits(destContext, destBitmap, 0, (uint)Math.Abs(_captureHeight), _ptr, ref m_bmi, 0);
GDI32.DeleteDC(destContext);
User32.ReleaseDC(IntPtr.Zero, srcContext);
GDI32.DeleteObject(destBitmap);
_sample.SetActualDataLength(_sample.GetSize());
_sample.SetSyncPoint(true);
return NOERROR;
}
I'm not exactly 100% sure how this would be possible, but here are the only things that remotely make sense to me (not saying they are possible, just brainstorming):
Render the UI controls via a passed window pointer from another process to srcContext each frame but only after its copied to destContext
how would you even render the passed pointer?
how do you refresh the screen so that it wont just get captured next frame (now that its rendered to the screen)?
could this really be viable performance wise (refreshing the screen each frame)?
Is it really going to look nice? - i can't imagine that rendering to the WindowDC is reccomended
is there a way to trigger a re-draw of just the UI control region before the next frame and not refresh the entire screen?
Some how capture the window with GDI but omit the UI window handle via some native API interface i'm unaware of
Capture the entire screen with current method, then re-draw the area that contains the UI controls with the contents of whatever window(s) were underneath (is this even possible?)
Capture individual windows, and then render each visible window separately in z-order but skip the UI window (i have a sneaking suspicion that this may be the only way to actually do this, but i doubt that it would meet reasonable performance requirements)
EDIT, i just thought of using PrintWindow of all of the windows behind the UI control, but PrintWindow is slow and unreliable, and this is performance critical code.. So i would really like a different solution.
I understand that Fraps and similar, use DirectX hooks to intercept paint messages further up the pipeline, and are able to draw overlays on the screen that way - but this is not an option, and neither is a display mirror driver.
My program (C#) is a taskbar and in its menu I can launch applications, compatible XP to 8.1. I would like to start any applications with a specific window's size, because few applications (OpenOffice, LibreOffice...), start flattened when i launch them for the first time.
I've tested to maximize this window with ShellExecute with ShowCommands.SW_MAXIMIZE parameter:
ShellExecute(IntPtr.Zero, "open", executablePath, executableParam, "", ShowCommands.SW_MAXIMIZE);
But when I clicked on the "restore" button of the window, there is the same problem, the window is flattened.
While the "restored" size isn't configured by manual resizing, the used value is specific for each applications.
Instead of ShellExecute, I use CreateProcess to specify a size :
const uint NORMAL_PRIORITY_CLASS = 0x0020;
STARTUPINFO si = new STARTUPINFO();
si.dwY = 50;
si.dwX = 50;
si.dwXSize = 200;
si.dwYSize = 800;
si.dwFlags = 0x00000006; //STARTF_USESIZE + STARTF_USEPOSITION
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
CreateProcess(programPath, programParams, IntPtr.Zero, IntPtr.Zero, true, NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref si, out pi);
But there is one problem, we can launch an other OpenOffice/LibreOffice process with File->New->... In this case the application isn't launch by my program so my default size isn't applied.
I've checked the Windows Registry before and after changing this value, two keys are changed :
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\OpenSavePidlMRU\reg]
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\Count]
These keys contains hexa values (first key) :
before :
"MRUListEx"=hex:00,00,00,00,04,00,00,00,01,00,00,00,05,00,00,00,03,00,00,00,02,\00,00,00,ff,ff,ff,ff
after :
"MRUListEx"=hex:01,00,00,00,00,00,00,00,04,00,00,00,05,00,00,00,03,00,00,00,02,\00,00,00,ff,ff,ff,ff
It isn't understandable to me, so I'm still searching if exist a programmatic way to specific window size of an application/all applications or an other specification to set into the Windows Registry. I'm not searching a solution to resize or move a window already launched like SetWindowPos.
But many websites explain there's just one solution to define the window's size: "drag the window to the size, then close the window and restart it"
http://help.wfu.edu/public/computers/standard-load-software/windows-7---set-default-window-size
http://www.tomshardware.co.uk/forum/28659-45-change-default-window-size
This answer is the final solution or there is a way to do it programmatically?
Thanks in advance.
EDIT :
Actually, when a window is restored, I resize only the first time until the user kill it.
My program keeps every window's handle in an object with a boolean initialized to false to specify if my window has been resized. I use GetForegroundWindow then with GetWindowPlacement I check if WINDOWPLACEMENT.flags == WindowPlacementFlag.WPF_NONE and if my boolean is always egals to false in that case I use GetWindowRect and SetWindowPos to set a specific size with the same position and assign my boolean to true.
I think is a good solution, but I loose the saved position by user (I can do it when window's size is under a specific value but the problem still there) and we seen the resizing...
This problem usually occurs because the coder used GetWindowPos to get the position to save, but the window is closed while minimised, so the position is a tiny window hidden by the taskbar. This is then the position which is saved. The correct method is to use GetWindowPlacement to save the position.
You could enumerate all top-level windows, check GetWindowPlacement to see which are visible and restored but with an inappropriate size or position (e.g. off-screen, too small). Then you could call SetWindowPlacement to move those windows back to a reasonable minimum size on-screen.
In all likelihood when they save their position again they will save the new position.
Don't poll though. Instead make it a utility that the user can run when they need to. (You will probably need an exceptions list too - some utilities might create an off-screen window on purpose.)
I am playing a video using IVMRWindowlessControl9 i.e. WindowlessControl w/ VMR-9. When I initially had difficulty setting the video up, I noticed many examples found online had this piece of code in the Window's Paint handler:
IntPtr hdc = e.Graphics.GetHdc();
int hr = 0;
hr = windowlessCtrl.RepaintVideo(this.Handle, hdc);
e.Graphics.ReleaseHdc(hdc);
My video is playing correctly yet I do not have the above code. What does the above code do and is there a point in using it?
In most cases you don't need to repaint the video explicitly because the video renderer does it automatically. Yes, if the hosting window needs - for any reason - to request forced replaint, such as as part of WM_PAINT window message handler, then RepaintVideo method is here at your service.
See more on MSDN, VMR Windowless Mode:
During playback, the application should notify the VMR of the
following Windows messages:
WM_PAINT: Call IVMRWindowlessControl::RepaintVideo to repaint the
image.
As to what it does:
if the video is being drawn using a destination colour key, then it paints the key colour into the destination window.
if the image is letterboxed, or part of it falls on another monitor, it will paint the parts that are not covered by the image.
So it may be that you won't see any difference if it is not done at all, if none of these things apply.
I'm writing a simple Windows Forms utility to screenshot either the entire screen or an active window and save the file. The winform has a button called 'Capture' that, when clicked, takes the screenshot.
I have the code entirely working for the entire screen but can't figure it out for the active window.
By my current code, if I select 'Active Window', it ends up Print Screening the application itself which isn't what I want.
Here's my code (under the Capture_Click method):
try
{
this.Hide();
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
//bmpScreenshot = new Bitmap(this.Bounds.Width, this.Bounds.Height, PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
if (AltPrint.Checked)
{
gfxScreenshot.CopyFromScreen(this.Bounds.X, this.Bounds.Y, 0, 0, this.Bounds.Size, CopyPixelOperation.SourceCopy);
}
else
{
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
}
bmpScreenshot.Save(SaveLocation, ImageFormat.Png);
number++;
}
In your button_Click method, to capture a window other than the one which is doing the capturing, the only possible way I can see is to make use of the user32 Windows API.
You will first need to platform invoke some methods like this:
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr hWnd, GetWindowLongNIndex nIndex);
I recommend www.pinvoke.net/ for more info about this.
Here is my own Window class (work in progress, but useable) which you are welcome to use as an example: http://pastebin.com/fj2tEaPY
Some useful properties in there for you are:
get AllWindows
get TaskbarWindows
set ForegroundWindow
You'll need to get the handle of your current window (use get ForegroundWindow, then the Handle property), then find the window which you want to capture the image of, perhaps using code similar to that in TaskbarWindows. Once you have the window, you will need to send the keypresses Alt + PrtScn (again maybe using user32 API, but I won't go into it.) Then use the static ForegroundWindow property to set the orignal window as the forground using the handle which you saved at the start.
Good luck!
As far as I can see your code uses the bounds of its own form to capture the "active" window. That is probably not what you intend.
If you want to use the mouse to "print" a window (and not hotkey which probably is easier to implement) you need to be able to from your own form let the user point to another window to capture. Here is an idea describing how to do that:
When the user presses the "Capture" button capture the mouse and initiate a drag operation.
Let the user drag the mouse (perhaps now having a "bullseye" or a "camera" cursor) to the window to capture.
When the user releases the mouse determine the window it was on top and use the location and size of this window to capture the correct pixels.
You will probably need to use P/Invoke to get information about all top-level windows on the desktop.
Note that "mouse capture" is very different from "screen capture". When an application "captures the mouse" it takes ownership of the mouse cursor and doesn't the ability to track the mouse even when it is moved outside the bounds of the window of the application.
Your comments seem to understand that once your app gets the focus (when you click on it) the other app has lost it and is no longer 'active'.
A possible alternative: your application gets a WM_SETFOCUS message when it gets focus. The wParam of that message is a handle to whichever window has just given up the focus -- and that window belongs to the app you want to capture.
Of course, that window will only be a subwindow or control, but you can figure out which app owns that button.
I ended up taking a different route.
I allow the user to resize my form window to cover exactly the area they need to screenshot, and then when they click the button, the form hides itself, takes a screenshot of the area it was covering and appears again. Contrary to my initial plan, I believe this will be more beneficial, as my targeted users (friends at an overclocking forum I'm part of) seldom take screenshots of just one window. They mostly need a couple of windows covered (temp monitoring, clockspeed monitoring, stability test etc.).
Thanks for all the answers guys. They definitely got me thinking, and made me realize what my options were.
Should anyone else need it, this is the code I use for the button that captures only part of the screen:
try
{
this.Hide();
Thread.Sleep(250);
bmpScreenshot = new Bitmap(this.Bounds.Width, this.Bounds.Height, PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(this.Bounds.X, this.Bounds.Y, 0, 0, this.Bounds.Size, CopyPixelOperation.SourceCopy);
bmpScreenshot.Save(SaveLocation, ImageFormat.Png);
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText("Screenshot saved at " + SaveLocation);
numSuffix++;
}
catch (Exception ex)
{
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText(Environment.NewLine);
tbxStatus.AppendText("Unable to take screenshot. Exception: " + ex.ToString());
}
finally
{
this.Show();
}
You need to borrow the design of commercially available screen capturing apps, such as Psp. I think you need to use a 'hot' key to trigger the capturing of the active window. Instead of doing it on Capture_Click event. This way, you can hide your application's window so that it does not become the active window. Then when the user pushes the 'hot' key, you can capture it.
What about SendKeys?
Windows.Forms.SendKeys.Send("%{PRTSC}");
It will copy currently selected window in to Clipboard object.
Than calling
BitmapSource image = Clipboard.GetImage();
you can get your image back