Bug 1984

Summary: Wine разрешает WM декорировать окна, которые декорируются самим приложением
Product: WINE@Etersoft Reporter: Анатолий Лютин <vostok>
Component: Окна / фокус / перерисовкаAssignee: BUGS@Etersoft <bugs>
Status: DEFERRED --- QA Contact:
Severity: minor    
Priority: P4 CC: kondratyuk, lav
Version: 1.0.9   
Target Milestone: ---   
Hardware: PC   
OS: All   
Whiteboard:
Заявки RT: Связано с:
Дата напоминания:
Bug Depends on:    
Bug Blocks: 1982, 3000    

Description Анатолий Лютин 2008-06-24 17:28:14 MSD
Из-за этого врубается wine-вское декорирование вместо декора приложения, забиваются кнопки системного меню системным меню декоратора wine.
Comment 1 Vitaly Lipatov 2008-06-26 13:42:53 MSD
Уточни пожалуйста тему и содержание баги, чтобы было понятно, как это может быть связано с конкретной проблемой (подразумевается, что речь идёт об окнах КМ-Школы).
Я до сих пор не смог понять как такая бага может быть организована. Или это нужно исследовать с помощью тестовой программы?
Что говорят разработчики КМ-Школы про это?
Comment 2 Анатолий Лютин 2008-06-26 13:47:36 MSD
(In reply to comment #1)
> Уточни пожалуйста тему и содержание баги,
> чтобы было понятно, как это может быть
> связано с конкретной проблемой
> (подразумевается, что речь идёт об окнах
> КМ-Школы).

Врубается wine-вское
декорирование вместо декора приложения,
забиваются кнопки системного меню
системным меню декоратора wine.
Смотрите багу про "крестик"
> Я до сих пор не смог понять как такая бага
> может быть организована.
Нужно особое заполнение структуры класса окна.
> Или это нужно
> исследовать с помощью тестовой программы?
В принципе можно, но тест будет выдавать результат что всё хорошо, т.к. wine создаст эти кнопки, но они будут забиты декоратором wine (следовательно область системного меню будет принадлижать, допустим, KDE, а из теста невозможно и архитекрутно неверно обратиться/обращаться к нему).
> Что говорят разработчики КМ-Школы про это?
Они тут причём? Они просто используюст стандартные возможность Delphi. В Delphi очень легко сделать сабклассинг любого приложения.

Comment 3 Анатолий Лютин 2008-06-26 13:50:14 MSD
Что такое сабклассинг?

Между нами девочками, это слово, которым обычно пугают новичков, добавляя к нему еще умные слова вроде хендл, идентификатор окна, WndProc и т.д.
 
В реальности сабклассинг ? перехват системных сообщений. Вообще вся работа Windows построена на сообщениях. То есть, когда юзер Вася Пупкин щелкает левой кнопкой мыши по иконке в верхнем левом углу окна, Windows говорит окну WM_SYSCOMMAND, батенька, а окно в свою очередь открывает системное меню. Когда окно (не всегда окно :) сабклассят, то перехватывают сообщения от Windows и говорят совсем другое. Например, юзер Вася щелкает левой кнопкой мыши по иконке в верхнем правом углу, Windows говорит WM_SYSCOMMAND, батенькаЋ, а мы, услышав это, затыкаем Windows и говорим WM_QUIT, млин и окно идет гулять, то есть закрывается :). При этом юзер Вася удивляется больше всех :), после него удивляется Windows, и ухмыляется наша программа :).
Как это делать?

