Экспертная система Delphi.int.ru

Сообщество программистов
Общение, помощь, обмен опытом

Логин:
Пароль:
Регистрация | Забыли пароль?

Delphi.int.ru Expert

Другие разделы портала

Переход к вопросу:

#   

Статистика за сегодня:  


Лучшие эксперты

Подробнее »



Вопрос # 2 485

/ вопрос открыт /

Доброго времени суток, уважаемые эксперты!
Помогите, пожалуйста, с "переводом" данной функции под Delphi 2009. Пробовал по-разному менять преобразование строковых типов, пересчёт длины через SizeOf - так и не получилось, в буфере обмена кракозябры всё время.
P.S. Использование модуля Clipbrd не предлагать - у него есть проблемы, потому и намеренно отказался от него. Аналогичную функцию для получения текста из буфера адаптировать под D2009 удалось.

Приложение:
  1. function SetClipboardText(Wnd: HWND; Value: string): Boolean;
  2. var
  3. hData: HGlobal;
  4. pData: pointer;
  5. Len: integer;
  6. begin
  7. Result := True;
  8. if OpenClipboard(Wnd) then
  9. begin
  10. try
  11. Len := Length(Value) + 1;
  12. hData := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE, Len);
  13. try
  14. pData := GlobalLock(hData);
  15. try
  16. Move(PChar(Value)^, pData^, Len);
  17. EmptyClipboard;
  18. SetClipboardData(CF_Text, hData);
  19. finally
  20. GlobalUnlock(hData);
  21. end;
  22. except
  23. GlobalFree(hData);
  24. raise
  25. end;
  26. finally
  27. CloseClipboard;
  28. end;
  29. end
  30. else
  31. Result := False;
  32. end;


Sunshine Вопрос ожидает решения (принимаются ответы, доступен мини-форум)

Вопрос задал: Sunshine (статус: Посетитель)
Вопрос отправлен: 6 марта 2009, 09:48
Состояние вопроса: открыт, ответов: 0.


Мини-форум вопроса

Всего сообщений: 21; последнее сообщение — 6 марта 2009, 16:20; участников в обсуждении: 2.

Страницы: [1] [2] [Следующая »]

Вадим К

Вадим К (статус: Академик), 6 марта 2009, 11:37 [#1]:

Нравиться мне это "функция имеет ошибку, не предлагать". Я склонен полагать, что то, как Вы используете эту функцию имеет ошибку:)
Дальше, сколько писать, что sizeof для строки в случае юникода надо очень аккуратно применять. ведь теперь один символ - это два байта. Но не только это. наиболее правильно и совместимо будет запись вида
length(s)*sizeof(char) - это будет выдавать длину строки в байтах для любой делфи.
Галочка "подтверждения прочтения" - вселенское зло.
Вадим К

