【问题标题】:Painting in Gdi occurs only onceGdi 中的绘画只出现一次
【发布时间】:2021-07-30 19:02:15
【问题描述】:

我在 c# 中使用这个 winapi 库并尝试用它创建一个屏幕截图应用程序。它与普通的winapi完全相似。问题是即使我正在更新窗口,油漆也只会发生一次。我曾经在 c++ 中使用过 winapi,遇到了完全相同的问题,但我似乎找不到解决方案。有谁知道这是什么问题? 谢谢!

    internal class Program
    {
        static public int virtualScreenLeft = User32Methods.GetSystemMetrics(SystemMetrics.SM_XVIRTUALSCREEN);
        static public int virtualScreenTop = User32Methods.GetSystemMetrics(SystemMetrics.SM_YVIRTUALSCREEN);
        static public int virtualScreenRight = User32Methods.GetSystemMetrics(SystemMetrics.SM_CXVIRTUALSCREEN);
        static public int virtualScreenBottom = User32Methods.GetSystemMetrics(SystemMetrics.SM_CYVIRTUALSCREEN);
        static public int virtualScreenWidth = virtualScreenRight - virtualScreenLeft;
        static public int virtualScreenHeight = virtualScreenBottom - virtualScreenTop;

        static public bool mousePressed = false;
        static public NetCoreEx.Geometry.Point lastMousePos = new NetCoreEx.Geometry.Point();
        static public NetCoreEx.Geometry.Point currentMousePos = new NetCoreEx.Geometry.Point();
        static public NetCoreEx.Geometry.Rectangle rect = new NetCoreEx.Geometry.Rectangle();

        static public IntPtr bmp = captureVirtualScreen();

        static IntPtr captureVirtualScreen()
        {
            // Get a DC compat. w/ the screen
            IntPtr hdc = Gdi32Methods.CreateCompatibleDC(IntPtr.Zero);

            // Make a bmp in memory to store the capture in
            IntPtr bitmap =  Gdi32Methods.CreateCompatibleBitmap(User32Methods.GetDC(IntPtr.Zero) , 
                                                                 virtualScreenWidth, virtualScreenHeight);

            // Join them
            Gdi32Methods.SelectObject(hdc, bitmap);

            // Copy from the screen to my bitmap
            Gdi32Methods.BitBlt(hdc, virtualScreenLeft, virtualScreenTop,
                                     virtualScreenWidth, virtualScreenHeight, 
                                     User32Methods.GetDC(IntPtr.Zero), 0, 0, BitBltFlags.SRCCOPY);

            // Delete the DC
            Gdi32Methods.DeleteDC(hdc);

            // Return the bitmap
            return bitmap;
        }
        

        static int Main(string[] args)
        {
            var instanceHandle = Kernel32Methods.GetModuleHandle(IntPtr.Zero);

            var wc = new WindowClassEx
            {
                Size = (uint)Marshal.SizeOf<WindowClassEx>(),
                ClassName = "MainWindow",
                CursorHandle = User32Helpers.LoadCursor(IntPtr.Zero, SystemCursor.IDC_ARROW),
                IconHandle = User32Helpers.LoadIcon(IntPtr.Zero, SystemIcon.IDI_APPLICATION),
                Styles = WindowClassStyles.CS_HREDRAW | WindowClassStyles.CS_VREDRAW,
                BackgroundBrushHandle = new IntPtr((int)StockObject.WHITE_BRUSH),
                WindowProc = WindowProc,
                InstanceHandle = instanceHandle
            };

            var resReg = User32Methods.RegisterClassEx(ref wc);
            if (resReg == 0)
            {
                Console.Error.WriteLine("registration failed");
                return -1;
            }

            var hwnd = User32Methods.CreateWindowEx(WindowExStyles.WS_EX_LEFT,
                wc.ClassName, ";)", WindowStyles.WS_POPUP | WindowStyles.WS_VISIBLE,
                virtualScreenLeft, virtualScreenTop,
                virtualScreenWidth, virtualScreenHeight,
                IntPtr.Zero, IntPtr.Zero, instanceHandle, IntPtr.Zero);

            if (hwnd == IntPtr.Zero)
            {
                Console.Error.WriteLine("window creation failed");
                return -1;
            }

            User32Methods.ShowWindow(hwnd, ShowWindowCommands.SW_SHOWNORMAL);
            User32Methods.UpdateWindow(hwnd);

            Message msg;
            int res;
            while ((res = User32Methods.GetMessage(out msg, IntPtr.Zero, 0, 0)) != 0)
            {
                User32Methods.TranslateMessage(ref msg);
                User32Methods.DispatchMessage(ref msg);
                User32Methods.UpdateWindow(hwnd);
            }

            return res;
        }
        
        private static IntPtr WindowProc(IntPtr hwnd, uint umsg, IntPtr wParam, IntPtr lParam)
        {
            var msg = (WM)umsg;
            switch (msg)
            {
                case WM.KEYDOWN:
                    {
                        switch (wParam.ToInt32())
                        {
                            // 27 - flag for esc
                            case 27:
                                {
                                    User32Methods.PostQuitMessage(0);
                                    break;
                                }
                        }
                        break;
                    }
                case WM.LBUTTONDOWN:
                    {

                        mousePressed = true;
                        User32Methods.GetCursorPos(out lastMousePos);
                        User32Methods.GetCursorPos(out currentMousePos);

                        break;
                    }
                case WM.MOUSEMOVE:
                    {
                        if (mousePressed)
                            User32Methods.GetCursorPos(out currentMousePos);
                        break;
                    }
                case WM.LBUTTONUP:
                    {
                        mousePressed = false;
                        break;
                    }
                case WM.CLOSE:
                    {
                        User32Methods.PostQuitMessage(0);
                        break;
                    }
                case WM.PAINT:
                    {

                        IntPtr bm = new IntPtr();
                        PaintStruct ps;
                        
                        var hdc = User32Methods.BeginPaint(hwnd, out ps);

                        IntPtr hdcMem = Gdi32Methods.CreateCompatibleDC(hdc);
                        IntPtr hbmOld = Gdi32Methods.SelectObject(hdcMem, bmp);
                        
                        Gdi32Methods.GetObject(bmp, 0, bm);

                        Gdi32Methods.BitBlt(hdc, virtualScreenLeft, virtualScreenTop,
                                            virtualScreenWidth, virtualScreenHeight,
                                            hdcMem, 0, 0, BitBltFlags.SRCCOPY);


                        Gdi32Methods.SelectObject(hdcMem, hbmOld);
                        Gdi32Methods.DeleteDC(hdcMem);

                        rect.Left = lastMousePos.X;
                        rect.Top = lastMousePos.Y;
                        rect.Right = currentMousePos.X;
                        rect.Bottom = lastMousePos.Y;

                        User32Methods.FillRect(hdc, ref rect, 
                         Gdi32Helpers.GetStockObject(StockObject.WHITE_BRUSH));

                        User32Methods.EndPaint(hwnd, ref ps);
                        break;
                    }
            }
            return User32Methods.DefWindowProc(hwnd, umsg, wParam, lParam);
        }
    }

【问题讨论】:

  • “即使我正在更新窗口”,sn-p 也没有显示任何证据。使用 InvalidateRect() 强制重绘。

标签: c# winapi


【解决方案1】:

UpdateWindow API 调用的行为在其文档中有详细说明:

UpdateWindow 函数通过向窗口发送WM_PAINT 消息来更新指定窗口的客户区如果窗口的更新区域不为空。该函数绕过应用程序队列,直接向指定窗口的窗口过程发送WM_PAINT消息。 如果更新区域为空,则不发送消息

由于有问题的代码从未向更新区域添加任何内容,因此对UpdateWindow 的调用变成了无操作。要添加到更新区域,您可以在调用UpdateWindow 之前调用InvalidateRectInvalidateRgn(参见Invalidating the Client Area)。

或者,您可以调用RedrawWindow,将InvalidateRect/InvalidateRgnUpdateWindow 的效果组合到一个函数调用中。

【讨论】:

    【解决方案2】:

    UpdateWindow 仅在窗口具有无效区域时发送 WM_PAINT 消息。创建此类区域的简单方法是 InvalidateRect 函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-05-20
      • 1970-01-01
      • 1970-01-01
      • 2011-03-01
      相关资源
      最近更新 更多