Take screenshot of external OpenGL game C# with BitBlt (CopyFromScreen) - c#

I am trying to create some kind of antycheat for counter strike (hl) game. Of course funcionality of making a screenshot in-game is built-in, but exploited (detected) by antyss applications, so every time screenshot is taken from the game, antyss is disabling the cheats (so that no cheats are visible on the screenshots)
For the last few days, I've read dozens of threads regarding this topic. Most of them are outdated and are using libraries, that are obsolete right now.
I've read about the approach with mirage driver (which is not working on windows 10), about injecting to the application (of course application/game is not part of my code) and using/incjeting some code with OPEN GL/D3D library (to read backbuffer). Probably this could be in the end the only solution.
But right now I have almost a working solution. I write "almost" because it is working but giving me only some kind of "cached" data. It is giving me a correct screenshot, but if I take another screenshot - still the same screenshot is taken as last time. If while being in-game I minimize the application (full-screen mode) and then get back to the game, the new screenshot taken will have up to date screenshot, but then again, the next screenshot would be exactly the same.
I don't know if it is "by design" or is it "some sort of bug" Nevertheless my question is: Can I force somehow this "reloading" without having to programmatically call some kind of "alt+tab" and then focusing on the application once again?
In this topic:
How to take screenshots of a game with OpenGL
#Andon M. Coleman wrote:
Are you on Windows? In fullscreen mode starting with Windows Vista, there is trouble with anything that tries to capture the front-buffer (including the built-in Alt + PrintScreen). The easiest solution is to change your buffer swap behavior to Copy Swap (PFD_SWAP_COPY, slower but guaranteed to work). Often if you Alt+TAB out and back in after making the fullscreen mode switch that will fix it too; though I have never been able to explain that ;) If you did not write the game in the question, then the second solution may be your only choice if you want to use that code.
This is exactly the problem I am facing. As he wrote: "Alt+Tab" is fixing the problem (although he did not know whether it is a feature or a bug) He proposed to change the buffer swap behavior to Copy Swap(PFD_SWAP_COPY) Any tips on how to change my code with that will also be most welcome (I can try this one) But if I understood correctly, this approach is the viable solution only if you can change this in the game (and this is not my case)
Here is my working code (which in topics about such scenarios was claiming that in this approach the screenshot is BLACK. But it is working for me)
private const int SW_RESTORE = 9;
public void TakeScreenShot()
{
var guid = Guid.NewGuid();
string procName = "hl";
Process proc;
try
{
proc = Process.GetProcessesByName(procName)[0];
}
catch (IndexOutOfRangeException e)
{
return;
}
// Focus on the application
SetForegroundWindow(proc.MainWindowHandle);
ShowWindow(proc.MainWindowHandle, SW_RESTORE);
Thread.Sleep(1000);
Rect rect = new Rect();
IntPtr error = GetWindowRect(proc.MainWindowHandle, ref rect);
while (error == (IntPtr)0)
{
error = GetWindowRect(proc.MainWindowHandle, ref rect);
}
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
using (Bitmap printscreen = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
using (var graphics = Graphics.FromImage(printscreen))
{
graphics.CopyFromScreen(rect.left,
rect.top,
0,
0,
new Size(width, height),
CopyPixelOperation.SourceCopy);
printscreen.Save($#"{Path.GetTempPath()}\{guid.ToString()}.jpg", ImageFormat.Jpeg);
}
}
}
I want this application to work on Windows7, Windows8, Windows 10. The best would be to cover full screen and windowed mode (but fullscreen is probably more important)
Any advice how to proceed (or why I am getting the "cached" data) would be nice :)
Of course if someone will say (with full authority), that what i want to achieve is impossible with CopyFromScreen (and there is no hack to fix that, apart from minimizing and maximazing the screen) i will consider option with injecting the code. But normally i would want to stay away from this one, as this could be treated as cheat and can lead to VAC ban.
====== UPDATE ======
You can try reproduce the process of taking screenshot by downloading the game (is small one, 260 MB):
https://cssetti.pl/Api/GameDownload.php?GameDownloadId=v43
Then you can copy-paste my code to Linqpad (or any other editor) and run the code. The application after launching will launch the HL process which is then use to try to grab the screenshot.
====== UPDATE 2 ======
In windows mode everything works correctly (the printscreens are ok)

Related

Is there any way to set the left and right margin of a console using Terminal sequences? the same way we can currently set Top and Bottom?