Можно сидеть самому отлавливать системные сообщения, но это отсылает нас, нет не туда, куда ты подумал, а в глубины Win32 API, а в этот подвал мне порой лезть неохота. Для этого дела есть более удобная вещь, написанная, кстати, на Visual Basic, а именно элемент управления Subclass (http://www.softcircuits.com).

Статья из http://vbrussian.com/Article.asp?ID=87
Comment 4 Vitaly Lipatov 2008-06-26 15:27:57 MSD
А есть информация, какие сообщения нужно перехватить и обработать по-своему,
чтобы добиться такого поведения: отключить стандартную декорацию окна и нарисовать свой крестик, посылающий WM_SYSCOMMAND?
Comment 5 Анатолий Лютин 2008-06-26 15:40:05 MSD
(In reply to comment #4)
> А есть информация, какие сообщения нужно
> перехватить и обработать по-своему,
> чтобы добиться такого поведения: отключить
> стандартную декорацию окна и нарисовать
> свой крестик, посылающий WM_SYSCOMMAND?
> 

http://forum.x-coder.ru/showthread.php?p=6715
Comment 6 Анатолий Лютин 2008-06-26 15:43:00 MSD
2.08. Как мне сделать окно нестандаpтной формы, например, как у Norton
       CrashGuard, в форме щита?

       int SetWindowRgn(
        HWND hWnd,     // дескриптор окна
        HRGN hRgn,     // дескриптор региона, созданного вызовами
                       // CreateEllipticRgn(), CreateRectRgn() или др.
        BOOL bRedraw   // TRUE, если нужно перерисовать окно после смены
региона
                       );

       Вот пример, делающий окно с закругленными углами:
       HRGN myrgn;
       myrgn=CreateRoundRectRgn(0,0,Width,Height,20,20);
       SetWindowRgn(hWnd, myrgn, TRUE);

        Регион ни в коем случае не нужно закрывать с помощью вызова
        DeleteObject(...) - это сделает система при необходимости
        (после вызова SetWindowRgn()).

        А вот пример создания pегиона, по битмапу.

       //
       //  BitmapToRegion: Create a region from the "non-transparent"
       //                  pixels of a bitmap
       //  Author        : Jean-Edouard Lachand-Robert
       // (http://www.geocities.com/Paris/LeftBank/1160/resume.htm), June 1998.
       //
       //  hBmp :              Source bitmap
       //  cTransparentColor : Color base for the "transparent" pixels
       //                      (default is black)
       //  cTolerance :        Color tolerance for the "transparent" pixels.
       //
       //  A pixel is assumed to be transparent if the value of each of its 3
       //  components (blue, green and red) is
       //  greater or equal to the corresponding value in cTransparentColor and
is
       //  lower or equal to the corresponding value in cTransparentColor +
       //  cTolerance.
       //

        HRGN BitmapToRegion (HBITMAP hBmp,
                             COLORREF cTransparentColor = 0,
                             COLORREF cTolerance = 0x101010)
        {
         HRGN hRgn = NULL;

         if (hBmp)
         {
        // Create a memory DC inside which we will scan the bitmap content
        HDC hMemDC = CreateCompatibleDC(NULL);
        if (hMemDC)
        {
            // Get bitmap size
            BITMAP bm;
            GetObject(hBmp, sizeof(bm), &bm);

            // Create a 32 bits depth bitmap and select it into the memory DC
            BITMAPINFOHEADER RGB32BITSBITMAPINFO = {
                    sizeof(BITMAPINFOHEADER),   // biSize
                    bm.bmWidth,                 // biWidth;
                    bm.bmHeight,                // biHeight;
                    1,                          // biPlanes;
                    32,                         // biBitCount
                    BI_RGB,                     // biCompression;
                    0,                          // biSizeImage;
                    0,                          // biXPelsPerMeter;
                    0,                          // biYPelsPerMeter;
                    0,                          // biClrUsed;
                    0                           // biClrImportant;
            };
            VOID * pbits32;
            HBITMAP hbm32 = CreateDIBSection(hMemDC,
                                (BITMAPINFO*)&RGB32BITSBITMAPINFO,
                                DIB_RGB_COLORS, &pbits32, NULL, 0);
            if (hbm32)
            {
                HBITMAP holdBmp = (HBITMAP)SelectObject(hMemDC, hbm32);

                // Create a DC just to copy the bitmap into the memory DC
                HDC hDC = CreateCompatibleDC(hMemDC);
                if (hDC)
                {
                    // Get how many bytes per row we have for the bitmap bits
                    // (rounded up to 32 bits)
                    BITMAP bm32;
                    GetObject(hbm32, sizeof(bm32), &bm32);
                    while (bm32.bmWidthBytes % 4)
                        bm32.bmWidthBytes++;

                    // Copy the bitmap into the memory DC
                    HBITMAP holdBmp = (HBITMAP)SelectObject(hDC, hBmp);
                    BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight,
                           hDC, 0, 0, SRCCOPY);

                    // For better performances, we will use the
                    // ExtCreateRegion() function to create the
                    // region. This function take a RGNDATA structure on
                    // entry. We will add rectangles by
                    // amount of ALLOC_UNIT number in this structure.
                    #define ALLOC_UNIT  100
                    DWORD maxRects = ALLOC_UNIT;
                    HANDLE hData = GlobalAlloc(GMEM_MOVEABLE,
                                               sizeof(RGNDATAHEADER) +
                                               (sizeof(RECT) * maxRects));
                    RGNDATA *pData = (RGNDATA *)GlobalLock(hData);
                    pData->rdh.dwSize = sizeof(RGNDATAHEADER);
                    pData->rdh.iType = RDH_RECTANGLES;
                    pData->rdh.nCount = pData->rdh.nRgnSize = 0;
                    SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);

                    // Keep on hand highest and lowest values for the
                    // "transparent" pixels
                    BYTE lr = GetRValue(cTransparentColor);
                    BYTE lg = GetGValue(cTransparentColor);
                    BYTE lb = GetBValue(cTransparentColor);
                    BYTE hr = min(0xff, lr + GetRValue(cTolerance));
                    BYTE hg = min(0xff, lg + GetGValue(cTolerance));
                    BYTE hb = min(0xff, lb + GetBValue(cTolerance));

                    // Scan each bitmap row from bottom to top (the bitmap is
                    // inverted vertically)
                    BYTE *p32 = (BYTE *)bm32.bmBits +
                                (bm32.bmHeight - 1) * bm32.bmWidthBytes;
                    for (int y = 0; y < bm.bmHeight; y++)
                    {
                        // Scan each bitmap pixel from left to right
                        for (int x = 0; x < bm.bmWidth; x++)
                        {
                            // Search for a continuous range of "non
                            // transparent pixels"
                            int x0 = x;
                            LONG *p = (LONG *)p32 + x;
                            while (x < bm.bmWidth)
                            {
                                BYTE b = GetRValue(*p);
                                if (b >= lr && b <= hr)
                                {
                                    b = GetGValue(*p);
                                    if (b >= lg && b <= hg)
                                    {
                                        b = GetBValue(*p);
                                        if (b >= lb && b <= hb)
                                            // This pixel is "transparent"
                                            break;
                                    }
                                }
                                p++;
                                x++;
                            }

                            if (x > x0)
                            {
                                // Add the pixels (x0, y) to (x, y+1) as a
                                // new rectangle in the region
                                if (pData->rdh.nCount >= maxRects)
                                {
                                    GlobalUnlock(hData);
                                    maxRects += ALLOC_UNIT;
                                    hData = GlobalReAlloc(hData,
                                                   sizeof(RGNDATAHEADER) +
                                                   (sizeof(RECT) * maxRects),
                                                   GMEM_MOVEABLE);
                                    pData = (RGNDATA *)GlobalLock(hData);
                                }
                                RECT *pr = (RECT *)&pData->Buffer;
                                SetRect(&pr[pData->rdh.nCount], x0, y, x, y+1);
                                if (x0 < pData->rdh.rcBound.left)
                                    pData->rdh.rcBound.left = x0;
                                if (y < pData->rdh.rcBound.top)
                                    pData->rdh.rcBound.top = y;
                                if (x > pData->rdh.rcBound.right)
                                    pData->rdh.rcBound.right = x;
                                if (y+1 > pData->rdh.rcBound.bottom)
                                    pData->rdh.rcBound.bottom = y+1;
                                pData->rdh.nCount++;

                                // On Windows98, ExtCreateRegion() may fail if
                                // the number of rectangles is too
                                // large (ie: > 4000). Therefore, we have to
                                // create the region by multiple steps.
                                if (pData->rdh.nCount == 2000)
                                {
                                    HRGN h = ExtCreateRegion(NULL,
                                                    sizeof(RGNDATAHEADER) +
                                                    (sizeof(RECT) * maxRects),
                                                                    pData);
                                    if (hRgn)
                                    {
                                        CombineRgn(hRgn, hRgn, h, RGN_OR);
                                        DeleteObject(h);
                                    }
                                    else
                                        hRgn = h;
                                    pData->rdh.nCount = 0;
                                    SetRect(&pData->rdh.rcBound,
                                            MAXLONG, MAXLONG, 0, 0);
                                }
                            }
                        }

                        // Go to next row (remember, the bitmap is
                        // inverted vertically)
                        p32 -= bm32.bmWidthBytes;
                    }

                    // Create or extend the region with the remaining
                    // rectangles
                    HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) +
                                             (sizeof(RECT) * maxRects), pData);
                    if (hRgn)
                    {
                        CombineRgn(hRgn, hRgn, h, RGN_OR);
                        DeleteObject(h);
                    }
                    else
                        hRgn = h;

                    // Clean up
                    GlobalFree(hData);
                    SelectObject(hDC, holdBmp);
                    DeleteDC(hDC);
                }
                DeleteObject(SelectObject(hMemDC, holdBmp));
             }
              DeleteDC(hMemDC);
           }
         }

           return hRgn;
        }
