Нет, EnumWindows или EnumWindowsEx на каждое окно возвращает структуру, там поле hwndParent и hwnd, первое - это хэндл главного окна, и проверяешь тупо со своим: mov eax, dword[my_main_hwnd] cmp eax, dword[window.hwndParent] jne .not_my_window
А, так там еще прикол в том, что я эти окошки генерирую без родителя, чтобы они не перекрывали основное окно
GetWindowThreadProcessId, ну и сравниваешь со своим GetCurrentProcessId. Но это какой-то костыль. Почему бы не учитывать окна в связном списке, например? Вот захочешь ты открыть порт, а он в одном из окон уже открыт, и ты такой прошёлся по списку, нашёл и ShowWindow ему, а не ругаешься, что не удалось порт открыть.
Это не костыль. Вот я бы сделал ReadProcessMemory, и в памяти какую-то константу уникальную хранить, и так определять мой это процесс, или нет. Ну, и проверку на свой же дескриптор, чтоб самого себя не прикрыть.
Список это выделение памяти, переменная, функции для работы с добавлением и удалением из этой памяти, и сортировка (либо можно при добавлении просто искать дыру, если нет - добавлять в конец, при удалении выставить 0, а при добавлении туда уже добавится новый дескриптор, но это тоже функция, это тоже цикл, и тоже лишние операции).
Интересны — да, хороши — нет. Читать память ненадёжно как минимум, потому что процессов может быть больше одного. И ещё потому что читать памтяь чужих процессов без причины — зло, может быть какое-нибудь весёлое поведение с античитами, антивирусами, отладчиками, ещё с чем-нибудь, но мы об этом не узнаем, пока не случится. А что до выделения памяти, то тут опять происходит ассемблер, потому что надо же код писать, а код писать больно, поэтому мы изобретаем костыли. И нет, для связного списка не нужно искать дыры, он не массив. И нет, на фоне всего того, что делает винда при создании окна, «лишний» цикл стоит примерно ноль.
В яву это три строчки. А если мы имеем в виду хотя бы C++, то одна.
не забываем про free :D
😳 Одна строчка на функцию для поиска своих окон?
ну для плюсов делит)
А зачем нам искать свои окна, если мы в WM_CREATE можем положить в хэш номер порта и hWnd?
Оно само.
ну мб ядро и офистит всё, но желательно чистить
Не понял. Как это решит задачу?
@MrMiscipitlick хочет закрывать свои окна. Он не знает, какие окна он открыл, поэтому он хочет обходить все топлевел окна в системе и проверять, не ему ли они принадлежат. Если бы он при открытии запоминал hWnd открытых окон, при завершении процесса он мог бы просто взять их из списка/словаря.
Хэш = словарь.
Впервые слышу, чтобы его так называли. Но, в любом случае, не подходит сюда никак твой аргумент про "на ассемблере писать долго, поэтому придумываем костыли". У меня тут столько много вопросов вытекает вроде: 1. А что плохого, что язык учит думать над алгоритмом и сокращать его? 2. Но написать словарь\список на ассемблере это несложно вообще, и не слишком долго, быстрее чем сама программа @MrMiscipitlick
Сокращать — это делать ReadProcessMemory * количество топ-левел окон? Отличные оптимизации, да. А написать список, конечно же, несложно, но лень.
Кстати, что-то мне кажется, что NtReadVirtualProcessMemory намного меньше кода имеет, чем тот же EnumWindowsEx :)
А это тебя от Enum* не избавляет никак. Это вдобавок. И процесс ещё нужно открыть тоже. А про длину конкретно Read*Memory не знаю, но там же проверки, что можно читать, что можно писать, и вроде там несколько способов копирования ещё.
Нужно замерить, что быстрее - Enum*+ReadProcessMemory, или выделение памяти+функции добавления и удаления элементов из списка+функция проверки находится ли элемент в списке+очистка памяти
Проход по списку дешевле даже одного сисколла. Итого у нас будет PostMessage * количество наших окон сисколлов + освобождение памяти (в юзермоде) vs. около 10 контекстсвитчей * количество топлевел окон. Про выделение памяти на фоне открытия окна я уже писал, это ни о чём.
Если я буду оценивать по нагрузке на процессор в ProcessHacker - это честно?
запусти на дохленьком селероне или атоме, с вин10 , сразу видно как всё работает, даже видно будет как окна рисуются
Без понятия. Попробуй. Но ещё раз. Тебе в любом случае нужно PostMessage на одно окно. Только в варианте с Enum* у тебя вдобавок есть этот самый Enum*, колбеки, сравнения (по pid, памяти или как ты там сделаешь). И PostMessage никуда не девается. Что тут сравнивать-то?
Зачем PostMessage? Я хочу через диалоговые окна, один процесс в таймере будет запускать и закрывать диалоговое окно (перебирать процессы и искать дочернее), а другой - будет сохранять выделять память, добавлять в список, затем смотреть есть ли окно в списке, и если есть - убрать из списка, затем очистить память. И проверить, кто сколько будет нагружать.
PostMessage итог всей этой затеи. Посылать WM_CLOSE. Ну а так, да. Тебе и закрывать не нужно. Ты же меряешь, сколько времени нужно на поиск одного окна vs. malloc+free+проход по списку.
как микросервисная архитектура в асм софт проникла?
Не malloc, а NtVirtualAlloc, он быстрее))
Ладно, как-то уже перехотелось. Хочу что-то другое замерить.
всё равно в ядро прыгать
Нет не быстрее.
А, ну тогда давай перед этим 10 раз вычислим Фибоначчи и отрисуем танцующего медведя на GDI.
А если замерить?
на малых блоках желательно, меньше страницы
А если подумать? Сисколл один раз в n выделений vs. сисколл каждое выделение?
Один раз в N конечно быстрее будет, там проверки, и на одно действие проверки по отношению к реальному коду 50 на 50, а когда 10 раз вызываешь - то уже большую часть действий составляют одни и те же проверки, код умножается, кода больше, код медленнее.
Вот замеры сделал кто-то, только там про new[], но не суть.
любой сискол это минимум 100-120 тактов + сохранение контекста и тд Проверки это 1 такт на штуку если уж упрощать
Забавно, что ты считаешь, что проверка это тупо один cmp.
я так не считаю, но думается явно меньше 100 для самого простого malloc
И правда шустрый VirtualAlloc😁 но он вроде минимум 1кб выделяет
И что теперь? Есть две функции, оба вызывают VirtualAlloc, это уже по 100 тактов на две функции. Затем другая функция делает какую-то ещё свою дичь, из 100 вырастает 200, 300, 400... Вопрос - при чём тут вообще переход в ядро и системный вызов, если речь про нагрузку самой функции?
Не знаю, что там, но судя по описанию, это свой аллокатор, который использует апи или кучу как бэк. Т.е., это не аллокация указанной функцией каждый раз. А если каждый раз, то будет вот так: heap, 32 size, 50000 iters: -> 16 ms virtualalloc, 32 size, 50000 iters: -> 18594 ms
Вот я открыл malloc сейчас, что вижу: 1. Проверка на 0 аргумент (конечно, оригинальный VirtualAlloc же не проверяет это!! Спасибо!) 2. Вызов заглушки какой-то с set_error_mode, круто 3. Вызов ещё одной заглушки 4. Ещё одна заглушка 5. Ищет библиотеку mscoree.dll, ищет там функцию LdrpInitializeProcess, шикарно! 6. Ещё куча проверок, и только потом вызов HeapAlloc
Я тебе померял, смирись уже. Разница в сотни раз. Сисколлы медленные. 500 тактов ты платишь только за сам факт сисколла, а внутри сисколла менеджмент виртуальной памяти дико медленный, потому что делать нужно очень много всего. И проверять нужно очень много всего.
Ты померял HeapAlloc, это нечестно. Куча естественно быстрее, но она и ограничена на старте процесса, а VirtualAlloc из ОЗУ выделяет, которое больше.
А вот если померять NtHeapAlloc и malloc? Что быстрее?
😳 Кто ограничена? Она тот же самый VirtualAlloc использует, просто реже.
Я вот не пойму, при чём тут системные вызовы? А malloc что-ли не использует системный вызов? Из воздуха берёт?
Куча по определению берёт кучу памяти и нарезает блоки оттуда. В юзермоде. Быстро. В результате системные вызовы у тебя только когда куча заканчивается, и тебе нужна новая.
Поле SizeOfHeapCommit/SizeOfHeapReserve не влияют на HeapAlloc?
Влияют на то, сколько будет изначально у процесса. Например, у тебя есть какие-то классы, они вместе весят N. И ты можешь попросить при инициализации сразу выделить тебе N, чтобы эти классы её разобрали, чтобы как раз избежать лишних походов в ядро. Но это не значит, что ты не можешь попросить потом ещё сколько-то.
А я еще думал, зачем в PE указывается размер кучи, если она безразмерная, а оказывается вон оно что.
Ладно, так а если NtHeapAlloc и malloc сравнить? Что быстрее? Всё-таки мы тут именно malloc оцениваем, я забыл что он HeapAlloc использует.
Пример?
Да хоть что. Я такой функции не помню, я плохо нейтив знаю.
Ой, я ошибся, такой функции нет, там RtlAllocateHeap, на который в целом и ведёт HeapAlloc, без всяких заглушек, так что это одно и то же.
mov eax, dword[fs:0x30] mov dword[esp], 256 ; Size mov eax, dword[eax+0x18] mov dword[esp-4], HEAP_ZERO_MEMORY mov dword[esp-8], eax sub esp, 8 call dword[HeapAlloc] ; RtlAllocateHeap
Будет чуть быстрее, malloc рано или поздно туда придёт, если кастомный аллокатор не использовать.
Обсуждают сегодня