Форум программистов, компьютерный форум, киберфорум
C++: WinAPI
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.67/6: Рейтинг темы: голосов - 6, средняя оценка - 4.67
0 / 0 / 0
Регистрация: 02.03.2019
Сообщений: 12
1

Непонятный баг (я думаю с BS_OWNERDRAW); выдает ошибку 6(invalid handle)

09.06.2019, 19:03. Просмотров 1122. Ответов 10


В общем пишу что-то вроде менеджера загрузок(пока что только окошко к нему). Использую winapi. Все хорошо, создал окошки, кнопочки пауза/удалить и все работает. Но внешний вид кнопочек не понравился мне и я решил поставить на кнопочку BS_OWNERDRAW. Все работает,кнопочки стали красивее, но есть один неприятный баг. Когда много(ну не так уж и много, раз 50 наверно - а это совсем крошечное число) раз покликать, то окно выходит из вот такого цикла, обрабатывающего сообщения:
C++
1
2
3
4
5
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
}
Естественно после этого окно ни на что не реагирует. Заменил while(GetMessage(&Msg, NULL, 0, 0) > 0) на while(true) и на всякий случай сделал try catch.
C++
1
2
3
4
5
6
try {
    GetMessage(&Msg, NULL, 0, 0);
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
    }
catch (bool qwe) {}
Теперь окно после пары десятков кликов реагирует на сообщения как-то неадекватно. Например, я все еще могу закрыть окно, показать его(но оно уже не рисуется - просто прозрачная фигня без рамки), контекстное меню при клике на иконку в трее не показывает(хотя WM_CONTEXTMENU все-таки приходит и обрабатывается). Попробовал вставить в свой WndProc GetLastError() и поставил брейкпоинт в студии. Ошибку показывает 6(ERROR_INVALID_HANDLE). Ну и еще заметил что после того как выдает эту ошибку(я не знаю,на каком конкретно этапе ее выдает) в cообщении WM_DRAWITEM (DRAWITEMSTRUCT*)lParam->hDc становится NULL. Помогите пожалуйста, я в с++ особо не шарю и не подозреваю даже, конкретно в чем заключается проблема.
Если надо, вот мой WM_DRAWITEM:
C++
1
2
3
4
5
6
7
8
case WM_DRAWITEM:
        pdis = (DRAWITEMSTRUCT*)lParam;
        switch (pdis->CtlID) {
        case ID_BUTTON11:
            iResult = myManageOwnerDrawIconButton(pdis, g_hInst);
            return TRUE;
            break;
                        ...
Вот моя функция, которой я рисую(скопипастил с интернета):
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int myManageOwnerDrawIconButton(DRAWITEMSTRUCT* pdis, HINSTANCE hInstance) {
 
    static RECT rect;
    static int iCount = 0;
 
    rect = pdis->rcItem;
 
    if (iCount < 1) {
        iCount++;
        hDnIconPause = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PAUSE)); //hDnIconPause уже объявлен глобально, как и две кнопки ниже.
        hDnIconRem = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_REM));
        hDnIconPlay = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PLAY));
    }
    if (ID_BUTTON11 == pdis->CtlID) {
        hCurrIcon = hDnIconPlay;
    }
    //тут еще кучка кнопок с иконками...
 
    if (!DrawIconEx(
        pdis->hDC,
        (int) 0.5 * (rect.right - rect.left - 33),
        (int) 0.5 * (rect.bottom - rect.top - 33),
        (HICON)hCurrIcon,
        35,
        35,
        0, NULL, DI_NORMAL
    )) {
    }
 
    return TRUE;
}
вот мои окошки:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    g_hInst = hInstance;
    getWorkArea(width, height);
    RegisterWindowClass(szWindowClass, MAKEINTRESOURCE(IDM_IDM), WndProc);
    RegisterWindowClass(szDownloadsClass, NULL, DownProc);
    RegisterWindowClass(szSpacerClass, NULL, SpacerProc);
    LoadString(hInstance, IDS_APP_TITLE, szTitle, ARRAYSIZE(szTitle));
    HWND hwnd = CreateWindow(szWindowClass, szTitle, WS_MYWINDOW,
        (width-800)/2, (height-600)/2, 800, 600, NULL, NULL, g_hInst, NULL);
    HBRUSH brush = CreateSolidBrush(RGB(243, 236, 236));
    SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, (LONG)brush);
    if (hwnd)
    {
        ShowWindow(hwnd, 1);
        PauseDownload2 = CreateWindow(L"BUTTON", L"OK", WS_BUTTONS, 675, 25, 35, 35, hwnd, (HMENU)ID_BUTTON12, g_hInst, NULL);
        PauseDownload1 = CreateWindow(L"BUTTON", L"OK", WS_BUTTONS, 675, 25, 35, 35, hwnd, (HMENU)ID_BUTTON11, g_hInst, NULL);
        Number1 = CreateWindow(L"BUTTON", NULL, WS_BUTTONS, 20, 25, 35, 35, hwnd, (HMENU)ID_NUM1, g_hInst, NULL);
        Remove1 = CreateWindow(L"BUTTON", NULL, WS_BUTTONS, 710, 25, 35, 35, hwnd, (HMENU)ID_REM1, g_hInst, NULL);
        Download1 = CreateWindow(szDownloadsClass,NULL, WS_DOWNLOAD, 5, 0, 775, 75, hwnd, NULL, g_hInst, NULL);
        Spacer1 = CreateWindow(szSpacerClass, NULL, WS_SPACER, 20, 76, 745, 5, hwnd, NULL, g_hInst, NULL);
                //дальше еще куча подобных окошек просто с другими координатами
вот стили классов:
C++
1
2
3
4
5
#define WS_MYWINDOW (WS_OVERLAPPED | WS_CAPTION  | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZE)
 
#define WS_DOWNLOAD         (WS_CHILD)
#define WS_SPACER           (WS_CHILD|WS_BORDER)
#define WS_BUTTONS              (WS_CHILD|WS_TABSTOP |BS_DEFPUSHBUTTON|BS_OWNERDRAW)
Добавлено через 10 минут
Я имел ввиду если много раз покликать не просто по окну,а конкретно по кнопкам.
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
09.06.2019, 19:03
Ответы с готовыми решениями:

VirtualAllocEx возвращает ошибку The handle is invalid
вопрос собственно уже в заголовке , не пойму почему ? Хэндл точно находит нормально !!!...

Выдает ошибку invalid numeric input
program Project2; {$APPTYPE CONSOLE} uses SysUtils; var y,x,s,an,eps:real; ...

Word. Выдает ошибку Invalid outside procesure
В чем может быть проблема?

загрузка формы! ! выдает ошибку: Invalid property value
Доброго времени суток! Есть форма, ввода пароля, если пароль верный то она открывает форму...

10
6406 / 4302 / 1740
Регистрация: 07.05.2019
Сообщений: 13,130
Записей в блоге: 1
09.06.2019, 19:05 2
Цитата Сообщение от andruuuxa Посмотреть сообщение
В общем пишу что-то вроде менеджера загрузок(пока что только окошко к нему). Использую winapi. Все хорошо, создал окошки, кнопочки пауза/удалить и все работает. Но внешний вид кнопочек не понравился мне и я решил поставить на кнопочку BS_OWNERDRAW. Все работает,кнопочки стали красивее, но есть один неприятный баг. Когда много(ну не так уж и много, раз 50 наверно - а это совсем крошечное число) раз покликать, то окно выходит из вот такого цикла, обрабатывающего сообщения:
А покажи полный код myManageOwnerDrawIconButton и обработчик нажатия ID_BUTTON11
0
0 / 0 / 0
Регистрация: 02.03.2019
Сообщений: 12
09.06.2019, 19:25  [ТС] 3
Виновником оказалась строка
C++
1
HFONT font = CreateFont(-14, 0, 0, 0, 0, ANSI_CHARSET, 0U, 0U, 0U, 0U, 0U, 0U, 0U, TEXT("Arial"));
в начале моего WndProc. Удалив её, баг я воспроизвести не могу. Но может ли кто объяснить, почему так? Может мне стоило засунуть CreateFont куда-нибудь вне WndProc? Или может мне нужно было его инициализировать в WM_PAINT?

Добавлено через 4 минуты
Вот обработчик нажатия кнопки:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    case WM_COMMAND:
        {
            int const wmId = LOWORD(wParam);
            switch (wmId)
            {
            case 3001:
            {
                down1p = false;
                ShowWindow(PauseDownload1, 0);
                ShowWindow(PauseDownload2, 1);
                if(!enabled) SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_ENABLE2, 0), 0);
                return TRUE;
            }
            break;
Код функции прорисовки я итак предоставил более-менее полный. Все что я оттуда убрал это еще куча таких же кнопок. Но если все-таки надо, то вот он:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
int myManageOwnerDrawIconButton(DRAWITEMSTRUCT* pdis, HINSTANCE hInstance) {
 
    static RECT rect;
    static int iCount = 0;
 
    // Use copy of rectangle:
    rect = pdis->rcItem;
 
    if (iCount < 1) {
        // LoadIcon only loads once, but LoadImage does not, 
        // so in case you call the latter, use a static counter:
        iCount++;
        // Inactive buttons:
        hDnIconPause = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PAUSE));
        hDnIconNum1 = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ONE));
        hDnIconNum2 = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TWO));
        hDnIconNum3 = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_THREE));
        hDnIconNum4 = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FOUR));
        hDnIconNum5 = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FIVE));
        hDnIconNum6 = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SIX));
        hDnIconRem = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_REM));
 
        // Active buttons:
        hDnIconPlay = (HICON)LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PLAY));
    }
    if (ID_BUTTON11 == pdis->CtlID) {
        hCurrIcon = hDnIconPlay;
    }
    if (ID_BUTTON12 == pdis->CtlID) {
        hCurrIcon = hDnIconPause;
    }
    if (ID_BUTTON21 == pdis->CtlID) {
        hCurrIcon = hDnIconPlay;
    }
    if (ID_BUTTON22 == pdis->CtlID) {
        hCurrIcon = hDnIconPause;
    }
    if (ID_BUTTON31 == pdis->CtlID) {
        hCurrIcon = hDnIconPlay;
    }
    if (ID_BUTTON32 == pdis->CtlID) {
        hCurrIcon = hDnIconPause;
    }
    if (ID_BUTTON41 == pdis->CtlID) {
        hCurrIcon = hDnIconPlay;
    }
    if (ID_BUTTON42 == pdis->CtlID) {
        hCurrIcon = hDnIconPause;
    }
    if (ID_BUTTON51 == pdis->CtlID) {
        hCurrIcon = hDnIconPlay;
    }
    if (ID_BUTTON52 == pdis->CtlID) {
        hCurrIcon = hDnIconPause;
    }
    if (ID_BUTTON61 == pdis->CtlID) {
        hCurrIcon = hDnIconPlay;
    }
    if (ID_BUTTON62 == pdis->CtlID) {
        hCurrIcon = hDnIconPause;
    }
    if (ID_NUM1 == pdis->CtlID) {
        hCurrIcon = hDnIconNum1;
    }
    if (ID_NUM2 == pdis->CtlID) {
        hCurrIcon = hDnIconNum2;
    }
    if (ID_NUM3 == pdis->CtlID) {
        hCurrIcon = hDnIconNum3;
    }
    if (ID_NUM4 == pdis->CtlID) {
        hCurrIcon = hDnIconNum4;
    }
    if (ID_NUM5 == pdis->CtlID) {
        hCurrIcon = hDnIconNum5;
    }
    if (ID_NUM6 == pdis->CtlID) {
        hCurrIcon = hDnIconNum6;
    }
    if (ID_REM1 == pdis->CtlID) {
        hCurrIcon = hDnIconRem;
    }
    if (ID_REM2 == pdis->CtlID) {
        hCurrIcon = hDnIconRem;
    }
    if (ID_REM3 == pdis->CtlID) {
        hCurrIcon = hDnIconRem;
    }
    if (ID_REM4 == pdis->CtlID) {
        hCurrIcon = hDnIconRem;
    }
    if (ID_REM5 == pdis->CtlID) {
        hCurrIcon = hDnIconRem;
    }
    if (ID_REM6 == pdis->CtlID) {
        hCurrIcon = hDnIconRem;
    }
 
    // Center the icon inside the control's rectangle:
    if (!DrawIconEx(
        pdis->hDC,
        (int) 0.5 * (rect.right - rect.left - 33),
        (int) 0.5 * (rect.bottom - rect.top - 33),
        (HICON)hCurrIcon,
        35,
        35,
        0, NULL, DI_NORMAL
    )) {
    }
 
    return TRUE;
}
0
6406 / 4302 / 1740
Регистрация: 07.05.2019
Сообщений: 13,130
Записей в блоге: 1
09.06.2019, 19:25 4
Цитата Сообщение от andruuuxa Посмотреть сообщение
в начале моего WndProc. Удалив её, баг я воспроизвести не могу. Но может ли кто объяснить, почему так? Может мне стоило засунуть CreateFont куда-нибудь вне WndProc? Или может мне нужно было его инициализировать в WM_PAINT?
Покажи, где ты использовал этот шрифт. Думаешь здесь кто-то умеет читать мысли?
0
0 / 0 / 0
Регистрация: 02.03.2019
Сообщений: 12
09.06.2019, 19:27  [ТС] 5
Этот шрифт,к моему удивлению, я нигде не использовал. Код скопипастил с другого моего проекта, поэтому скорее всего данную строку просто забыл убрать.
0
6406 / 4302 / 1740
Регистрация: 07.05.2019
Сообщений: 13,130
Записей в блоге: 1
09.06.2019, 19:28 6
Цитата Сообщение от andruuuxa Посмотреть сообщение
Код функции прорисовки я итак предоставил более-менее полный. Все что я оттуда убрал это еще куча таких же кнопок. Но если все-таки надо, то вот он:
К делу не относится, но воспользовался бы ты там оператором swith
1
0 / 0 / 0
Регистрация: 02.03.2019
Сообщений: 12
09.06.2019, 19:29  [ТС] 7
Если надо,то вот как выглядело начало моего WndProc:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;
    HRGN bgRgn;
    HBRUSH hBrush;
    HPEN hPen;
    HGDIOBJ old;
    DWORD errr;
    static DRAWITEMSTRUCT* pdis;
    HFONT font = CreateFont(-14, 0, 0, 0, 0, ANSI_CHARSET, 0U, 0U, 0U, 0U, 0U, 0U, 0U, TEXT("Arial"));
    switch (message)
    {
    case WM_CREATE:
        s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
        if (!AddNotificationIcon(hwnd))
        {
            MessageThenExit(hwnd, "no handle", L"Couldn't add Icon!", true, false);
        }
        break;
Закомментив HFONT, бага больше не наблюдаю(хотя это не говорит о том,что он полностью ликвидирован)
0
6406 / 4302 / 1740
Регистрация: 07.05.2019
Сообщений: 13,130
Записей в блоге: 1
09.06.2019, 19:33 8
Лучший ответ Сообщение было отмечено andruuuxa как решение

Решение

Цитата Сообщение от andruuuxa Посмотреть сообщение
Закомментив HFONT, бага больше не наблюдаю(хотя это не говорит о том,что он полностью ликвидирован)
Подозреваю, полностью. Здесь у тебя на каждое сообщение (а их приходит очень много) загружался шрифт. DeleteObject я так понимаю не делалось.
1
0 / 0 / 0
Регистрация: 02.03.2019
Сообщений: 12
09.06.2019, 19:37  [ТС] 9
Да, скорее всего ты прав. Мне нужно было быть внимательнее. Не мог бы ты посоветовать, куда бы мне засунуть этот шрифт? Я думаю мне стоит объявить его в начале WndProc, а инициализировать там,где использую DrawText(). Все правильно?

Добавлено через 2 минуты
Вот мой WM_PAINT:
C++
1
2
3
4
5
6
7
8
9
            hWnd = GetRealParent(hwnd);
            wdc = GetWindowDC(hWnd);
            GetClientRect(hWnd, &rect);
            SetTextColor(wdc, 0x00000000);
            SetBkMode(wdc, TRANSPARENT);
            rect.left = 100;
            rect.top = 80;
            DrawText(wdc, d1t, -1, &rect, DT_SINGLELINE);
            ReleaseDC(hWnd, wdc);
У меня здесь уже есть ReleaseDC(). Нужно ли здесь если буду добавлять шрифт вызывать DeleteObject(font)? Если да,то перед или после ReleaseDC()?
0
6406 / 4302 / 1740
Регистрация: 07.05.2019
Сообщений: 13,130
Записей в блоге: 1
09.06.2019, 19:42 10
Цитата Сообщение от andruuuxa Посмотреть сообщение
Да, скорее всего ты прав. Мне нужно было быть внимательнее. Не мог бы ты посоветовать, куда бы мне засунуть этот шрифт? Я думаю мне стоит объявить его в начале WndProc, а инициализировать там,где использую DrawText(). Все правильно?
Нет, не правильно. Этот и другие объекты надо хранить во внешней структуре, а не в стеке WndProc, которая вызывается/завершается на каждое сообщение

Добавлено через 49 секунд
Хотя бы сделай их все static, и не забудь удалить по WM_DESTROY или типа того
1
0 / 0 / 0
Регистрация: 02.03.2019
Сообщений: 12
09.06.2019, 19:44  [ТС] 11
Хорошо, спасибо, так и сделаю.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
09.06.2019, 19:44

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь или здесь.

Когда обращаюсь к mysql, то выдает ошибку Invalid library.
Поставил под винду апач, пхп и mysql. А вот когда обращаюсь к mysql, то выдает ошибку Invalid...

У меня выдаёт ошибку: [Error] invalid array assignment
#include &lt;iostream&gt; #include &lt;algorithm&gt; #include &lt;numeric&gt; #include &lt;iomanip&gt; #include &lt;ctime&gt;...

Выдает ошибку ORA-04098: trigger is invalid and failed re-validation
создаю триггер, который после добавления нового клиента в таблицу клиент создает новый заказ в...

JavaScript. After Effects выдает ошибку invalid numeric result (divide by zero )
Следующее выражение - sc = thisComp.layer(&quot;icon3&quot;).transform.scale; frame =...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.