Comment 7 Анатолий Лютин 2008-06-26 15:43:35 MSD
И вообще:
http://faqs.org.ru/progr/windows/win32_prog.htm
Comment 8 Vitaly Lipatov 2008-06-26 16:30:51 MSD
Похоже с документированием этой темы достаточно плохо, раз есть ссылки только на FAQ для новичков и статьи, в которых их автору предлагают почитать что такое сабклассинг :)
Comment 9 Анатолий Лютин 2008-06-26 16:35:02 MSD
(In reply to comment #8)
> Похоже с документированием этой темы
> достаточно плохо, раз есть ссылки только на
> FAQ для новичков и статьи, в которых их
> автору предлагают почитать что такое
> сабклассинг :)
> 
Вовсе нет, просто я подобрал наиболее понятные статьи для тех кто "не в теме".
Официальная документация:
http://msdn.microsoft.com/en-us/library/bb773183.aspx
http://msdn.microsoft.com/en-us/library/aa268967(VS.60).aspx
http://msdn.microsoft.com/en-us/library/ms997565.aspx
http://msdn.microsoft.com/en-us/library/ms229681.aspx
http://msdn.microsoft.com/en-us/library/ms997511.aspx

В общем документации навалом.
Comment 10 Vitaly Lipatov 2008-06-26 16:59:31 MSD
Вообще-то я хотел узнать твои соображения как это могло бы быть сделано, а получил ответ "читайте правила, там всё написано".
Comment 11 Анатолий Лютин 2008-06-26 17:08:18 MSD
(In reply to comment #10)
> Вообще-то я хотел узнать твои соображения
> как это могло бы быть сделано, а получил
> ответ "читайте правила, там всё написано".
> 

Читайте комментарий номер 2 сначала. Я ответил:
> Я до сих пор не смог понять как такая бага
> может быть организована.
Нужно особое заполнение структуры класса
окна.
Comment 12 Анатолий Лютин 2010-01-12 23:59:20 MSK
И собственно надо сделать так, чтобы структура описания окна, которое заполнило приложение, не переписывалась wine-ном.

Наверное не так сложно, если покапаться...
Comment 13 Vitaly Lipatov 2012-01-18 02:53:16 MSK
Больше не поручаем заданий bugs@.