ох уж эта SetClipboardData!

Тема в разделе "WASM.WIN32", создана пользователем rain, 16 янв 2007.

  1. rain

    rain New Member

    Публикаций:
    0
    Регистрация:
    22 апр 2006
    Сообщения:
    976
    С самого начала эта функция привлекала к себе внимание, обычный буфер(я тестировал только с CF_TEXT) ей не годится подавай только тот который получен с помощью LocalAlloc, тут собсно вопрос номер один, почему именно LocalAlloc? почему ей не подходит например VirtualAlloc или вообще просто буфер под строку нехай его копирует куда хочет и радуется жизни?
    Затем я заметил, что (в моём приложении по нажатию на пимпу копировался в клипбоард таким макаром кой-какой текст), часто и быстро давая на эту пимпу прога либо тихо умирала либо писала вайолэшн где-то внутрях этой самой злополучной функции.
    Возник интерес к этой функции, вот например такой код:
    Код (Text):
    1. .386
    2. .model flat,stdcall
    3.  
    4. include windows.inc
    5.  
    6. include kernel32.inc
    7. include user32.inc
    8.  
    9. includelib kernel32.lib
    10. includelib user32.lib
    11.  
    12.  
    13. .data
    14.     buf db 'asdfasdfasdfasdf',0
    15.     sz equ $-buf
    16. .code
    17. start:
    18.  
    19.     mov edi,6
    20.    
    21.     lea esi,buf
    22.     .repeat
    23.         invoke LocalAlloc,LMEM_FIXED,sz
    24.         mov ebx,eax
    25.         invoke RtlMoveMemory,ebx,esi,sz
    26.         invoke OpenClipboard,0
    27.         invoke SetClipboardData,CF_TEXT,esi
    28.         invoke CloseClipboard
    29.         invoke LocalFree,ebx
    30.         dec edi
    31.     .until edi==0
    32.     invoke MessageBox,0,0,0,MB_OK
    33.     invoke ExitProcess,0
    34.    
    35. end start
    первый цикл - выполняется нормально, воторой - вызываются всякие исключения, короче.. у меня если в edi в начале записать 5 мэссаджбокс появляется, если 6 - то уже нет ))) хотелось бы прояснить обстановку, может я чё-т нитак делаю, мож ещё что..
    интересно у вас до 6-го цикла дойдёт? %)
    пасиба за вниманиЁ
     
  2. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    rain
    Зачем LocalAlloc/LocalFree, если ты не используешь результат работы первого?
    С текстом надо работать примерно так:
    Код (Text):
    1. HGLOBAL  h = NULL;
    2. DWORD   *p = NULL;
    3. char     s[]  = "проверка";
    4.  
    5. if (0 != OpenClipboard(NULL))
    6. {
    7.   EmptyClipboard();
    8.  
    9.   if (NULL != (h = GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, sizeof(DWORD))))
    10.   {
    11.     if (NULL != (p = (DWORD *) GlobalLock(h)))
    12.     {
    13.       *p = 0x0419; /* это Rus, можно получить локаль через GetSystemDefaultLCID */
    14.       GlobalUnlock(h);
    15.       if (NULL != SetClipboardData(CF_LOCALE, h))
    16.       {
    17.         DWORD l = lstrlen(s);
    18.         if (NULL != (h = GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, l + 1)))
    19.         {
    20.           if (NULL != (p = (DWORD *) GlobalLock(h)))
    21.           {
    22.             lstrcpy((PSTR)p, s);
    23.             GlobalUnlock(h);
    24.             if (NULL != SetClipboardData(CF_TEXT, h))
    25.             {
    26.               /* текст с локалью в буфере обмена */
    27.             }
    28.           }
    29.         }
    30.       }
    31.     }
    32.   }
    33.   CloseClipboard();
    34. }
     
  3. rain

    rain New Member

    Публикаций:
    0
    Регистрация:
    22 апр 2006
    Сообщения:
    976
    простите, протупил, "offset buf" = esi, и всё ок, тема исчерпана
    но всё равно по большому счёту, ведь если функции скормить неправильные данные, и вызвать её хоть 100 раз подряд, прога не должна падать
     
  4. q_q

    q_q New Member

    Публикаций:
    0
    Регистрация:
    5 окт 2003
    Сообщения:
    1.706
    rain
    если функции скормить неправильные данные ... прога не должна падать
    Зависит от реализации функции, например, качество проверки входных параметров. + Программа может падать не внутри ОС, а в твоем коде.
     
  5. SammIk

    SammIk Member

    Публикаций:
    0
    Регистрация:
    11 янв 2004
    Сообщения:
    90
    Адрес:
    Russia
    Используй регистры общего назначения. edx, ebx, eax ,ecx.
    Если юзаешь esi и иже с ним, перед вызовом сис-фий восстанавливай его. А перед юзаньем сохраняй. Думаю это поможет
     
  6. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    SammIk
    Глупости.
     
  7. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    Clipboard работает только с GlobalAlloc (... GMEM_DDESHARE...).

    EBX, ESI, EDI надо сохранять/восстанавливать только в теле CALLBACK функций. Перед вызовом API не надо - наоборот, можно неплохо оптимизировать код, держа в этих регистрах нечто требующееся между вызовами API.
     
  8. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    rain
    А ты попробуй с отладчиком, может какую ошибочку и схлопочешь на LocalFree или еще где ;)
    Согласно мсдн clipboard работает с GlobalAlloc == LocalAlloc с флагом GMEM_MOVEABLE, причем удалять самому выделенный блок нельзя - его удаляет ОС. Но эксперименты вроде как показывают (если я ничего не путаю), что GMEM_MOVEABLE это все таки пережиток и SetClipboardData вполне съедает и GMEM_FIXED и HeapAlloc - только удалять блок самому не нужно
     
  9. rain

    rain New Member

    Публикаций:
    0
    Регистрация:
    22 апр 2006
    Сообщения:
    976
    leo а почему не работает с VirtualAlloc? в чём разница?
     
  10. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    А ты попробуй для блока памяти, выделенного VirtualAlloc'ом, вызвать функции кучи, например GlobalSize или GlobalFree и посмотри, что из этого выйдет - без отладки может и ничего не будет (просто отказ функции), а под Олей получишь AccessViolation в кернеле
    А суть простая - при работе с клипбордом винда копирует выделенный блок к себе и затем удаляет (только не сразу, а при следующем вызове OpenClipboard) и делает она это через функции GlobalXXX, которые могут обращаться к заголовку блока, а также к предыдущему или последующему блоку кучи (слияние блоков при освобождении). Поэтому если ты вместо валидного блока кучи засунешь в клипборд виртуальный или статический блок памяти, то ес-но получишь ошибку
     
  11. gevara

    gevara максим

    Публикаций:
    0
    Регистрация:
    10 ноя 2006
    Сообщения:
    112
    Адрес:
    г. Пермь
    очень похожая ситуация. довольно таки давно встретился мне баг, из-за которого я убил целый день. смысл в том, что если установить флаг направления, то АПИ начинают глючть. что, разработчикам так сложно было в начале каждой функции поставить один байт 0xFC?
     
  12. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    gevara
    А зачем им перегружать и без того тормозные API лишними командами?
    Мануалы кури однако ;) Эти нюансы документированы.
     
  13. rain

    rain New Member

    Публикаций:
    0
    Регистрация:
    22 апр 2006
    Сообщения:
    976
    leo пасиб
    в этом то и суть проблемы, сорри что не проявляю должную активность.. чем принципиально отличается статический блок памяти от, от виртуальной и кучи ? я имею в виду принципально как windows их отличает, а не для чего они предназначенны. Чем вот например отличается VirtualAlloc с параметром MEM_COMMIT, от GlobalAlloc c параметром GMEM_FIXED, ведь что то что то возвращает указатель?
     
  14. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    rain
    Ну извини, я думал намека на структурированность кучи будет достаточно ;)
    "... делает она это через функции GlobalXXX, которые могут обращаться к заголовку блока, а также к предыдущему или последующему блоку кучи (слияние блоков при освобождении). Поэтому если ты вместо валидного блока кучи засунешь в клипборд виртуальный или статический блок памяти, то ес-но получишь ошибку"
    Придется разжевать поподробнее. У виртуальных блоков памяти и статических массивов никаких предопределенных виндой заголовков нет - у статических ес-но вообще нет, а дескрипторы виртуальных блоков хранятся отдельно от самих блоков. А куча, несмотря на "мусорное" название, четко структурирована. Каждый блок кучи имеет 8-байтовый заголовок, в котором указана длина данного и предшествующего ему блока, флаги, идентификатор раздела и т.п. Свободные блоки заносятся в соотв.списки для последующего быстрого поиска блока нужного размера, перед освобождением делается проверка возможности слияния удаляемого блока с соседними и т.д. и т.п. Ес-но чтобы не наломать дров при всех этих хитрых операциях нужна хоть какая-то "защита от дурака", поэтому при любом обращении к функциям кучи проверяется валидность заголовка блока. Поэтому если ты вместо валидного блока попытаешься передать в функцию кучи указатель на виртуальный блок, то скорее всего возникнет Access Violation при попытке чтения заголовка блока по смещению -8 (если пред.страница не выделена), внутренний SEH его выловит и функция просто вернет ошибку. Для статического массива скорее всего несуществующий мусорный "заголовок" блока не пройдет проверку на валидность или "заголовки" пред. или послед.блоков и функция также вернет ошибку.