Use Magnification API to capture sub screen failed on Windows10 - c++

Magnification API can work well when used to capture primary screen, but when i use it to capture sub screen,after MagSetWindowSource() called, MagSetImageScalingCallback() not triggerd.
I checked the window position is set correctly, in my computer is {-1080, -250, 0, 1670}, and i showed the window, it placed on the right position.
I use the following code to get sub screenshot, just same with the webrtc code, but MagSetImageScalingCallback() not triggerd.
// Create the host window.
host_window_ = CreateWindowExW(WS_EX_LAYERED, kMagnifierHostClass,
kHostWindowName, 0, 0,
0, 0, 0, nullptr, nullptr, hInstance, nullptr);
// Create the magnifier control.
magnifier_window_ = CreateWindowW(kMagnifierWindowClass,
kMagnifierWindowName,
WS_CHILD | WS_VISIBLE, 0, 0, 0, 0,
host_window_, nullptr, hInstance, nullptr);
BOOL result = SetWindowPos(magnifier_window_, NULL, rect.left(), rect.top(),
rect.width(), rect.height(), 0);
// value is -1080, -250, 0, 1670
RECT native_rect = {rect.left(), rect.top(), rect.right(), rect.bottom()};
result = set_window_source_func_(magnifier_window_, native_rect);
The working environment is Windows 10 Professional 64bit, my application is also 64bit, my primary screen is plugged in discrete graphics card, sub screen is plugged in integrated graphics.

In the documentation of the MagSetImageScalingCallback, which can be found here, it specifies a few things to take into account:
"This function requires Windows Display Driver Model (WDDM)-capable video cards." You might want to check if the other screen is running via a different videocard, maybe the driver isn't WDDM capable.
"This function works only when Desktop Window Manager (DWM) is off." I'm not sure how much of this statement is true, but as of Windows 8 DWM can no longer be programmatically disabled. You might come into some border cases, I do not think the documentation of this API is kept up to date with all the quicks it might have.
One very important note, you might want to look for a different solution, as this is specified in the documentation: "The MagSetImageScalingCallback function is deprecated in Windows 7 and later, and should not be used in new applications. There is no alternate functionality."
Is there any specific reason why you want to use this Windows API, maybe an alternative is a better choice? I am quite familiar with making screenshots, but I'm more of a C# guy, but some c++ examples that look usable can be found here. The GDI way pretty much describes what I use in Greenshot, and this works without any big issues.
P.S.
If my information doesn't help, you might want to extend your question with more information on your setup, like Windows version, is it 32/64 bit, is your application 32/64 bit, graphics card, screens etc.

Related

SetLayeredWindowAttributes not working under certain conditions

I'm making this app in Haxe which compiles to C++ code. I'm using the SetLayeredWindowAttributes() function to set the background of the window to be fully transparent. The problem is that this works completely fine only on certain monitors, but not on others.
If I launch the app on my main monitor, which I believe to be 32-bit (if that matters), the background doesn't change at all, or will flicker and then completely disappear until I focus another window.
However, if I connect another monitor and run the app on that one, the background does become transparent, and the transparency persists even if I move it back to my main monitor. Even with the 2nd monitor connected at the same time. If I launch it on the main one, transparency fails.
I have tried changing the color values between 0xFFffffff/0xffffff, etc, but no combination of these work.
This same problem occurs even on a completely different machine with different hardware than mine. My hardware: Lenovo Ideapad L340-15IRH, i5-9300H, Gpu(s) Gtx 1650, Intel HD 630.
Please help me, what am I doing wrong?
Code to enable transparency, using windows.h inside a C++ function:
HWND hWnd = GetActiveWindow();
res = SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
SetLayeredWindowAttributes(hWnd, RGB(255, 255, 255), 0, LWA_COLORKEY);
Code which creates the transparent background, in main haxe code:
transparentsquare = new FlxSprite(0, 0).makeGraphic(screenW, screenH, FlxColor.WHITE);
add(transparentsquare);
It doesn't matter if I enable transparency before everything has loaded, I can enable it by pressing a key at any time and it still fails if I enable it after the app has fully loaded. Also, obviously graphics are not drawn in the C++ function but in the haxe code, so I can't use any fancy DirectX APIs and what-not.

How to get the "display stream" of a MS Windows window?

