static IntPtr KeyboardProc(int code, IntPtr wParam, IntPtr lParam)
 {
     if (code >= 0)
     {
         // Since this is a global hook, we don't want to react to
         // keypress on every window, so make sure game window is foreground
         if (PlatformInvokeUSER32.GetForegroundWindow() == childHwnd)
         {
             if ((int)wParam == PlatformInvokeUSER32.WM_KEYDOWN)
             {
                 var  kbdInfo = (PlatformInvokeUSER32.KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(PlatformInvokeUSER32.KBDLLHOOKSTRUCT));
                 Keys key     = (Keys)kbdInfo.vkCode;
                 if (key == Keys.F11)
                 {
                     // This does not seem to actually work when the overlay isn't loaded
                     //SteamScreenshots.TriggerScreenshot();
                     HandleScreenshotRequest(new ScreenshotRequested_t());
                 }
             }
         }
     }
     return(PlatformInvokeUSER32.CallNextHookEx(IntPtr.Zero, code, wParam, lParam));
 }
        static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            if (args.Length == 0)
            {
                return;
            }

            if (!SteamAPI.Init())
            {
                MessageBox.Show("Steam API cannot be initialized, exiting.");
                return;
            }

            string exeName = args[0];

            string[] procArgs      = args.Skip(1).ToArray();
            string   procArgString = procArgs.Length == 0 ? string.Empty : string.Join(" ", procArgs);
            Process  proc          = Process.Start(exeName, procArgString);

            if (proc != null)
            {
                // Set up screenshot callback and hook
                screenshotReadyCallback = Callback <ScreenshotRequested_t> .Create(HandleScreenshotRequest);

                SteamScreenshots.HookScreenshots(true);

                // Set up global keyboard hook
                IntPtr hook = PlatformInvokeUSER32.SetWindowsHookEx(PlatformInvokeUSER32.HookType.WH_KEYBOARD_LL, KeyboardProc,
                                                                    IntPtr.Zero, 0);
                if (hook == IntPtr.Zero)
                {
                    int err = Marshal.GetLastWin32Error();
                    MessageBox.Show($"Failed to set hook, error {err}");
                }
                // Wait for window to be created before setting main window handle
                // You might need to change this if your game creates several windows
                // before settling.
                while (proc.MainWindowHandle == IntPtr.Zero && !proc.HasExited)
                {
                    System.Threading.Thread.Sleep(100);
                }
                childHwnd = proc.MainWindowHandle;
                // Run callbacks until child process exits
                while (!proc.HasExited)
                {
                    SteamAPI.RunCallbacks();
                    System.Threading.Thread.Sleep(16);
                }
                // Unhook before we leave
                if (hook != IntPtr.Zero)
                {
                    PlatformInvokeUSER32.UnhookWindowsHookEx(hook);
                }
            }
            else
            {
                MessageBox.Show("Failed to start game process.");
            }

            SteamAPI.Shutdown();
        }
        static bool TakeScreenshot(IntPtr hwnd)
        {
            bool   success = false;
            IntPtr hBmp    = IntPtr.Zero;
            // Get DC of game window
            IntPtr hDC = PlatformInvokeUSER32.GetDC(hwnd);

            if (hDC != IntPtr.Zero)
            {
                // Make compatible DC that we can blit to
                IntPtr hMemDC = PlatformInvokeGDI32.CreateCompatibleDC(hDC);
                if (hMemDC != IntPtr.Zero)
                {
                    // Get size of what we're trying to take a screenshot of
                    if (PlatformInvokeUSER32.GetClientRect(hwnd, out PlatformInvokeUSER32.RECT rect))
                    {
                        int width  = rect.right - rect.left;
                        int height = rect.bottom - rect.top;
                        // Create bitmap
                        hBmp = PlatformInvokeGDI32.CreateCompatibleBitmap(hDC, width, height);
                        if (hBmp != IntPtr.Zero)
                        {
                            // Blit the thing
                            IntPtr hOld = PlatformInvokeGDI32.SelectObject(hMemDC, hBmp);
                            PlatformInvokeGDI32.BitBlt(hMemDC, 0, 0, width, height, hDC, rect.left, rect.top, PlatformInvokeGDI32.SRCCOPY);
                            PlatformInvokeGDI32.SelectObject(hMemDC, hOld);
                            success = true;
                        }
                    }
                    PlatformInvokeGDI32.DeleteDC(hMemDC);
                }
                PlatformInvokeUSER32.ReleaseDC(hwnd, hDC);
            }

            if (success)
            {
                // Use .NET's graphics processing to extract our pixels
                using (Bitmap bmp = Image.FromHbitmap(hBmp))
                {
                    // Remember to clean up the native bitmap from earlier
                    PlatformInvokeGDI32.DeleteObject(hBmp);

                    // Lock the bitmap data and prepare a buffer
                    BitmapData bits   = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                    byte[]     buffer = new byte[bmp.Width * bmp.Height * 3];
                    int        i      = 0;

                    unsafe
                    {
                        byte *scanline = (byte *)bits.Scan0;
                        while (i < buffer.Length)
                        {
                            for (int j = 0; j < bits.Width; ++j)
                            {
                                // The buffer in .NET is BGR, so flip it to RGB when copying
                                buffer[i++] = scanline[2];
                                buffer[i++] = scanline[1];
                                buffer[i++] = scanline[0];
                                scanline   += 3;
                            }
                            scanline += bits.Stride - bits.Width * 3;
                        }
                    }

                    // Unlock the bits
                    bmp.UnlockBits(bits);

                    // Send it off to Steam
                    ScreenshotHandle hScreenshot = SteamScreenshots.WriteScreenshot(buffer, (uint)buffer.Length, bmp.Width, bmp.Height);
                    success = hScreenshot != ScreenshotHandle.Invalid;
                }
            }

            return(success);
        }