С самого начала эта функция привлекала к себе внимание, обычный буфер(я тестировал только с CF_TEXT) ей не годится подавай только тот который получен с помощью LocalAlloc, тут собсно вопрос номер один, почему именно LocalAlloc? почему ей не подходит например VirtualAlloc или вообще просто буфер под строку нехай его копирует куда хочет и радуется жизни? Затем я заметил, что (в моём приложении по нажатию на пимпу копировался в клипбоард таким макаром кой-какой текст), часто и быстро давая на эту пимпу прога либо тихо умирала либо писала вайолэшн где-то внутрях этой самой злополучной функции. Возник интерес к этой функции, вот например такой код: Код (Text): .386 .model flat,stdcall include windows.inc include kernel32.inc include user32.inc includelib kernel32.lib includelib user32.lib .data buf db 'asdfasdfasdfasdf',0 sz equ $-buf .code start: mov edi,6 lea esi,buf .repeat invoke LocalAlloc,LMEM_FIXED,sz mov ebx,eax invoke RtlMoveMemory,ebx,esi,sz invoke OpenClipboard,0 invoke SetClipboardData,CF_TEXT,esi invoke CloseClipboard invoke LocalFree,ebx dec edi .until edi==0 invoke MessageBox,0,0,0,MB_OK invoke ExitProcess,0 end start первый цикл - выполняется нормально, воторой - вызываются всякие исключения, короче.. у меня если в edi в начале записать 5 мэссаджбокс появляется, если 6 - то уже нет ))) хотелось бы прояснить обстановку, может я чё-т нитак делаю, мож ещё что.. интересно у вас до 6-го цикла дойдёт? %) пасиба за вниманиЁ
rain Зачем LocalAlloc/LocalFree, если ты не используешь результат работы первого? С текстом надо работать примерно так: Код (Text): HGLOBAL h = NULL; DWORD *p = NULL; char s[] = "проверка"; if (0 != OpenClipboard(NULL)) { EmptyClipboard(); if (NULL != (h = GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, sizeof(DWORD)))) { if (NULL != (p = (DWORD *) GlobalLock(h))) { *p = 0x0419; /* это Rus, можно получить локаль через GetSystemDefaultLCID */ GlobalUnlock(h); if (NULL != SetClipboardData(CF_LOCALE, h)) { DWORD l = lstrlen(s); if (NULL != (h = GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, l + 1))) { if (NULL != (p = (DWORD *) GlobalLock(h))) { lstrcpy((PSTR)p, s); GlobalUnlock(h); if (NULL != SetClipboardData(CF_TEXT, h)) { /* текст с локалью в буфере обмена */ } } } } } } CloseClipboard(); }
простите, протупил, "offset buf" = esi, и всё ок, тема исчерпана но всё равно по большому счёту, ведь если функции скормить неправильные данные, и вызвать её хоть 100 раз подряд, прога не должна падать
rain если функции скормить неправильные данные ... прога не должна падать Зависит от реализации функции, например, качество проверки входных параметров. + Программа может падать не внутри ОС, а в твоем коде.
Используй регистры общего назначения. edx, ebx, eax ,ecx. Если юзаешь esi и иже с ним, перед вызовом сис-фий восстанавливай его. А перед юзаньем сохраняй. Думаю это поможет
Clipboard работает только с GlobalAlloc (... GMEM_DDESHARE...). EBX, ESI, EDI надо сохранять/восстанавливать только в теле CALLBACK функций. Перед вызовом API не надо - наоборот, можно неплохо оптимизировать код, держа в этих регистрах нечто требующееся между вызовами API.
rain А ты попробуй с отладчиком, может какую ошибочку и схлопочешь на LocalFree или еще где Согласно мсдн clipboard работает с GlobalAlloc == LocalAlloc с флагом GMEM_MOVEABLE, причем удалять самому выделенный блок нельзя - его удаляет ОС. Но эксперименты вроде как показывают (если я ничего не путаю), что GMEM_MOVEABLE это все таки пережиток и SetClipboardData вполне съедает и GMEM_FIXED и HeapAlloc - только удалять блок самому не нужно
А ты попробуй для блока памяти, выделенного VirtualAlloc'ом, вызвать функции кучи, например GlobalSize или GlobalFree и посмотри, что из этого выйдет - без отладки может и ничего не будет (просто отказ функции), а под Олей получишь AccessViolation в кернеле А суть простая - при работе с клипбордом винда копирует выделенный блок к себе и затем удаляет (только не сразу, а при следующем вызове OpenClipboard) и делает она это через функции GlobalXXX, которые могут обращаться к заголовку блока, а также к предыдущему или последующему блоку кучи (слияние блоков при освобождении). Поэтому если ты вместо валидного блока кучи засунешь в клипборд виртуальный или статический блок памяти, то ес-но получишь ошибку
очень похожая ситуация. довольно таки давно встретился мне баг, из-за которого я убил целый день. смысл в том, что если установить флаг направления, то АПИ начинают глючть. что, разработчикам так сложно было в начале каждой функции поставить один байт 0xFC?
gevara А зачем им перегружать и без того тормозные API лишними командами? Мануалы кури однако Эти нюансы документированы.
leo пасиб в этом то и суть проблемы, сорри что не проявляю должную активность.. чем принципиально отличается статический блок памяти от, от виртуальной и кучи ? я имею в виду принципально как windows их отличает, а не для чего они предназначенны. Чем вот например отличается VirtualAlloc с параметром MEM_COMMIT, от GlobalAlloc c параметром GMEM_FIXED, ведь что то что то возвращает указатель?
rain Ну извини, я думал намека на структурированность кучи будет достаточно "... делает она это через функции GlobalXXX, которые могут обращаться к заголовку блока, а также к предыдущему или последующему блоку кучи (слияние блоков при освобождении). Поэтому если ты вместо валидного блока кучи засунешь в клипборд виртуальный или статический блок памяти, то ес-но получишь ошибку" Придется разжевать поподробнее. У виртуальных блоков памяти и статических массивов никаких предопределенных виндой заголовков нет - у статических ес-но вообще нет, а дескрипторы виртуальных блоков хранятся отдельно от самих блоков. А куча, несмотря на "мусорное" название, четко структурирована. Каждый блок кучи имеет 8-байтовый заголовок, в котором указана длина данного и предшествующего ему блока, флаги, идентификатор раздела и т.п. Свободные блоки заносятся в соотв.списки для последующего быстрого поиска блока нужного размера, перед освобождением делается проверка возможности слияния удаляемого блока с соседними и т.д. и т.п. Ес-но чтобы не наломать дров при всех этих хитрых операциях нужна хоть какая-то "защита от дурака", поэтому при любом обращении к функциям кучи проверяется валидность заголовка блока. Поэтому если ты вместо валидного блока попытаешься передать в функцию кучи указатель на виртуальный блок, то скорее всего возникнет Access Violation при попытке чтения заголовка блока по смещению -8 (если пред.страница не выделена), внутренний SEH его выловит и функция просто вернет ошибку. Для статического массива скорее всего несуществующий мусорный "заголовок" блока не пройдет проверку на валидность или "заголовки" пред. или послед.блоков и функция также вернет ошибку.