Вадим К (статус: Академик), 6 марта 2009, 11:37 [#2]:

неправильно добавляет только русский текст, или с английским также есть проблемы?
Галочка "подтверждения прочтения" - вселенское зло.
Sunshine

Sunshine (статус: Посетитель), 6 марта 2009, 11:42 [#3]:

Вадим, вопрос не в проблемах модуля Clipbrd, а в том, как заставить эту функцию корректно рботать на D2009. Если интересно - при активной работе с буфером постоянно выскакивает "Cannot open clipboard". Практически через раз. В инете подобная проблема известна, но решения кроме try..expert никто не знает. А это решение не подходит, т.к. работа программа кардинально из-за этого нарушается. Та функция получения текста, что я нашёл, работает безотказно. Так что проблема именно в модуле.
<quote>Дальше, сколько писать, что sizeof для строки в случае юникода надо очень аккуратно применять. ведь теперь один символ - это два байта. Но не только это. наиболее правильно и совместимо будет запись вида
length(s)*sizeof(char) - это будет выдавать длину строки в байтах для любой делфи. </quote>
Я вообще-то в вопросе написал, что экспериментировал с разными вариантами. И Len*SizeOf(Value[1]) тоже пробовал, поверьте.
<quote>неправильно добавляет только русский текст, или с английским также есть проблемы? </quote>
Проблемы с любыми буквами, и даже с цифрами.
Вадим К

Вадим К (статус: Академик), 6 марта 2009, 11:54 [#4]:

Если проблемы с любыми буквами - значит неверно копируются данные в буфер - то есть, указатель левый, вот и попадает мусор.

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

Поехали дальше. Как минимум одну серьезную ошибку в коде я вижу. Когда выделяется буфер под строку, там выделяется на один байт больше чем длина. вроде норм, но! длина строки в два раза больше на самом деле... и так как она юникодная, надо как минимум два байта в конец добавлять - то есть Вы выделяете память только на пол строки на самом деле, но и копируете туда пол строки. А так как она юникодная, неведомо какой будет конец у неё.
И ещё, последний символ (для юникода - два последних байта) надо в обязательном порядке ЗАНУЛИТЬ! Иначе бедная винда не будет знать, где конец строки....
Начните с этого, а там увидим.
Галочка "подтверждения прочтения" - вселенское зло.
Sunshine

Sunshine (статус: Посетитель), 6 марта 2009, 12:07 [#5]:

function SetClipboardText(Wnd: HWND; Value: string): Boolean;
var
hData: HGlobal;
pData: pointer;
Len: integer;
begin
Result := True;
if OpenClipboard(Wnd) then
begin
try
Len := Length(Value) + 1;
//showmessage(inttostr(Len*SizeOf(Char)));
hData := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE, Len*SizeOf(Char));
try
pData := GlobalLock(hData);
try
Move(PChar(Value)^, pData^, Len*SizeOf(Char));
EmptyClipboard;
SetClipboardData(CF_Text, hData);
finally
GlobalUnlock(hData);
end;
except
GlobalFree(hData);
raise
end;
finally
CloseClipboard;
end;
end
else
Result := False;
end;

SetClipboardText(Handle,'Hello, world!'+#0);

Кракозябров нет, но в буфер помещается только первый символ строки (в данном случае "H").
Вадим К

Вадим К (статус: Академик), 6 марта 2009, 12:24 [#6]:

Во первых, зануления я не увидел. Я увидел только жалкую попытку сделать обходной маневр. Если так и в коде пишете, то я не удивляюсь, что то работает, то нет.
Во вторых, Len*SizeOf(Char) можно выделить в отдельную переменную.
В третих, возможно копирование мусора. Это связано с первым пунктом. Что бы не мучиться с занулениме последнего символа, сделаейте просто обнуление всего массива. GlobalAlloc вроде имеет флаг, когда данные будут занулены автоматом, поищите.
и на последок, а кто сказал, что PChar(s)^ даст указатель на строку? Никто и не обещал.
должно работать что то в стиле (@s[1])^
Галочка "подтверждения прочтения" - вселенское зло.
Sunshine

Sunshine (статус: Посетитель), 6 марта 2009, 12:34 [#7]:

1 - с этой целью единица к длине и прибавляется, я полагаю, чтобы в конце был нулевой символ.
2 - сейчас это не принципиально. Когда будет работать, тогда можно будет сделать оптимизацию.
3 - да, есть такой флаг.
4 - никто не обещал, но обычно так и бывает.
Тем не менее:

function SetClipboardText(Wnd: HWND; Value: string): Boolean;
var
hData: HGlobal;
pData: pointer;
Len,M: integer;
begin
Result := True;
if OpenClipboard(Wnd) then
begin
try
Len := Length(Value) + 1;
M:=Len*SizeOf(Char);
hData := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE or GMEM_ZEROINIT, M);
try
pData := GlobalLock(hData);
try
Move((@Value[1])^, pData^, M);
EmptyClipboard;
SetClipboardData(CF_Text, hData);
finally
GlobalUnlock(hData);
end;
except
GlobalFree(hData);
raise
end;
finally
CloseClipboard;
end;
end
else
Result := False;
end;

Снова копируется только первый символ. Куда деваются остальные - неясно.
Вадим К

Вадим К (статус: Академик), 6 марта 2009, 12:50 [#8]:

как минимум я бы написал так
Len := Length(Value);
M:=Len*SizeOf(Char);
hData := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE or GMEM_ZEROINIT, M + 2);

Именно так. И не следует к строке добавлять #0.
А теперь об отладке. Установите точку останова на EmptyClipboard;
и во время комирования посмотрите, что в pData^
Если всё нормально, то должны увидеть свою строку, только через один будут идти нули (если латинские символы).
Попробуйте добавить в буфер строку с только русскими символами.
Кажеться, я знаю, в чем проблема:)
Галочка "подтверждения прочтения" - вселенское зло.
Sunshine

Sunshine (статус: Посетитель), 6 марта 2009, 13:19 [#9]:

Это странно, но значения у pData^ нет. Сначала его нельзя было посмотреть по причине включенной оптимизации, а после её отключения pData^ = (no value). Сама pData некоторый адрес всё же содержит.
Вадим К

Вадим К (статус: Академик), 6 марта 2009, 13:31 [#10]:

после выключения оптимизации надо сделать полную перекомпиляцию (build all).
Содержимое не отображает, так как не знает, как.
Если добавить эту переменную в Watch list, то там можно выставить Memory Dump - вот там и будет видно
Галочка "подтверждения прочтения" - вселенское зло.
Sunshine

Sunshine (статус: Посетитель), 6 марта 2009, 13:45 [#11]:

Показывает просто " 'Hello' " без нулей в середине.
Вадим К

Вадим К (статус: Академик), 6 марта 2009, 14:01 [#12]:

Значит либо строка не юникодная, либо смотрите не дамп памяти.
Галочка "подтверждения прочтения" - вселенское зло.
Sunshine

Sunshine (статус: Посетитель), 6 марта 2009, 14:07 [#13]:

AddWatch:
Expression - pData^
[X] Memory Dump

При выполнении:
Watch Name - pData^
Value - (no value)

pData = $162768
Вадим К

Вадим К (статус: Академик), 6 марта 2009, 14:29 [#14]:

значение адреса указателя мне ничем не поможет.
строка с русских символов нормально вставляется?
Галочка "подтверждения прочтения" - вселенское зло.
Sunshine

Sunshine (статус: Посетитель), 6 марта 2009, 14:39 [#15]:

Вставляется куда?
P.S. Я так понимаю, у вас Delphi вообще нет под рукой?
Вадим К

Вадим К (статус: Академик), 6 марта 2009, 14:45 [#16]:

У меня есть 7 делфи прям сейчас.
2009 только дома.
Я имею ввиду, если использовать вышеприверённую функцию для вставки в буффер обмена полностью русскоязычной строки, то как результат - один символ/иероглифы/вообще ничего/другой вариант.
Галочка "подтверждения прочтения" - вселенское зло.
Sunshine

Sunshine (статус: Посетитель), 6 марта 2009, 14:49 [#17]:

"привет" переходит в "?@825B" и после каждого из этих символов "квадратик".
Остальное (латинские буквы, цифры) - попадает только первый символ.
Вадим К

Вадим К (статус: Академик), 6 марта 2009, 14:58 [#18]:

Тогда мое предположение верно. Буфер по ходу "неюникодный". В случае латиской строки он натыкается на первый 0, который будет гарантированое вторым байтом (подумайте почему) и считает это концом строки. Вот поэтому и один символ.
В случае русскоязычной строки, ноль есть только в конце. Но как отобразит промежуточные символы - неизвестно.
Попробуем по простому. Надо задать правильный флаг, что бы бедная виндовс поняла, что он неё хотят. Нужен такой флаг
CF_UNICODETEXT вместо CF_Text,
А может и такой CF_UNICODETEXT or CF_Text.
Галочка "подтверждения прочтения" - вселенское зло.
Sunshine

Sunshine (статус: Посетитель), 6 марта 2009, 16:08 [#19]:

Ура! CF_UNICODETEXT решило все проблемы.
Вадим, огромное спасибо! Всё работает!
Вадим К

Вадим К (статус: Академик), 6 марта 2009, 16:18 [#20]:

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

Страницы: [1] [2] [Следующая »]

Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.

Версия движка: 2.6+ (26.01.2011)
Текущее время: 22 февраля 2025, 11:29
Выполнено за 0.02 сек.