Перейти к содержанию

Antonshka

Пользователи+
  • Постов

    410
  • Зарегистрирован

  • Посещение

  • Победитель дней

    16

Сообщения, опубликованные Antonshka

  1. Привет всем, подскажите, как удалить из очереди сообщений потока определенное сообщение/сообщения? Кто-нибудь игрался с этим? Что-то как-то темно все с этим в интернете.

     

    Ситуация примерно такая:

    - Поток A создал кнопку.

    - При клике на эту кнопку, поток A создал поток Б.

    - Поток Б через 30 секунд запостит (используя PostMessageW) в очередь сообщений потока A специальное мною определенное сообщение. Запостит и завершится.

    - Однако, я передумал, и захотел отменить поток Б. Поток А, спустя 29.99 секунд после создания потока Б, идет и выполняет функцию отмены.

     

    Но, поток А не успел, и поток Б уже запостил сообщение. После возвращения потока А из функции отмены потока Б, он найдет сообщение от потока Б, и начнет его выполнять.

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

    Затем, поток А, вернется из функции отмены в MessageLoop функцию, и когда вызовет GetMessage, найдет что сообщения от потока Б уже нет. Значит оно не исполниться.

  2. 10 часов назад, MasterGH сказал:

    Скорее всего, только свой компонент делать. Я бы поигрался с Image компонентом. В нем менять картинки этого чекбокса  по обраотчику.

    Я игрался как-то раз с ним. Хотел сделать чекбоксы в виде слайдеров-переключателей. В каждом положении было по две картинки, одна видимая, другая нет. При клике они менялись видимостью.

    Такой трейнер занимал 1 гигабайт RAM памяти. Пришлось делать стандартные чекбосы.

    Не помню, использовал ли я тогда collectgarbage или нет. И помогает ли он вообще в этой ситуации.

  3. 3 часа назад, Pitronic сказал:

    Вопрос короткий.

    При создании формы на UDF1 с чекбоксами. Что можно добавить в lua чтоб чекбокс менял цвета, если галочка стоит цвет чекбокса красный, а если нет цвет чекбокса по дефолту чёрный, чтоб красивее выглядели функции активация диактивация.

    Я в свое время хотел поменять цвет текста на обычной СЕ кнопке. Но это невозможно.

    Спойлер

    The WM_CTLCOLORBTN message is sent to the parent window of a button before drawing the button. The parent window can change the button's text and background colors. However, only owner-drawn buttons respond to the parent window processing this message.

     

    Единственный выход, рисовать самому. В СЕ в celua.txt я видел такую возможность.

    Спойлер

    CECustomButton class(Inheritance: CustomControl->WinControl->Control->Component->Object)
    A more customizable button instead of the windows theme'd button, and lets you repaint it from scratch as well

    createCECustomButton(owner)

    properties
      ShowPrefix: boolean - Process first single '&' per line as an underscore and draw '&&' as '&'  
      BorderColor: Color  - The color of the button border
      BorderSize: integer - The thickness of the border
      ButtonColor: Color - The color of the button face
      ButtonHighlightedColor: Color  - The color of the buttonface when highlighted(hovered over with the mouse)
      ButtonDownColor: Color - The color of the buttonface when the mouse is pressed down on it
      DrawFocusRect: boolean   - If true will draw a focus roundrect showing it has focus
      DrawBorder: boolean - default=true.  Will draw a border around the button
      FocusedSize: integer - The with of the focus roundrect
      FocusElipseColor: Color - The color of the focus roundrect
      GrowFont: boolean read - When true the font will get resized till the caption fits the button
      RoundingX: integer
      RoundingY: integer
      CustomDrawn: boolean - Do your own drawing in the OnPaint property of the button
      FramesPerSecond: integer - If animation is used this will determine how often per second the OnPaint gets called
      ButtonAnimationSpeed: integer - If not customdrawn, this determnines how long the animations for enter/leave take

     

  4. 7 часов назад, imaginary сказал:

    Всё очень просто, надо написать блок __try и __except и всё, можешь пытаться разыменовывать что угодно, а исключение интерпретировать как неправильный указатель с мусором вместо адреса.

    У меня такой способ не работает.

    Спойлер
    #include <windows.h>
    #include <iostream>
    
    
    int main() {
        DWORD_PTR* processAddress = reinterpret_cast<DWORD_PTR*>(GetModuleHandleW(NULL));
        processAddress += 0x10000;
    
        __try {
            if (processAddress  && *processAddress) { //Завершение работы прилжения
                std::wcout << L"Good";
            }
        }
        __except (EXCEPTION_EXECUTE_HANDLER){
            std::wcout << L"Bad";
        }
    
        //-----------------------------------------------------------------
    
        INT v1 = 1;
        INT v2 = 0;
        __try {
            if (v1 / v2) {
                std::wcout << L"Good";
            }
        }
        __except (EXCEPTION_EXECUTE_HANDLER) { // Выводится "Bad"
            std::wcout << L"Bad";
        }
    
        return 0;
    }

     

     

    Когда есть предусмотренный способ проверки через VirtualQuery, зачем использовать try/except?

    Я кстати в библиотеке WGW нигде не использую обработку ошибок. Если ее использовать, то использовать по максимуму. А если так, то код превратится не пойми во что.

  5. 24 минуты назад, Xipho сказал:

    С Qt

    Понятно.

    Кстати я сейчас смотрел исходники QT. Там тоже есть интересные моменты в коде, как и у меня

    Спойлер
    void QGridLayoutPrivate::setupSpacings(QList<QLayoutStruct> &chain, QGridBox *grid[],
                                           int fixedSpacing, Qt::Orientation orientation)
    {
        Q_Q(QGridLayout);
        int numRows = rr;       // or columns if orientation is horizontal
        int numColumns = cc;    // or rows if orientation is horizontal
    
        if (orientation == Qt::Horizontal) {
            qSwap(numRows, numColumns);
        }
    
        QStyle *style = nullptr;
        if (fixedSpacing < 0) {
            if (QWidget *parentWidget = q->parentWidget())
                style = parentWidget->style();
        }
    
        for (int c = 0; c < numColumns; ++c) {
            QGridBox *previousBox = nullptr;
            int previousRow = -1;       // previous *non-empty* row
    
            for (int r = 0; r < numRows; ++r) {
                if (chain.at(r).empty)
                    continue;
    
                QGridBox *box = gridAt(grid, r, c, cc, orientation);
                if (previousRow != -1 && (!box || previousBox != box)) {
                    int spacing = fixedSpacing;
                    if (spacing < 0) {
                        QSizePolicy::ControlTypes controlTypes1 = QSizePolicy::DefaultType;
                        QSizePolicy::ControlTypes controlTypes2 = QSizePolicy::DefaultType;
                        if (previousBox)
                            controlTypes1 = previousBox->item()->controlTypes();
                        if (box)
                            controlTypes2 = box->item()->controlTypes();
    
                        if ((orientation == Qt::Horizontal && hReversed)
                                || (orientation == Qt::Vertical && vReversed))
                            qSwap(controlTypes1, controlTypes2);
    
                        if (style)
                            spacing = style->combinedLayoutSpacing(controlTypes1, controlTypes2,
                                                 orientation, nullptr, q->parentWidget());
                    } else {
                        if (orientation == Qt::Vertical) {
                            QGridBox *sibling = vReversed ? previousBox : box;
                            if (sibling) {
                                if (sibling->item()->isEmpty()) {
                                    spacing = 0;
                                } else {
                                    QWidget *wid = sibling->item()->widget();
                                    if (wid)
                                        spacing = qMax(spacing, sibling->item()->geometry().top() - wid->geometry().top());
                                }
                            }
                        }
                    }
    
                    if (spacing > chain.at(previousRow).spacing)
                        chain[previousRow].spacing = spacing;
                }
    
                previousBox = box;
                previousRow = r;
            }
        }
    }

     

     

    Я заметил что в WGW Layout система уступает той что в QT. Теперь хочу доработать.

    Подумал, посмотрю как там сделано в QT. Но при виде кода решил что легче придумать самому. Пока разберешься, что там и к чему.

    Как было с Docking.

  6. 5 часов назад, Xipho сказал:

    Есть, но поверхностно - делал UI управления настройками устройства для депарафинизации нефтяных скважин. Там не было необходимости дорабатывать существующие контролы.

    У тебя только с QT мало опыта, или вообще с GUI?

  7. 1 час назад, Xipho сказал:

    Есть, но поверхностно - делал UI управления настройками устройства для депарафинизации нефтяных скважин. Там не было необходимости дорабатывать существующие контролы.

    Получается это коммерческая ситуация? Нужно было платить как-то за использование библиотеки QT?

    1 час назад, Xipho сказал:

    Вопрос подхода. Qt в этом случае выбрали наследование, а ты выбираешь композицию. Сходу нельзя сказать, лучше что-то из этого, или хуже. Тут вопрос больше удобства кодинга. В случае с композицией у тебя ответственность смещается в сторону "композитора", то есть того, кто создает кнопку. В случае с наследованием всё, что связано с кнопками, инкапсулировано в классах кнопок.

    По моему личному скромному мнению - всё, что связано с GUI контролами выглядит как вполне подчинающееся иерархии классов, и, следовательно, тут хорошо подходит наследование. Но наследование должно быть грамотным, продуманным.

    Я сейчас посмотрел видео, где пишут калькулятор на QT. Мне понравилось. Что-то в этом наследование есть.

    Нужно подумать, может получиться совместить то что сейчас в WGW с тем что в QT.

  8. В 03.08.2022 в 09:24, Xipho сказал:

    Твоя обязанность как разработчика библиотеки - максимально обезопасить пользователя от подобных ошибок. Пользователь НЕ должен иметь доступа на запись полей внутри класса.

    А у тебя есть опыт работы с QT? В QT, как я понял, можно расширять классы контролов. Наследуясь от них.

    В WGW же, создание интерфейса осуществляется как бы процедурно, не через наследование.

    Спойлер
    WND_MAIN = new WGW::Window(TRUE, 0, 0, L"MainWindowName", 20, 20, 900, 900); 
    WND_MAIN->onClose = OnCloseFunction; // Это обычная функция, не метод какого-либо класса
    WND_MAIN->color = RGB(188, 190, 220);
    WND_MAIN->onPaint = OnPaintFunctionName; // Это обычная функция, не метод какого-либо класса
    WND_MAIN->onEraseBkg = OnErase;
    WND_MAIN->onClick = functionName1; // Это обычная функция, не метод какого-либо класса
    
    BTN1 = new WGW::ButtonStandart(TRUE, 0, 0, L"Btn1", 10, 100, 100, 100, WND_MAIN);
    BTN1->toolTip.enabled = TRUE;
    BTN1->toolTip.titleText.append(L"ToolTipTitle ", RGB(10, 10, 200));
    BTN1->toolTip.titleText.append(L"ToolTipTitleSecond1  \n", RGB(144, 140, 10));
    BTN1->onClick = functionName2; // Это обычная функция, не метод какого-либо класса

     

     

    В QT события как я понял обрабатываются в методах класса, а не в простых функция как в WGW.

    И еще я кажется видел что в QT можно создать контрол через оператор "new". Не через наследование. То есть как я понял просто используя то что QT дает стандартно .

     

    - в QT есть расширение классов

    - в QT события обрабатываются в методах класса, а не в простых функцияч, как в WGW.

     

    Интересно, WGW от этого сильно проигрывает QT?

     

    Ну хорошо, наследовал в QT класс QPushButton, расширин функционал, добавил что новый класс при нажатии на кнопку пиликает. И теперь все кнопки созданные этим классом будут пиликать.

    А в WGW, вместо этого, можно создать файл PilicedButtons.h, там определить функцию на событие кнопка нажата, а в ней уже делать пиликанье. Затем все простые кнопки направить при нажатии на эту функцию. Получаем что и в QT.

  9. 8 часов назад, MasterGH сказал:

    Через WinAPI проверять доступные регионы памяти

    А ведь логично. Должна же API предоставлять какой-то механизм исследования участка памяти.

    Рихтера читал недавно, но видимо не запечатлелось. Из MSDN собрал такое

    Спойлер
    #include <windows.h>
    #include <iostream>
    
    
    BOOL isAddressAccesable(LPVOID address) {
        BOOL isAddressAccesable = FALSE;
        MEMORY_BASIC_INFORMATION memoryInfo{ 0 };
        SIZE_T result = VirtualQueryEx(GetCurrentProcess(), address, &memoryInfo, sizeof(MEMORY_BASIC_INFORMATION));
        if (result && memoryInfo.State == MEM_COMMIT) {
            isAddressAccesable = TRUE;
        }
        return isAddressAccesable;
    }
    
    int main() {
        DWORD_PTR* processAddress = reinterpret_cast<DWORD_PTR*>(GetModuleHandleW(NULL));
        processAddress += 0x10000;
    
        if (isAddressAccesable(processAddress)) {
            processAddress = reinterpret_cast<DWORD_PTR*>(*processAddress);
        }
        return 0;
    }

     

     

    9 часов назад, youneuoy сказал:

    ага, мусор будет или ошибка чтения. Как в cheat engine когда структуру смотришь - оно указателями всё подряд считает и даже развернуть их можно. 

    Да, есть такой момент.

  10. 11 часов назад, Antonshka сказал:

    Интересно как на С++ проверяется валидность значения адреса.

    Кажется вот рабочий способ. Не знаю насколько это правильно, но работает.

    Спойлер
    #include <windows.h>
    #include <iostream>
    
    
    int main() {
        DWORD_PTR* processAdress = reinterpret_cast<DWORD_PTR*>(GetModuleHandleW(NULL));
        processAdress += 0x10000;
        BYTE byteData = 0;
        BOOL accessResult = ReadProcessMemory(GetCurrentProcess(), processAdress, &byteData, sizeof(BYTE), NULL);
        
        if (accessResult) {
            int d = *processAdress;
        }
        return 0;
    }

     

    Каждый раз перед проверкой доступа вызывать ReadProcessMemory.

    Спойлер

    Return value

    If the function succeeds, the return value is nonzero.

    If the function fails, the return value is 0 (zero). To get extended error information, call GetLastError.

     

  11. 2 часа назад, Xipho сказал:

    Твоя обязанность как разработчика библиотеки - максимально обезопасить пользователя от подобных ошибок. Пользователь НЕ должен иметь доступа на запись полей внутри класса. Да, пользователь может сделать переменную и ее переназначить - и вот это уже будет на совести пользователя. А вот открытые к изменению публичные поля, это еще одна точка отказа, за которую ответственность несешь ты, как разработчик библиотеки. В идеальном мире ни одно поле не должно быть публичным, а сеттер поля должен делать валидацию поступающих на вход значений. У того же HDC в OnPaintEvent можно сеттер вообще сделать приватным, как и само поле, и тогда снаружи его изменить будет нельзя. Не устаю говорить всегда о том, что пользователю библиотеки ни в коем случае нельзя доверять. Потому нужно стараться максимально закрыть от пользователя детали реализации, и открыть ему только те части, в которых не сможет накосячить. Ну или накосячит по минимуму. В данном конкретном случае я сходу не подскажу, как это можно сделать, надо посидеть, подумать.

    Я понял тебя. Ты говоришь вообще в целом. С этим я согласен, это я держу во внимании.

    Сейчас в библиотеке все так и есть. Все закрыто, ничего лишнего пользователь не то что изменить, даже и создать не может. Например никакой объект класса свойства контрола.

    Все что ему открыто, все безопасно.

     

    А я говорил про конкретную ситуацию с параметром функции события. Этот объект-параметр, специально имеет открытые поля, они безопасны, их можно изменять. Как я писал выше, по сути это все равно что вызвать метод такого объекта-параметра, просто библиотека делает это за пользователя.

    LRESULT OnPaintFunctionName(WGW::OnPaintEvent* onPaintEvent) {
      	//Как в QT, получаем значение методом.
    	HDC compDC = onPaintEvent->getMyCompatibleDC(); 
    	FillRect(compDC, ...)
    
        //Как в WGW
        FillRect(onPaintEvent->compDС, ...)
    }

     

  12. Интересно как на С++ проверяется валидность значения адреса.

    HMODULE gameExeModule = GetModuleHandleW(L"ThisGame.exe");
    gameExeModule += 0x10E58C; 
    gameExeModule = *gameExeModule; //А если по *gameExeModule у нас NILL? Помню даже на форуме видел темы про это, только на СЕ.
    //Интересно такое допустимо на C++?
    if (*(gameExeModule))
    
    //Этот вопрос конечно же разрешается несколькими тестами. Но пока не до них.

     

  13. 14 минут назад, Xipho сказал:

    Подход не очень. Тот, кто будет пользовать твою библиотеку, может задуматься, ошибиться и записать в это поле какое-то значение. А потом будет тратить часы на отладку не понимая, почему получает графические артефакты (или что-нибудь еще менее приятное)

    Графические артефакты? Я немного не понял тебя.

    Если к примеру взять событие WM_PAINT.

    Вот обычная функция для события рисования

    Спойлер
    LRESULT OnPaintFunctionName(WGW::OnPaintEvent* onPaintEvent)
    {
    	RECT rect{ 0 };
    	GetClientRect(onPaintEvent->hWnd, &rect);
    	FillRect(onPaintEvent->compDC, &rect, reinterpret_cast<HBRUSH>(GetClassLongPtrW(onPaintEvent->hWnd, GCLP_HBRBACKGROUND)));
    	BitBlt(onPaintEvent->origDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, onPaintEvent->compDC, rect.left, rect.top, SRCCOPY);
        return TRUE;
    }
    
    int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
    {
    	WGW::GUI::initializeWGWLibrary(hInstance);
    	//-------------------------------------------
    	WND_MAIN = new WGW::Window(TRUE, 0, 0, L"MainWindowName", 20, 20, 1000, 900);
    	WND_MAIN->onPaint = OnPaintFunctionName;

     

     

    Это класс передаваемого объекта в параметре (указателя на него, сам он находится в классе окна, постоянно). Как видно из класса, только поля объявлены как public. Пользователь не может создать или обратится к этому объекту вне функции события. Создать его может только класс окна/контрола.

    Спойлер
    	struct OnPaintEvent : public OnEvent {
    		friend class Control;
    		friend class Window;
    	private:
    		OnPaintEvent() {}
    		OnPaintEvent(CONST OnPaintEvent&) = delete;
    		~OnPaintEvent() {}
    	public:
    		HDC compDC{ NULL };
    		HDC origDC{ NULL };
    		INT menuHeight{ 0 };
    		INT menuTopPosition{ 0 };
    		INT titleBarHeight{ 0 };
    	};
    	using OnPaint = LRESULT(*)(OnPaintEvent* onPaintEvent);

     

     

    А это то как заполняются поля при каждом сообщении WM_PAINT

    Спойлер
    	LRESULT Window::wmPaint(HWND& hWnd, UINT& msg, WPARAM& wParam, LPARAM& lParam) {
    		if (onPaint != nullptr || titleBar.enabled.isTitleBarEnabled && !::GetParent(hWnd) || menu.enabled.isMenuEnabled) {
    			if (GetUpdateRect(hWnd, NULL, FALSE)) {
    				ValidateRect(hWnd, NULL);
    				if (onPaint != nullptr) {
    					onPaintEvent.compDC = compatibleDC.compDC;
    					onPaintEvent.control = this;
    					onPaintEvent.hWnd = hWnd;
    					onPaintEvent.msg = msg;
    					onPaintEvent.wParam = wParam;
    					onPaintEvent.lParam = lParam;
    					onPaintEvent.origDC = compatibleDC.origDC;
    					if (titleBar.enabled.isTitleBarEnabled && !::GetParent(hWnd)) {
    						onPaintEvent.titleBarHeight = titleBar.titleBarRect.bottom;
    					}
    					else {
    						onPaintEvent.titleBarHeight = 0;
    					}
    					if (menu.enabled.isMenuEnabled)	{
    						onPaintEvent.menuHeight = menu.menuHeight;
    						onPaintEvent.menuTopPosition = menu.menuTopPosition;
    					}
    					else {
    						onPaintEvent.menuHeight = 0;
    						onPaintEvent.menuTopPosition = 0;
    					}
    					onPaint(&onPaintEvent);
    				}
    				if (titleBar.enabled.isTitleBarEnabled && !::GetParent(hWnd)) {
    					titleBar.onOwnerWmPaint();
    				}
    				if (menu.enabled.isMenuEnabled) {
    					menu.onOwnerWmPaint();
    				}
    				return 0;
    			}
    		}
    		return DefWindowProcW(hWnd, msg, wParam, lParam);
    	}

     

     

    Поля объекта получают копии значений. Оригиналы не затрагиваются.

    Если пользователь по ошибке запишет в поле compDC что-то свое, то да, код далее этого момента будет глючить. Но так подумать, это можно отнести вообще ко всем переменным.

     

    Например

    Спойлер
    LRESULT OnPaintFunctionName(WGW::OnPaintEvent* onPaintEvent)
    {
    	HDC hDC = onPaintEvent->getMyCompatibleDC(); //Как в QT, получаем значение методом.
    	//Продолжение кода.
    	hDC = 0; //Пользователь ошибся, изменил значение определенной самим же им переменной.
    	//Продолжение кода.
    }

     

     

    В WGW этот вызов

    HDC hDC = onPaintEvent->getMyCompatibleDC();

    выполняется за пользователя, автоматически и заранее.

    Все ошибки какие пользователь может совершить с переменными в данном случае в WGW, равны тем что в QT.

     

    Поэтому я не понял тебя.

  14. 2 часа назад, youneuoy сказал:

    Это не мой код🙂Я его взял у какого-то пользователя с форума когда программированию учился только и по этому шаблону наделал патчей в память.

    Понятно 🙂.

    1 час назад, Xipho сказал:

    Это на твоё усмотрение ) 

    Мы же вместе собрались изобретать велосипед. Пусть простой, но зато свой.

    Вчера я даже не смог скачать QT Creator. Хотел посмотреть как там устроены сигналы, слоты.

    VPN не стал организовывать, из роликов и документации я примерно понял что там и к чему. Мне кажется WGW в этом плане попроще будет.

     

    Я заметил что в QT в параметре метода события (например кнопка нажата), передается определенный для этого типа события объект. И чтобы получить какие-либо его характеристики, нужно вызвать его методы.

    //QPushButton Class
    virtual void 	keyPressEvent(QKeyEvent *e) override

    И некоторые методы QKeyEvent

    Спойлер
    int 	count() const
    bool 	isAutoRepeat() const
    int 	key() const
    QKeyCombination 	keyCombination() const
    bool 	matches(QKeySequence::StandardKey key) const
    Qt::KeyboardModifiers 	modifiers() const
    quint32 	nativeModifiers() const
    quint32 	nativeScanCode() const
    quint32 	nativeVirtualKey() const
    QString 	text() const

     

     

    А в WGW немного по другому. Вместо QKeyEvent объекта передается тоже определенный для этого события объект , но он не имеет методов. Он имеет public поля с уже подсчитанными/полученными значениями (из wParam, lparam). Такой подход в сравнении с QT удобнее, как мне кажется. Но есть один минус, - даже если пользователю не нужны эти данные в методе события, они все равно подсчитываются/получаются. То есть холостая работа. Хотя опять же, она такая незначительная, что я подумал так и оставить, а не писать методы для их получения.

    Либо разработчики гнались за максимальной производительностью, что даже такие мелочи оформили в отдельные методы. Либо это у них такой принцип. Не знаю.

  15. 30 минут назад, Xipho сказал:

    Оптимально будет выложить код на гитхаб в приватный репозиторий, и добавить желающих в контрибьюторы

    Понял, так и сделаю. Не буду ждать пока поправлю все.

    Несколько дней назад я создал Github аккаунт. Раза с 10 только получилось разобраться как правильно обновлять проект. Пока не знаю как вести обсуждения, впрочем, пока не до них.

    Я отправил приглашение для Xipho Github.

    @Xipho можно мне написать что этот проект совместная работа с тобой? А также всеми, кто помогал в разрешении вопросов, конечно же.

     

    Если у кого-то также есть желание, присоединяйтесь. Нужно ваше имя на Github, для отправки приглашения.

  16. 33 минуты назад, youneuoy сказал:

    метод update слишком размыт, а GUI, от которого наследуются виджеты - нет?😃

    По идее да, тоже размыто 😜. Но в случае с update я думаю что будут ложные холостые вызовы. Например если есть 100 кнопок, из которых активны только 99 . Система сделает 99 вызовов update просто так. 99 проверок if легче же для системы чем 99 вызовов?

     

    38 минут назад, youneuoy сказал:

    Вариантов масса, просто сделай как-нибудь для начала.

    Уже сделано, мы просто теперь обсуждаем правильность сделанного. Вообще просто обсуждаем как сделано.

  17. 59 минут назад, youneuoy сказал:

    это не так. У тебя ведь условие || есть. Для второй части выражения isAnchorEnabled теперь не существует.

     

    Теперь кажется понятно. Значить действие || распространяется на все вообще части, слева от него и справа.

    Спойлер
    #include <windows.h>
    #include <iostream>
    
    
    VOID  calculate() {
        std::wcout << L"calculated!";
    }
    
    int main() {
        INT isAnchorEnabled = 0;
        INT isInLayout = 1;
        INT layoutOwner = 1;
        INT layoutOwnerLayoutEnabled = 0;
    
        if (isAnchorEnabled && TRUE && !isInLayout || layoutOwner && !layoutOwnerLayoutEnabled) {
           calculate();
        }
    
    	//Действие || распространяется на всю левую часть - isAnchorEnabled && TRUE && !isInLayout
      	//и на всю правую часть - layoutOwner && !layoutOwnerLayoutEnabled
      
      	//Я же думал что только на - !isInLayout || layoutOwner
        return 0;
    }

     

     

    Со скобками получается можно избавиться от повторного сравнения isAnchorEnabled

    if ( isAnchorEnabled && (!isInLayout || layoutOwner && !layoutOwner->layout.enabled) ) {
       calculate();
    }

     

  18. 2 часа назад, Xipho сказал:

    Все логические операторы обладают одинаковым приоритетом и выполняются слева направо. Если тебе нужно сложное условие, ты можешь более простые условия объединять в круглые скобки, тогда приоритетно будут вычисляться выражения внутри них.

    В приведенном тобой примере получится 

    if ((isAnchorEnabled && !isInLayout) || (isAnchorEnabled && layoutOwner && !layoutOwner->layout.enabled))
      calculate();

     

    isAnchorEnabled повторяется два раза. Я поэтому и начал задумываться над тем как все это работает.

    Скобки в интернете советовали тоже, но что если без них? Как тогда поведет себя выражение. Хочу просто понять, а использовать можно и скобки.

     

    Можно ли так упростить

    if (isAnchorEnabled && !isInLayout || layoutOwner && !layoutOwner->layout.enabled)
      calculate();

    isAnchorEnabled если будет true, тогда по идее далее его нет смысла проверять.

    Если && и || работают только с тем что конкретно слева и справа от них (а не вообще все, до конца), тогда по идее такой вариант сработает.

    У меня еще была мысль посмотреть в Disassembler, посмотреть как там на самом деле работает && и || в таких длинных выражениях.

  19. 2 часа назад, Xipho сказал:

    Вот тут ты, на мой взгляд, слегка торопишься. Книги, которые ты прочитал, содержат примеры на Java, и используют общепринятую код конвенцию языка Java. Код конвенция C++ вполне себе может отличаться, этот момент лучше прокопать, учитывая, что библиотеку ты собираешься делать опенсорсной.

    Я столько много копал на этот момент. Плюс сам для себя пытался понять, как мне удобнее.

    В интернете большинство придерживается methodName. На счет скобок не нашел.

    Автор Imgui использует MethodName, и скобки все слева.

     

    Я перевел в одном классе все методы на methodName, перевел все поля-объекты также на camelCase (не поля обычных int, float... они и так были camelCase). И я нашел что для глаз так легче.

    BTN2->ToolTip.TitleText.Append(L"ToolTipTitleSecond1  \n", RGB(144, 140, 10)); //было
    
    BTN2->toolTip.titleText.append(L"ToolTipTitleSecond1  \n", RGB(144, 140, 10)); //стало

    Не знаю как это работает, но camelCase читается удобнее.

    Со скобками как в Java менее удобно, (наверно потому что не привык). Но зато с ними методы отделяются друг от друга более очевидно.

    Спойлер
    // было
    VOID ToolTip::onOwnerWmLButtonDown(LPARAM& lParam) 
    {
      if (threadLocal)
      {
        terminateThreadLocalForThisToolTipObject();
      }
    }
    
    VOID ToolTip::onOwnerWmMButtonDown(LPARAM& lParam) 
    {
      if (threadLocal) 
      {
        terminateThreadLocalForThisToolTipObject();
      }
    }
    
    VOID ToolTip::onOwnerWmRButtonDown(LPARAM& lParam) 
    {
      if (threadLocal) 
      {
        terminateThreadLocalForThisToolTipObject();
      }
    }
    
    // стало
    
    VOID ToolTip::onOwnerWmLButtonDown(LPARAM& lParam) {
      if (threadLocal) {
        terminateThreadLocalForThisToolTipObject();
      }
    }
    
    VOID ToolTip::onOwnerWmMButtonDown(LPARAM& lParam) {
      if (threadLocal) {
        terminateThreadLocalForThisToolTipObject();
      }
    }
    
    VOID ToolTip::onOwnerWmRButtonDown(LPARAM& lParam) {
      if (threadLocal) {
        terminateThreadLocalForThisToolTipObject();
      }
    }

     

     

    2 часа назад, Xipho сказал:

    Например, если у тебя есть класс базового окна, и класс окна с рамкой - выглядит, как будто у них будет очень много общих свойст, и если в данном случае ты будешь использовать композицию, то какой тогда смысл в разных классах окна?

    Класс окна всего один. Он создает простое окно. Он поддерживает все что только можно сделать с окном. Создал такой экземпляр, и навесил на него все что нужно, все что он позволяет на себя навесить. Стили, полосы прокрутки, альфа канал, переопределил WM_PAINT и получил в своей функции в параметре уже готовый CompatibleDC, только и рисуй на нем.

    Я примерно прикидывал, и не нашел ничего такого специфичного, чтобы не позволил сделать класс окна с окном. Потому не стал его раздваивать.

    С предопределенными WinAPI контролами (Button, EditBox), тоже самое что и с окном, что хочешь, то и навесил на них.

     

    Есть например и специфичные окна, как например самодельный PopupMenu. Можно было наследовать его о таким обрызом

    Спойлер

    spacer.png

    Но PopupMenu не нуждается ни в Menu ни в Полосах прокрутки. Поэтому PopupMenu выведен в свой собственный класс, и наследуется не от Control класса, как например это делают Window, Button, EditBox, а наследуется сразу от GUI.

    PopupMenu не нуждается в тех объектах-свойствах в которых нужнаются эти.

     

    Поэтому всему архитектура сейчас такая. Ее лучше как ты говоришь увидеть, но и так схематично тоже помогает понять что к чему.

     

    2 часа назад, Xipho сказал:

    но, еще раз повторюсь, всё нужно использовать в разумных пределах.

    Я запомнил этот момент. Лишний раз не нужно оверинженерить, как говорил ты и книга.

  20. Привет всем, недавно видел на форуме пост @imaginary

    [bad_alloc] переполнение памяти C++

    В 27.07.2022 в 20:53, imaginary сказал:

    Ты знаешь что некоторые думают что если проверять всё в одном месте то будет тратить много ресурсов, потому что если вложенно, то там первое не выполнится - другие не будут тратить время. Я тоже так думала когда не разбиралась как всё работает раньше. Так что не удивительно что многие делают много условий

    , и думал про себя, а я разбираюсь? Оказалось что нет.

    Короткие выражения я понимаю/ Как например

    if (a == 1 || a == 2)

    a == 2 проверяется только если a == 1 false.

    if (a == 1 && a == 2)

    a == 2 проверяется только если a == 1 true.

     

    Но как быть когда операторов много? Например

    if (isAnchorEnabled && !isInLayout || isAnchorEnabled && layoutOwner && !layoutOwner->layout.enabled)
      calculate();

    Нужно чтобы calculate() вызывался только если у контрола якорь активен и контрол НЕ в системе Layout.

    Либо если у контрола якорь активен и контрол В системе Layout, но система Layout не активна.

    && layoutOwner - эта проверка указателя на null перед тем как к нему обратиться.

     

    Не могу найти в интернете решение для такого выражения.

    Оператор || в данном примере оперирует только !isInLayout и isAnchorEnabled? Или он оперирует вообще всем что слева от него и всем что справа, и рссматривает все что слева как одну единицу и все что справа как одну единицу.

  21. 1 час назад, Xipho сказал:

    В целом выглядит неплохо, но смущает использование стратегии.

    Стратегия используется только в качестве свойств контролов. Без них я даже не представляю как организовать свойства. Делать классы свойств и наследовать их контролами? Или писать методы для общих свойств в базовом классе, а некоторые, специфичные, в конкретном классе? Стратегия выглядит более подходящей. В книга также было написано, использовать композицию вместо наследования.

     

    1 час назад, Xipho сказал:

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

    Если использовать стратегию в качестве свойств, то в случае если в классе Window будет объект-стратегия Menu, и если контролы будут наследовать класс Window, то тогда они наследуют и объект Menu. Да и другие некоторые, не свойственные для них объекты.

    Даже если использовать не стратегию для свойств, а наследование их классов, то получится тоже самое. Класс Window наследует класс Menu, а класс Button, например, наследует и Window и Menu.

    Это если я правильно понял тебя про наследование. Я тоже думаю лучше увидеть код.

    Я его привожу в порядок. Вчера половина дня ушла только на переход с MethodName на methodName. На выставление скобок для всех одиночных if. На перенос первой скобки вправо в конец.

    • Печалька 1
  22. 1 час назад, Xipho сказал:

    Это немного странно, потому как четко прослеживается иерархия классов окна и контрола (опять же, по примеру винапи).

    Когда я говорил "в основном", я имел ввиду что всю работу выполняют объекты "стратегии". Они в библиотеке представляю определенные свойства, окна или WinAPI контрола. А сама иерархия вот такая

    Спойлер

    spacer.png

     

    Сами контролы практически ничего не умеют делать. Все на что они способны, это делегировать (теперь новое слово в моем словарном запасе) задачу объектам стратегии из своей оконной процедуры, когда это нужно.

    А так, сам пользователь обращается к объекту "стратегии" окна/контрола, и через него влияет на окно/контрол.

    Спойлер
    WND_MAIN = new WGW::Window(TRUE, 0, 0, L"MainWindowName", 20, 20, 1000, 900);
    WND_MAIN->onClose = OnCloseFunction;
    WND_MAIN->color = RGB(188, 190, 220);
    WND_MAIN->onPaint = OnPaintFunctionName;
    WND_MAIN->onEraseBkg = OnErase;
    WND_MAIN->onClick = FirstRandomFunctionName;
    WND_MAIN->icon16 = _IDI_PACK;
    WND_MAIN->titleBar.enabled = TRUE;
    
    
    BTN2 = new WGW::ButtonStandart(TRUE, 0, 0, L"Btn2", 220, 400, 200, 200, WND_MAIN);
    BTN2->toolTip.enabled = TRUE;
    BTN2->toolTip.titleText.append(L"ToolTipTitle ", RGB(10, 10, 200));

     

     

    1 час назад, Xipho сказал:

    А у тебя есть понимание, почему стоит программировать именно так? 

    Да, в этом плане все хорошо, все понятно из книги. Как я понимаю, основная цель паттернов это перспективы на будущее. Патерны это приемы избавляющего разработчика от переделывания проекта при его расширении.

    В этом плане "стратегия" мне очень нравится. Ни от кого не зависит, делает что хочет. Окну/контролам не важно что она там у себя делает. Важно только иметь в своем классе ее объект и некоторые разрешения через "friend" по необходимости.

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.