Nano Hash - криптовалюты, майнинг, программирование

Плавное изменение размера окна в Windows (с использованием Direct2D 1.1)?

Меня раздражает, что изменение размера окон в Windows происходит не так «плавно», как хотелось бы (это касается программ Windows вообще, а не только моих собственных. Хороший пример — Visual Studio). Из-за этого ОС и ее программы кажутся «хлипкими» и «дешевыми» (да, меня волнует, как программы и пользовательские интерфейсы чувствуют себя, так же, как я забочусь о звуке и ощущении закрывания автомобиля). дверь. Это отражение качества сборки), что, на мой взгляд, влияет на общий UX и, в конечном итоге, на восприятие бренда.

Перерисовка содержимого окна просто не поспевает за движением мыши при изменении размера. Всякий раз, когда я изменяю размер окна, возникает эффект «заикания» / «мерцания», по-видимому, из-за того, что содержимое окна предыдущего размера перерисовывается в новой оконной рамке с измененным размером до того, как будет нарисовано новое содержимое с измененным размером.

Я создаю приложение Win32 (x64), которое использует Direct2D 1.1 для отрисовки своего пользовательского интерфейса, и, учитывая скорость Direct2D, я думаю, что не должно быть необходимости страдать от таких артефактов в ОС в 2014 году. Я сам на Windows 8.1, но ориентируюсь Windows 7 и выше с этим приложением.

Эффект «предыдущего размера» особенно заметен при максимальном увеличении небольшого окна (поскольку разница в размере окна достаточно велика, чтобы легко контрастировать с изображением старого содержимого, когда оно кратковременно мигает в левом верхнем углу большего окна с новым содержимым). впоследствии его закрашивают).

Вот что происходит:

  1. (Допустим, на экране есть полностью визуализированное окно размером 500 x 500 пикселей).
  2. Я максимизирую окно:
  3. Оконная рама увеличена
  4. Старый контент 500 x 500 рисуется в новом фрейме, прежде чем...
  5. .. развернутое окно перерисовывается с содержимым правильного размера.

Мне интересно, есть ли способ смягчить это (т.е. избавиться от шага 4) - например, перехватив сообщение Windows - и избежать перерисовки окна в новом размере со старым содержимым перед окончательным повторным рендерингом появляется новый контент. Это похоже на то, как Windows сама перерисовывает окно, используя любую графику, которая у нее уже есть, ДО ТОГО, как она удосужится попросить меня предоставить обновленный контент с помощью сообщения WM_PAINT или подобного.

Можно ли это сделать?

Правка: кажется, что WM_WINDOWPOSCHANGING / WM_SIZING предоставляет "ранний доступ" к данным о новом размере, но мне все еще не удалось подавить отрисовку старого содержимого.

Мой WndProc выглядит так:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_ERASEBKGND:
        return 1;
    case WM_PAINT:
        PAINTSTRUCT ps;
        BeginPaint(hWnd, &ps);
        D2DRender();
        EndPaint(hWnd, &ps);
        return 0;
    case WM_SIZE:
        if (DeviceContext && wParam != SIZE_MINIMIZED)
        {
            D2DResizeTargetBitmap();
            D2DRender();
        }
        return 0;
    case WM_DISPLAYCHANGE:
        D2DRender();
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

В окне не установлены CS_HREDRAW или CS_VREDRAW. Цепочка обмена имеет двойную буферизацию, а вызов Present выполняется с SyncInterval = 0.