some background,
I'm the author of a C# console library called Konsole (link below).
https://github.com/goblinfactory/konsole
The primary feature of the library is to provide a simple DSL for creating scrollable console windows like so ...
This works really well on windows, but currently does not work on 'nix' because the .NET Core port of Console is incomplete. Yeah, it's incomplete Sobs! if you call Console.MoveBufferArea(...), you'll receive a stern PlatformNotSupported rebuking, like so ...
Unhandled exception. System.PlatformNotSupportedException: Operation is not supported on this platform.
at System.ConsolePal.MoveBufferArea(Int32 sourceLeft, Int32 sourceTop, Int32 sourceWidth, Int32 sourceHeight, Int32 targetLeft, Int32 targetTop, Char sourceChar, ConsoleColor sourceForeColor, ConsoleColor sourceBackColor)
at System.Console.MoveBufferArea(Int32 sourceLeft, Int32 sourceTop, Int32 sourceWidth, Int32 sourceHeight, Int32 targetLeft, Int32 targetTop)
I've managed to get my library to work around everything except the MoveBufferArea method, and almost have a nice hack work around using Virtual-terminal-sequences, hence this SO question.
...when running on Mac in a terminal window, I can scroll a (100% wide) region of the window, by setting the Scrolling Region using terminal-sequences, but can only set the top and bottom margin, using virtual-terminal-sequences.
from : https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#viewport-positioning
So, using terminal sequences I can set the Top and Bottom margin, then I can call scroll, then I reset the scroll regions and return to my library for all other window writing.
The problem is (as per the title) that I can not find any way to set the left and right margin, which means I cannot scroll a neat (startx, startY, width, height) portion of the screen, I am forced to scroll the whole width.
This feels SOOO close. if I can find a hack to tell a Mac (Nix) window to please scroll a region of a console window, or, I can set the Left and Right margin before calling scrollup, or ScrollDown, then that would also solve me my problem and my Konsole library would be fully cross platform.
This can apparently be done using something called ncurses but all the documentation I have found for that appear to require that you do some wierd mind meld compile time magic to get them to work.
Any help would be amazing and super super appreciated!
here's some code that shows how the terminal sequences are used
private static void SetScrollMargins(int top, int bottom) => Console.Write("\x1b[" + top + ";" + bottom + "r");
private static void ScrollUp() => Console.Write("\x1b[1S");
private static void ScrollDown() => Console.Write("\x1b[1T");
static void Main(string[] args)
{
try
{
Console.Clear();
// fill the screen with random stuff so that we can see
// that a small portion (region) of the screen is moved up and down
// with each call to scroll up/down without the rest of the screen
// being impacted. Demonstrates that all I am missing is left and right margins
// and my problem is solved. Sooo close, but sooo far!
for (int i = 0; i < 800; i++) Console.Write($" ({i} ) ");
Console.WriteLine();
Console.WriteLine("set scrollmargins, press a key");
Console.ReadKey(true);
SetScrollMargins(2, 5);
for (int i = 0; i < 20; i++)
{
Console.ReadKey(true);
ScrollUp();
Console.ReadKey(true);
ScrollDown();
}
}
So ideally I'm hoping I've missed an obvious trick where you can in fact set the left and right margin, using terminal-sequences, or via some other terminal sequence code that Mac Terminals support.
Thanks to everyone in advance. Happy new year too :D
update:
I managed to find some tests that hint at the functionality being plausible. e.g. DECLRMM (https://vt100.net/docs/vt510-rm/DECLRMM.html)
This Java test for example
public void testScrollRegionRight() {
// ${CSI}?69h for DECLRMM enabling, ${CSI}${LEFTMARGIN};${RIGHTMARGIN}s for DECSLRM margin setting.
withTerminalSized(3, 3).enterString("YYY\033[?69h\033[1;2sABCDEF").assertLinesAre("ABY", "CD ", "EF ");
enterString("GH").assertLinesAre("CDY", "EF ", "GH ").enterString("IJ").assertLinesAre("EFY", "GH ", "IJ ");
enterString("\n").assertLinesAre("GHY", "IJ ", " ");
}
I've tried this, but can't get this to work, seems to be ignored, and possibly this open happens with terminal.app on Mac, as #Thomas Dickey has suggested in the comments.

How do screenshot in c# (low level may be) when desktop blocked?

I try get screenshot (windows 8) with code and get black screen when desktop blocked:
public static Bitmap ImageFromScreen()
{
Graphics gr;
Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height,PixelFormat.Format32bppRgb);
gr = Graphics.FromImage(bmp);
gr.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y,
0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
return bmp;
}
You don't.
Apart from window cache (which isn't accessible, and doesn't really have to be there), the data simply isn't there. You'd have to fake WM_PAINT messages and force the application to draw to your surface (most controls will take the HDC from wParam; that still excludes a ton of badly written (or not Windows-native) applications - and even then, this will not work most of the time, like when the windows are minimized or the desktop is locked.
What is it that you're actually trying to do?
EDIT:
Okay, it's obvious that you're explicitly talking about the "locked screen" case - there's no way to get a screenshot of the desktop in that case - it doesn't really exist. The lock screen exists in a different session, so you no longer have any connection to the "hidden" user session. This is similar to trying to take screenshots on a server application after you've disconnected Remote Desktop - there's nothing to take shots of. It may be possible to force some applications to draw to your context, but nothing that would simply work.

C# opengl context handle getter returns wrong address

Problem solved!
Deleted pragma of sharing from kernel string.(using opencl 1.2)
Reordered GL-VBO-creating and CL-Context-Creating. First create CL-context from gl-context. Then create GL-VBO. Then acquire it by cl. Then compute. Then release by cl. Then bind by gl. Draw. Finish gl. Start over. Use clFinish always to ensure it synchs with gl. For more speed, clflush can be okay maybe even an implicit sync can be done which I did not try.
[original question from here]
In C#, context construction for opencl-gl-interop fails because handle getter function gives wrong address and causes System.AccessViolationException.
C# part:
[DllImport("opengl32.dll",EntryPoint="wglGetCurrentDC")]
extern static IntPtr wglGetCurrentDC();//CAl
[DllImport("opengl32.dll", EntryPoint = "wglGetCurrentContext")]
extern static IntPtr wglGetCurrentContext();// DCAl
C++ part in opencl(this is in a wrapper class of C++ opencl):
pl = new cl_platform_id[2];
clGetPlatformIDs( 1, pl, NULL);
cl_context_properties props[] ={ CL_GL_CONTEXT_KHR, (cl_context_properties)CAl,
CL_WGL_HDC_KHR, (cl_context_properties)DCAl,CL_CONTEXT_PLATFORM,
(cl_context_properties)&pl[0], 0};
ctx=cl::Context(CL_DEVICE_TYPE_GPU,props,NULL,NULL,NULL);//error comes from here
//ctx=cl::Context(CL_DEVICE_TYPE_GPU); this does not interop >:c
What is wrong in these parts? When I change "opengl32.dll" to "opengl64.dll" compiler/linker cannot find it.
Calling wglGetCurrentDC() and wglGetCurrentContext() after glControl1 is loaded but these seem to be giving wrong addresses. Calling wglMakeCurrent() or glControl1.MakeCurrent() before those did not solve the problem too.
OS: 64 bit windows7
Host: fx8150
Device: HD7870
MSVC2012(windows forms application) + OpenTK(2010_10_6) + Khronos opencl 1.2 headers
Build target is x64(release).
Note: opencl part is working well for computing(sgemm) and opengl part is drawing VBO well (some plane built of triangles with some color and normals) but opencl part(context) refuses to interop.
Edit: Adding #pragma OPENCL EXTENSION cl_khr_gl_sharing : enable into kernel string did not solve the problem.
Edit: Creating GL VBOs "after" the construction of cl context, error vanishes but nothing is updated by opencl kernel. Weird. PLus, when I delete cl_khr_sharing pragma, the 3D shape starts artifacting which means opencl is doing something now but its just random deleted pixels and some cropped areas which I did not wrote in kernel. Weirdier. You can see this in below picture(I am trying to make the flat blue sheet disappear but it doesnt fully disappear and also i try changing color and that is not changing)
Edit: CMSoft's opencltemplate looks like what I need to learn/do but their example code consists only 6-7 lines of code! I dont know where to put compute kernel and where to get/set initial data, but that example works great(gives hundreds of "WARNING! ComputeBuffer{T}(575296656) leaked." by the way).
Edit: In case you wonder, here is kernel arguments' construction in C++:
//v1,v2,v3,v4 are unsigned int taken from `bindbuffer` of GL in C#
//so v1 is buf[0] and v2 is buf[1] and so goes like this
glBuf1=cl::BufferGL(ctx,CL_MEM_READ_WRITE,v1,0);
glBuf2=cl::BufferGL(ctx,CL_MEM_READ_WRITE,v2,0);
glBuf3=cl::BufferGL(ctx,CL_MEM_READ_WRITE,v3,0);
glBuf4=cl::BufferGL(ctx,CL_MEM_READ_WRITE,v4,0);
and here is how set into command queue:
v.clear();
v.push_back(glBuf1);
v.push_back(glBuf2);
v.push_back(glBuf3);
v.push_back(glBuf4);
cq.enqueueAcquireGLObjects(&v,0,0);
cq.finish();
and here is how I set as arguments of kernel:
kernel.setArg(0,glBuf1);
kernel.setArg(1,glBuf2);
kernel.setArg(2,glBuf3);
kernel.setArg(3,glBuf3);
here is how executed:
cq.enqueueNDRangeKernel(kernel,referans,Global,Local);
cq.flush();
cq.finish();
here is how released:
cq.enqueueReleaseGLObjects(&v,0,0);
cq.finish();
Simulation iteration:
for (int i = 0; i < 200; i++)
{
GL.Finish(); // lets cl take over
//cl acquires buffers in glTest
clh.glTest(gci.buf[0], gci.buf[1], gci.buf[2], gci.buf[3]);// then computes
// then releases
Thread.Sleep(50);
glControl1.MakeCurrent();
glControl1.Invalidate();
gci.ciz(); //draw
}

How can I update the frame of a Windows Media Player COM?

I have a windows media player COM in my Windows Form project that plays and opens videos admirably. However, I would like to be able to grab the first frame of the loaded video so my program users can preview the video (and, ideally, recognize one video from another).
How can I update the frame displayed by the windows media player object?
I have tried using the following code at the end of my openFileDialog event response:
private void openFileDialog1_FileOk(object sender, CancelEventArgs e)
{
Text = openFileDialog1.SafeFileName + " - MPlayer 2.0";
//mediaPlayer1.openPlayer(openFileDialog1.FileName);
mediaPlayer1.URL = openFileDialog1.FileName;
//hopefully, this will load the first frame.
mediaPlayer1.Ctlcontrols.play();
mediaPlayer1.Ctlcontrols.pause();
}
However, when I run this, the pause command gets ignored (Auto-play for video loading is turned off, so the video won't start playing without calling .play(), above). If I had to guess, I'd say that this is because of some threading operation that calls play, moves on to call pause, calls pause, and then, finally, the play resolves, and the video starts - but because the .pause resolved before the .play, the net effect is the .pause is ultimately unheeded.
Firstly, is there a way other than .play(); .pause(); to snag a preview image of the video for the AxWindowsMediaPlayer object? If not, how can I make sure that my .pause() doesn't get ignored?
(I know that .play(); .pause(); works in the general case, because I tested with a separate button that invoked those two methods after the video finished loading, and it worked as expected)
You can't do a lot of things with this COM. However Follow this link and you will find a Class that will help you extract an image from a Video file. You could simply get extract the image and just put it in top of the video, or next to it. This is a simple workaround for your requirement. If not happy with it, I would strongly recommend not using this COM at all and use some other open source video player/plugins. There a lot of real good ones, but I could recommend the VLC Plugin, or try finding another.
Good luck in your quest.
Hanlet
While the Windows Media Player Com might not officially support a feature like this, its quite easy to 'fake' this. If you use a CtlControls2, you have access to the built-in "step(1)" function, which proceeds exactly one frame.
I've discovered that if you call step(1) after calling pause(), searching on the trackbar will also update the video.
It's not pretty, but it works!
This is a little trick to solve the common step(-1) not working issue.
IWMPControls2 Ctlcontrols2 = (IWMPControls2)WindowsMediaPlayer.Ctlcontrols;
double frameRate = WindowsMediaPlayer.network.encodedFrameRate;
Console.WriteLine("FRAMERATE: " + frameRate); //Debug
double step = 1.0 / frameRate;
Console.WriteLine("STEP: " + step); //Debug
WindowsMediaPlayer.Ctlcontrols.currentPosition -= step; //Go backwards
WindowsMediaPlayer.Ctlcontrols.pause();
Ctlcontrols2.step(1);

Better way to "dock" a third party running application inside a windows.forms panel?

I am currently doing this as follows:
// _Container is the panel that the program is to be displayed in.
System.Diagnostics.Process procTest = new System.Diagnostics.Process();
procTest.StartInfo.FileName = "TEST.EXE";
procTest.StartInfo.CreateNoWindow = false;
procTest.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
procTest.Start();
procTest.WaitForInputIdle();
SetParent(procTest.MainWindowHandle, _Container.Handle);
MoveWindow(procTest.MainWindowHandle,
0, 0, _Container.Width, _Container.Height, true);
The problem I am having with this code is that some parts of the application UI no longer function properly once I change the MainWindowHandle (ie: buttons missing text).
Is there a way to do this without causing issues with the docked application? (Either through .net or user32)?
First of all, instead of simply waiting 1.5 seconds, try calling procTest.WaitForInputIdle to wait until its message loop is free. You already are.
In general, I don't think it's possible to do this without modifying the program that you're hosting.
EDIT: You could try to keep the other program above your hosting area by hiding in from the taskbar, removing its title bar, moving it as your program moves, etc. However, this still wouldn't work perfectly; I recommend that you try to find some alternative.
Try contacting the original developers of the third-party application and asking for their advice.

Categories

Resources