I have a program (we will call it the "virtual screen") that create a full screen window and start arbitrary programs, and with the help of hooks (CBTProc) get handles to windows that started programs create. From those handles I retrieve the content of the windows (using GetDIBits) and displays it in the "virtual screen" window.
Currently, this "virtual screen" copy content of windows and then redraw them, which make it work, sort of like a mirroring software.
Here is how I get the content of a window:
struct WindowContent {
void *pixel;
int width;
int height;
};
WindowContent getWindowContent(HWND hWnd, int height, int width)
{
WindowContent content;
WINDOWINFO windowInfo;
GetWindowInfo(hWnd, &windowInfo);
content.height = windowInfo.rcClient.right - windowInfo.rcClient.left;
content.width = windowInfo.rcClient.bottom - windowInfo.rcClient.top;
HDC hdc = GetDC(hWnd);
HDC captureHdc = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, content.width, content.height);
HGDIOBJ oldHdc = SelectObject(captureHdc, hBitmap);
BitBlt(captureHdc, 0, 0, content.width, content.height, hdc, 0, 0, SRCCOPY|CAPTUREBLT);
SelectObject(captureHdc, oldHdc);
DeleteDC(captureHdc);
BITMAPINFO outputBitmapInfo = {};
outputBitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
GetDIBits(hdc, hBitmap, 0, 0, NULL, &outputBitmapInfo, DIB_RGB_COLORS);
content.pixel = (BYTE *)malloc(outputBitmapInfo.bmiHeader.biSizeImage);
outputBitmapInfo.bmiHeader.biCompression = BI_RGB;
outputBitmapInfo.bmiHeader.biBitCount = 32;
GetDIBits(hdc, hBitmap, 0, outputBitmapInfo.bmiHeader.biHeight, content.pixel, &outputBitmapInfo, DIB_RGB_COLORS);
return content;
}
My question is, how do I remove the copying part, how can I make an area on my "virtual screen" the window output for those programs ?
Emphasis on the fact that I'm trying to make created windows be the area on the "virtual screen", I don't want an additional window hidden or living on the desktop.
In my research, I've looked into Windows DWM DLLs and found some undocumented function (SignalRedirectionStartComplete or MilConnection_CreateChannel) which names look linked to what I want to do, but I don't think I should use them, as they are undocumented.
Also, the code is using Win32 API but I don't mind using another Windows API or another language (C#, DX* ...).
Forgot to mention, I have already thought about using the DWM thumbnail stuff, but it's not that reliable enough for what I'm trying to do.
As far as I understand Windows 10 uses DX under the hood for all display output, for GDI, and even for Vulkan / OpenGL programs, and someone used it to make a lib that
gets DX 10 texture from a window (). Is it possible to make something similar, that, for a specific HWND, set its "output" to a texture or some region in
memory (swapchain redirection ?) instead of the screen and then display the output in another program (in my case, on the "virtual screen" window) ?
The DWM was a reasonable place to look. It has some documented functions to get you at least a little closer to what you want.
You can register your window as a "thumbnail" viewer using DwmRegisterThumbnail. Then you (probably) call DwmUpdateThumbnailProperties to tell it how to draw to your window (e.g., set opacity, rectangle to draw to, and whether to draw the whole source window, or just its client area). When you're done, you call DwmUnregisterThumbnail to unhook from displaying it.
This is only a little closer to what you want though--it eliminates your having to copy bitmaps from the source into your own window--but that's about all it does. The target application will still have its own window running elsewhere.
If you want to do more to hide the application, you could create another desktop, and display the application in that desktop. If you don't provide a way to switch desktops, that can keep it pretty well hidden. On the other hand, there are external tools that will let the user change desktops, which would let them see the application directly.
One other avenue to consider (for a subset of applications) would be COM. Part of what COM supports is having a COM server that displays its output inside the frame of some COM client's window. That's no longer nearly the big thing it once was, but (I believe) all the code to support it is still available. But, it only works for applications that are specifically written to support it (which, honestly, isn't a huge number). The code for this isn't exactly trivial either.

Allegro color change in fullscreen mode

I am learning to use the allegro library right now and when using the set_gfx_mode function if I use GFX_AUTODETECT_FULLSCREEN for the first argument the window will go fullscreen when running the compiled application, but after about the first second of running, all the colors change. Using any other graphics mode this doesn't happen, but on two separate machines the colors change just after changing to fullscreen mode. Has anybody else seen this happen before? I can't find any discussion on this problem at all.
I am using the pre-compiled allegro 4.4.2 library for visual studio 2010 and running windows 7.
Allegro 4 is old and uses APIs that are no longer very well supported by modern operating systems. The full screen mode is going to be buggy, especially on 8-bit graphics. The best way to get a reliable full screen is to honor the user's current desktop settings:
int w, h;
get_desktop_resolution(&w, &h);
set_color_depth(desktop_color_depth());
set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, w, h, 0, 0);
Then your application will need to center/scale the drawing. It's not really that difficult, just draw everything to an intermediate buffer that is the width/height of your native game, and then stretch blit it to the appropriate screen size.
All that said, you should really be learning Allegro 5 as it is designed to work on today's hardware and operating systems, including iOS and Android.

C++ - How to screen-capture, except for some windows