Я знаю, что повторное создание буферов цепочки обмена каждый раз при изменении размера окна создает некоторые накладные расходы по сравнению с простым перерисовыванием на статической поверхности окна. Однако "заикание" вызвано не этим, так как оно происходит, даже когда изменение размера буфера отключено, а существующее содержимое окна просто масштабируется во время изменения размера окна (хотя это делает он лучше справляется с движением мыши).


  • Проблема в том, что в Windows сообщения о рисовании асинхронны - к тому времени, когда вы начинаете обрабатывать перерисовку, окно может уже снова изменить размер. Вы можете свести последствия к минимуму, приложив много усилий, но никогда не сможете полностью избавиться от них. 17.02.2014
  • Мы говорим об удалении границ окна и ручной реализации обнаружения перетаскивания/изменения размера в клиентской области, самостоятельном определении нового размера окна, изменении размера цепочки обмена, перерисовке и ЗАТЕМ ручном изменении размера окна перед обработкой следующей (текущей) позиции мыши? 17.02.2014
  • Я подозреваю, что это те длины, на которые вам нужно пойти. 17.02.2014
  • То, что вы видите, в некотором роде является преднамеренным поведением. Например, вы говорите, что содержимое окна перерисовывается слишком маленьким сразу после максимизации, но технически оно не перерисовывается именно в этот момент, как если бы вы его переместили. Такая оптимизация (или вообще вся композиция рабочего стола) делается для того, чтобы сделать графический интерфейс более ответственным (предоставлять более быструю визуальную обратную связь, когда пользователь выполняет действие — фактическое перерисовка заняло бы заметно слишком много времени). Мы настолько привыкли к отзывчивости современных операционных систем, что даже небольшие задержки (менее 50 мс) могут раздражать опытных пользователей. 17.02.2014
  • @JonathanPotter: Хм. В моем случае это может быть не так уж и плохо. В любом случае, я создаю полную систему компоновки в стиле WPF со встроенными функциями перетаскивания и изменения размера элементов пользовательского интерфейса, поэтому я мог бы просто обозначить все окно как такой [корневой] элемент управления и получить его бесплатно. Сейчас я изучаю WM_WINDOWPOSCHANGING после прочтения этой статьи Рэймонда Чена: blogs.msdn.com/b/oldnewthing/archive/2008/01/16/7123299.aspx 17.02.2014
  • @dialer: я понимаю проблему с отзывчивостью, но я перебрасываю графику со скоростью 60 кадров в секунду (что в любом случае является частотой обновления экрана) в полноэкранных окнах, поэтому я не могу понять, почему та же скорость перерисовки не может быть использована для также обновлять содержимое окна со скоростью 60 кадров в секунду во время изменения размера. 17.02.2014
  • @ d7samurai Это хороший момент, и я не хотел мешать вам пытаться, просто прокомментировал, почему все так. Мне было бы очень интересно, если бы вам удалось найти решение (может быть, вы сможете обновлять эту тему, если продолжите над ней работать?). Я сам пробовал с этим возиться, но пришел к выводу, что это может просто не вписываться в концепцию того, как Windows обрабатывает окна (‹-?) 17.02.2014
  • @dialer: Конечно. Я подозреваю, что это может быть какой-то архитектурный реликт из более ранних версий ОС, изначально предназначенный для более медленных процессоров и до ускорения Direct2D/GPU. Но сейчас 2014 год, и я хочу, чтобы мои приложения ощущались как Mercedes, а не как Skoda 70-х :) 17.02.2014
  • @JonathanPotter Решено. Я полностью преодолел это (но мне нужно было пойти на все, что указано в моем первом комментарии). 26.09.2014
  • @звонилка Готово. Надеюсь, у меня будет время опубликовать решение вместе с некоторыми размышлениями в качестве ответа позже. 26.09.2014

Ответы:


1

Что делать, если у вас есть дочернее окно без полей (тип, который отображается только внутри родителя) с фиксированным размером (такой же, как полноэкранное разрешение), вы должны получить гораздо более плавные результаты, потому что нет перераспределения памяти (что, я думаю, и вызывает нервозность). ).

Если это все еще не идеально, изучите как WM_SIZE, так и WM_SIZING и проверьте, можете ли вы сделать с ними какое-то волшебство. Например, для WM_SIZING вы можете вернуть true, сообщая Windows, что вы обработали сообщение (оставив окно как есть), и вы повторно визуализируете свой пользовательский интерфейс в буфер с размером, предоставленным WM_SIZING, и когда это будет сделано, вы отправляете свой собственный WM_SIZING, но с измененным неиспользуемым битом в WPARAM (вместе с его предыдущим содержимым), который говорит вам, что у вас есть предварительно обработанный буфер для этого, который вы можете просто удалить. Из документации WM_SIZING на msdn видно, что у WPARAM должна быть пара битов в вашем распоряжении.

Надеюсь это поможет.