Situation: I have a software that performs screen sharing over the Internet, where one user acts as a presenter, and other users act as viewers/attendees.
Besides the presentation windows, the presenter also has a set of NON-SHARING-WINDOWS that appear on the screen (a button bar for start sharing/stop sharing/etc., a Skype window etc.).
The presenter can configure from the setup of the screen sharing software to make these NON-SHARING-WINDOWS invisible (i.e. they will not appear in the screen sharing that is being sent to the attendees, but the window content behind them will appear in the screenshot).
The screenshots are sent at approximately 10 frames-per-second, or faster.
Question: how can I programmatically capture the screen, except for these NON-SHARING-WINDOWS windows?
Notes:
Because of the higher frames-per-second value, I cannot minimize/maximize/set alpha for these windows, because then the windows will flicker. The application is written in Win32 C++.
I would use layered windows, but because of the Windows 7 Desktop Composition feature, this is not usable out-of-the-box (and in Windows 8, you cannot use DwmEnableComposition anymore to temporarily and programmatically disable composition)
I could use the layered window approach for Windows XP/2000/7 etc., and a different approach for Windows 8 (if there is one), though I would prefer a single process that works on all systems
I could also try to "compose" the screenshots by capturing individual images (of the desktop, the windows that need to be captured) and using their z-index to create the final image, but because of the required frames-per-second value, this process would be too slow.
In windows even the desktop is considered a window and has its own HWND.
It seems however, not easily possible to only copy the "wallpaper" on its own.
So i basically see two ways to do that.
1. Copy the entire desktop e.g. BitBlt(GetWindowDC(GetDesktopWindow()),...)
OR
Use GetWindow and traverse the window list in backward direction starting from the Desktop-Window whose HWND you just can determine with GetDesktopWindow(), Like this:
// paint on a black DC
hwnd=GetDesktopWindow()
while (hwnd = GetWindow(hwnd, GW_HWNDPREV))
{
// is this window not shared? continue
// else bitblt it into our dc
}
Hope i gave some inspiration :-)
If someone knows a way how to copy ONLY the desktop without its child windows please let me know.
You can use Magnifier API.
There is a function in magnifier API that allows you to exclude specific windows from your target window (your window with 1x magnification where magnifier renders).
You can set this window to full screen and make it transparent and then use PrintWindow function.
The function: https://learn.microsoft.com/en-us/windows/desktop/api/magnification/nf-magnification-magsetwindowfilterlist
Sample projects:
https://www.codeproject.com/Articles/607288/Screenshot-using-the-Magnification-library
https://code.msdn.microsoft.com/windowsdesktop/Magnification-API-Sample-14269fd2
I'm aware this question is pretty old, but I ran into the same problem and it was very, very hard to find any information at all regarding this.
Since Windows 10 version 2004 (build 10.0.19041), the SetWindowDisplayAffinity API has been expanded to include a flag called WDA_EXCLUDEFROMCAPTURE (0x00000011). This will remove the window from images captured with BitBlt
The window is displayed only on a monitor. Everywhere else, the window does not appear at all.
One use for this affinity is for windows that show video recording controls, so that the controls are not included in the capture.
Introduced in Windows 10 Version 2004. See remarks about compatibility regarding previous versions of Windows.
For versions before 2004, it will use the existing WDA_MONITOR flag.
I have tested this with a screen capture of the desktop and I am unsure what would happen if you were to use a window DC.
So I guess a possible solution would be:
// get window handle
hWnd = (...)
BOOL result = SetWindowDisplayAffinity(m_hWnd, WDA_EXCLUDEFROMCAPTURE);
// do bitblt stuff
mabye you can use Magnification API, even Microsoft said The MagImageScalingCallback function is deprecated in Windows 7 and later, and should not be used in new applications. There is no alternate functionality., but it still work on Windows 10;
Here is the overview of this API : https://learn.microsoft.com/en-us/previous-versions/windows/desktop/magapi/magapi-intro
The sample code of Microsoft is here : https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Magnification
If you want to get the screenshot rgb data, you can use this api MagSetImageScalingCallback to set callback of Magnifier window, every time you use MagSetWindowSource or InvalidRect of magnifer window, this callback function MagImageScalingCallback will be called, so you can get screenshot rgb data here.
I think that to limit the capture content within a big window will be more simple. otherwise you will need to cut some windows from the screen capture.

SetLayeredWindowAttributes not working on Windows 7

I am using SetLayeredWindowAttributer to make a particular color of layered window transparent.
This works fine on Windows XP,VISTA . But when I use the same on Windows 7 its not working.
SetLayeredWindowAttributes(hWnd, RGB(0xff,0xff,0xff), 0, LWA_COLORKEY);
When I use LWA_ALPHA then also it works.
Problem is that I am not able to make a particular color transparent in Windows 7.
The following statement works on Windows 7
SetLayeredWindowAttributes(hWnd,RGB(0xff,0xff,0xff), 100, LWA_ALPHA);
Is it possible that the rendered color values not matching the color value in SetLayeredWindowAttributes?
You should avoid using 0xff,0xff,0xff (white) with LWA_COLORKEY. Any other value should be fine (e.g. 0xff,0xff,0xfe).
For more control over your layered window I suggest you consider using UpdateLayeredWindowIndirect. I wrote an article that describes in detail how it can be used with both GDI and Direct2D.
http://msdn.microsoft.com/en-us/magazine/ee819134.aspx