11.04.2014
  • В приложении уже работает полнофункциональная настраиваемая (на основе Direct2D/3D) система компоновки внутри — аналогичная рабочему столу ОС с окнами, которые можно перемещать, изменять размер, затемнять и т. д. — и это гладко, как шелк. И он предназначен для запуска в полноэкранном режиме. Но когда приложение работает в оконном режиме, есть причины сохранить его как обычное оконное окно Windows, и именно в этом заключается проблема. 11.04.2014
  • Мне еще предстоит попытаться обойти все пути изменения размера на основе WM_ и вместо этого полагаться на свою собственную систему макета, чтобы справиться с этим - имея главное окно без полей, но с настраиваемыми границами, которые захватывают мышь и отслеживают перетаскивание. Затем он сначала изменит размер оконного буфера (используя изменение размера буфера цепочки обмена), повторно отобразит содержимое, а затем вручную установит размер/положение окна, чтобы отразить изменения. Вместо того, чтобы Windows сначала изменяла размер границы окна, а затем сообщала мне новый размер после этого, через WM_SIZE и т. д. Это отключение могло быть причиной мерцания. 11.04.2014
  • Обход сообщений WM_size звучит как хорошая идея, я также ищу решение для плавного изменения размера Direct3D/OpenGL, когда мой проект немного улучшится, также используя окно без полей. Пожалуйста, опубликуйте свое решение, когда вы доберетесь до него, я сделаю то же самое. 14.04.2014

  • 2

    При вызове CreateSwapChainForHwnd убедитесь, что для свойства Scaling описания цепочки обмена задано значение DXGI_SCALING_NONE. Это поддерживается только в Windows 7 с обновлением платформы, поэтому вам может потребоваться вернуться к стандартному DXGI_SCALING_STRETCH (последнее вызывает мерцание).

    17.11.2015

    3

    Есть способ предотвратить ненужный BitBlt, упомянутый в шаге 4 выше.

    До Windows 8 это можно было сделать либо путем создания собственной пользовательской реализации WM_NCCALCSIZE, чтобы сообщить Windows ничего не перемещать (или перемещать один пиксель поверх самого себя), либо, альтернативно, вы могли перехватить WM_WINDOWPOSCHANGING (сначала передав его на DefWindowProc) и установить WINDOWPOS.flags |= SWP_NOCOPYBITS , который отключает BitBlt внутри внутреннего вызова SetWindowPos(), который Windows делает при изменении размера окна. Это имеет тот же конечный эффект пропуска BitBlt.

    Однако ничего не может быть так просто. С появлением Windows 8/10 Aero приложения теперь рисуются в буфере вне экрана, который затем компилируется новым злым оконным менеджером DWM.exe. И оказывается, что DWM.exe иногда выполняет свою собственную операцию типа BitBlt поверх той, которая уже выполняется устаревшим кодом XP/Vista/7. И остановить DWM от его блит гораздо сложнее; до сих пор я не видел никаких полных решений.

    Так что вам нужно пройти через оба слоя. Пример кода, который преодолеет уровень XP/Vista/7 и по крайней мере улучшит производительность уровня 8/10, см.:

    Как сгладить уродливое дрожание/мерцание/ прыгает при изменении размера окна, особенно при перетаскивании левой/верхней границы (Win 7-10; bg, bitblt и DWM)?

    26.10.2018

    4

    Установите WM_SETREDRAW в FALSE, измените размер, затем снова включите рисование, сделайте окно недействительным, и ОС уничтожит его.

    Я сделал это для включения и отключения кнопок при выборе различных элементов из списка, а не для всего окна.

    17.02.2014
  • Это полностью отключает рисование, поэтому содержимое окна вообще не изменяется при изменении размера, а вместо этого старое содержимое просто обрезается или расширяется черным цветом (в зависимости от того, уменьшаете вы или увеличиваете размер соответственно). И в довершение всего, последний артефакт содержимого отрисовки все еще присутствует при повторном включении и перерисовке в конце операции. 17.02.2014

  • 5

    Это лучшее, что я придумал, и оно отлично изменяет размеры, хотя перекрытие заднего буфера вызывает некоторое мерцание краев, еще не тестировалось с DX или OGL, но оно должно работать еще лучше с аппаратным ускорением. Это немного громоздко, но подойдет в качестве доказательства концепции.

    Если бы холст можно было обрезать без использования MDI, это было бы еще лучше, например, с использованием буфера битовой маски.

    Одна вещь, которой я не доволен, - это координаты положения дочернего окна, потому что они могут работать не во всех системах, но комбинация вызовов GetSystemMetrics для получения размеров границ и заголовков должна исправить это.

    /* Smooth resizing of GDI+ MDI window
     * 
     * Click window to resize, hit Escape or Alt+F4 to quit
     * 
     * Character type is set to multibyte
     * Project->Properties->Config Properties->General->Character Set = Multibyte
     * 
     * Pritam 2014 */
    
    
    // Includes
    #include <Windows.h>
    #include <gdiplus.h>
    #pragma comment (lib,"Gdiplus.lib")
    using namespace Gdiplus;
    
    
    // Max resolution
    #define XRES 1600
    #define YRES 900
    
    
    // Globals
    bool resizing = false;
    HWND parent, child;        // child is the canvas window, parent provides clipping of child
    Bitmap * buffer;
    
    
    // Render
    void Render() {
    
        // Get parent client size
        RECT rc;
        GetClientRect(parent, &rc);
    
        // Draw backbuffer
        Graphics * g = Graphics::FromImage(buffer);
    
            // Clear buffer
            g->Clear(Color(100, 100, 100));
    
            // Gray border
            Pen pen(Color(255, 180, 180, 180));
            g->DrawRectangle(&pen, 10, 10, rc.right - 20, rc.bottom - 20);
            pen.SetColor(Color(255, 0, 0, 0));
            g->DrawRectangle(&pen, 0, 0, rc.right - 1, rc.bottom - 1);
    
        // Draw buffer to screen
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(child, &ps);
        Graphics graphics(hdc);
    
            graphics.DrawImage(buffer, Point(0, 0));
    
        // Free
        EndPaint(child, &ps);
    }
    
    
    // MDI Callback
    LRESULT CALLBACK MDICallback(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
        switch(message) {
        case WM_LBUTTONDOWN:
            resizing = true; // Start resizing
            return 0;
            break;
        case WM_KEYDOWN:
            if(wparam == VK_ESCAPE) { // Exit on escape
                PostQuitMessage(0);
            }
            TranslateMessage((const MSG *)&message);
            return 0;
            break;
        case WM_PAINT:
            Render();
            return 0;
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
            break;
        }
    
        return DefMDIChildProc(hwnd, message, wparam, lparam);
    }
    
    
    // Parent window callback
    LRESULT CALLBACK WndCallback(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
        return DefFrameProc(hwnd, child, message, wparam, lparam);
    }
    
    
    // Create windows
    bool CreateWindows(void) {
    
        // Parent class
        WNDCLASSEX wndclass;
        ZeroMemory(&wndclass, sizeof(wndclass)); wndclass.cbSize = sizeof(wndclass);
    
            wndclass.style = CS_NOCLOSE;
            wndclass.lpfnWndProc = WndCallback;
            wndclass.hInstance = GetModuleHandle(NULL);
            wndclass.lpszClassName = "WNDCALLBACKPARENT";
            wndclass.hIcon = LoadIcon(NULL, IDI_WINLOGO);
            wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    
        if(!RegisterClassEx(&wndclass)) return false;
    
            // MDI class
            wndclass.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
            wndclass.lpfnWndProc = MDICallback;
            wndclass.lpszClassName = "MDICALLBACKCANVAS";
    
        if(!RegisterClassEx(&wndclass)) return false;
    
    
        // Parent window styles
        DWORD style = WS_POPUP | WS_CLIPCHILDREN;
        DWORD exstyle = 0;
    
            // Set initial window size and position
            RECT rc;
            rc.right = 640;
            rc.bottom = 480;
    
            AdjustWindowRectEx(&rc, style, false, exstyle);
    
            rc.left = 20;
            rc.top = 20;
    
        // Create window
        if(!(parent = CreateWindowEx(exstyle, "MDICLIENT", "MDI Resize", style, rc.left, rc.top, rc.right, rc.bottom, NULL, NULL, wndclass.hInstance, NULL))) return false;
    
    
        // MDI window styles
        style = MDIS_ALLCHILDSTYLES;
        exstyle = WS_EX_MDICHILD;
    
            // Set MDI size
            rc.left = - 8; // The sizes occupied by borders and caption, if position is not correctly set an ugly caption will appear
            rc.top = - 30;
            rc.right = XRES;
            rc.bottom = YRES;
            AdjustWindowRectEx(&rc, style, false, exstyle);
    
        // Create MDI child window
        if(!(child = CreateWindowEx(exstyle, "MDICALLBACKCANVAS", "", style, rc.left, rc.top, rc.right, rc.bottom, parent, NULL, wndclass.hInstance, NULL))) return 8;
    
            // Finalize
            ShowWindow(child, SW_SHOW);
            ShowWindow(parent, SW_SHOWNORMAL);
    
        // Success
        return true;
    }
    
    
    // Resize
    void Resize(void) {
    
        // Init
        RECT rc, rcmdi;
        GetClientRect(child, &rcmdi); // Use mdi window size to set max resize for parent
        GetWindowRect(parent, &rc);
    
        // Get mouse position
        POINT mp;
        GetCursorPos(&mp);
    
            // Set new size
            rc.right = mp.x - rc.left + 10;
            rc.bottom = mp.y - rc.top + 10;
    
            // Apply min & max size
            if(rc.right < 240) rc.right = 240; if(rc.bottom < 180) rc.bottom = 180;
            if(rc.right > rcmdi.right) rc.right = rcmdi.right; if(rc.bottom > rcmdi.bottom) rc.bottom = rcmdi.bottom;
    
        // Update window size
        SetWindowPos(parent, NULL, rc.left, rc.top, rc.right, rc.bottom, SWP_NOZORDER | SWP_NOMOVE);
    
            // Make sure client is entirely repainted
            GetClientRect(child, &rc);
            InvalidateRect(child, &rc, false);
            UpdateWindow(child);
    
        // Stop resizing if mousebutton is up
        if(!(GetKeyState(VK_LBUTTON) & 1 << (sizeof(short) * 8 - 1)))
            resizing = false;
    }
    
    
    // Main
    int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE pinstance, LPSTR cmdline, int cmdshow) {
    
        // Initiate GDI+
        ULONG_PTR gdiplusToken;
        GdiplusStartupInput gdiplusStartupInput;
        GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
        buffer = new Bitmap(XRES, YRES, PixelFormat24bppRGB);
    
        // Create windows
        if(!CreateWindows()) return 1;
    
    
        // Main loop
        bool running = true;
        MSG message;
        while(running) {
    
            // Check message or pass them on to window callback
            if(PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) {
                if(message.message == WM_QUIT) {
                    running = false;
                } else {
                    if(!TranslateMDISysAccel(child, &message)) {
                        TranslateMessage(&message);
                        DispatchMessage(&message);
                    }
                }
            }
    
            // Resize
            if(resizing)
                Resize();
    
            // Sleep a millisecond to spare the CPU
            Sleep(1);
        }
    
    
        // Free memmory and exit
        delete buffer;
        GdiplusShutdown(gdiplusToken);
        return 0;
    }
    

    Изменить: еще один пример с использованием «битовой маски»/многослойного окна.

    // Escape to quit, left mousebutton to move window, right mousebutton to resize.
    // And again char set must be multibyte
    
    // Include
    #include <Windows.h>
    #include <gdiplus.h>
    #pragma comment (lib,"Gdiplus.lib")
    using namespace Gdiplus;
    
    
    // Globals
    Bitmap * backbuffer;
    int xres, yres;
    bool move, size;
    POINT framePos, frameSize, mouseOffset;
    
    // Renders the backbuffer
    void Render(void) {
        if(!backbuffer) return;
    
        // Clear window with mask color
        Graphics * gfx = Graphics::FromImage(backbuffer);
        gfx->Clear(Color(255, 0, 255));
    
        // Draw stuff
        SolidBrush brush(Color(120, 120, 120));
        gfx->FillRectangle(&brush, framePos.x, framePos.y, frameSize.x, frameSize.y);
    }
    
    // Paints the backbuffer to window
    void Paint(HWND hwnd) {
        if(!hwnd) return;
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        Graphics gfx(hdc);
        gfx.DrawImage(backbuffer, Point(0, 0));
        EndPaint(hwnd, &ps);
    }
    
    
    void HandleMove(HWND hwnd) {
    
        // Get mouse position
        POINT mouse;
        GetCursorPos(&mouse);
    
        // Update frame position
        framePos.x = mouse.x - mouseOffset.x;
        framePos.y = mouse.y - mouseOffset.y;
    
        // Redraw buffer and invalidate & update window
        Render();
        InvalidateRect(hwnd, NULL, false);
        UpdateWindow(hwnd);
    
        // Stop move
        if(!(GetKeyState(VK_LBUTTON) & 1 << (sizeof(short) * 8 - 1)))
            move = false;
    }
    
    void HandleSize(HWND hwnd) {
    
        // Get mouse position
        POINT mouse;
        GetCursorPos(&mouse);
    
        // Update frame size
        frameSize.x = mouse.x + mouseOffset.x - framePos.x;
        frameSize.y = mouse.y + mouseOffset.y - framePos.y;
    
        //frameSize.x = mouse.x + mouseOffset.x;
        //frameSize.y = mouse.y + mouseOffset.y;
    
        // Redraw buffer and invalidate & update window
        Render();
        InvalidateRect(hwnd, NULL, false);
        UpdateWindow(hwnd);
    
        // Stop size
        if(!(GetKeyState(VK_RBUTTON) & 1 << (sizeof(short) * 8 - 1)))
            size = false;
    }
    
    
    LRESULT CALLBACK WindowCallback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
    
        POINTS p;
    
        switch(msg) {
        case WM_KEYDOWN:
            if(wparam == VK_ESCAPE) PostQuitMessage(0);
            return 0;
            break;
        case WM_LBUTTONDOWN:
            p = MAKEPOINTS(lparam); // Get mouse coords
            mouseOffset.x = p.x - framePos.x;
            mouseOffset.y = p.y - framePos.y;
            move = true;
            break;
        case WM_RBUTTONDOWN:
            p = MAKEPOINTS(lparam);
            mouseOffset.x = framePos.x + frameSize.x - p.x;
            mouseOffset.y = framePos.y + frameSize.y - p.y;
            size = true;
            break;
        case WM_PAINT:
            Paint(hwnd);
            return 0;
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
            break;
        }
        return DefWindowProc(hwnd, msg, wparam, lparam);
    }
    
    
    // Main
    int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE pinstance, LPSTR cmdline, int cmdshow) {
    
        // Init resolution, frame
        xres = GetSystemMetrics(SM_CXSCREEN);
        yres = GetSystemMetrics(SM_CYSCREEN);
    
        move = false; size = false;
        framePos.x = 100; framePos.y = 80;
        frameSize.x = 320; frameSize.y = 240;
        mouseOffset.x = 0; mouseOffset.y = 0;
    
        // Initiate GDI+
        ULONG_PTR gdiplusToken;
        GdiplusStartupInput gdiplusStartupInput;
        GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    
        // Init backbuffer
        backbuffer = ::new Bitmap(xres, yres, PixelFormat24bppRGB);
        Render();
    
    
        // Window class
        WNDCLASSEX wc; ZeroMemory(&wc, sizeof(wc)); wc.cbSize = sizeof(wc);
    
        wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
        wc.lpfnWndProc = WindowCallback;
        wc.hInstance = GetModuleHandle(NULL);
        wc.lpszClassName = "SingleResizeCLASS";
        wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
        wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    
        if(!RegisterClassEx(&wc)) return 1;
    
    
        // Create window
        HWND hwnd;
        DWORD style = WS_POPUP;
        DWORD exstyle = WS_EX_LAYERED;
        if(!(hwnd = CreateWindowEx(exstyle, wc.lpszClassName, "Resize", style, 0, 0, xres, yres, NULL, NULL, wc.hInstance, NULL)))
            return 2;
    
            // Make window fully transparent to avoid the display of unpainted window
            SetLayeredWindowAttributes(hwnd, 0, 0, LWA_ALPHA);
    
        // Finalize
        ShowWindow(hwnd, SW_SHOWNORMAL);
        UpdateWindow(hwnd);
    
        // Make window fully opaque, and set color mask key
        SetLayeredWindowAttributes(hwnd, RGB(255, 0, 255), 0, LWA_COLORKEY);
    
    
        // Main loop
        MSG msg;
        bool running = true;
        while(running) {
    
            // Check message
            if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
                if(msg.message == WM_QUIT) {
                    running = false;
                } else {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
    
            // Move or size frame
            if(move) { HandleMove(hwnd); }
            if(size) { HandleSize(hwnd); }
    
            Sleep(1);
        }
    
        // Free memory
        ::delete backbuffer;
        backbuffer = NULL;
        GdiplusShutdown(gdiplusToken);
    
        // Exit
        return 0;
    }
    
    17.04.2014
  • Я думаю, что вам лучше всего подойдет что-то вроде этого 17.04.2014

  • 6

    Хотя ваша цель похвальна, я подозреваю, что любая попытка сделать это просто закончится дракой между вами и Windows, в которой вы не выиграете (хотя вам, возможно, удастся пробиться к почетной ничьей). Извините за негатив.

    16.02.2014
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..