-
Возник вопрос с типом String и SendMessage. Итак, есть самописный компонент (за моим авторством), имеющий некоторое поле типа String. Я хочу сделать следующее: послав компоненту сообщение, установить ему значение этого поля. Сообщение кастомное (WM_USER + 1000). Посылаю сообщение так:
var
S: String;
begin
SendMessage(MyComp.Handle, WM_SETTEXTFIELD, WParam(@S), 0);
end;
В самом компоненте "приемник" выглядит так:
FLabelField := String(Message.WParam)
Под отладчиком видно, что поле FLabelField компонента принимает значение "Inaccessible value", и после закрытия программа зависает и "падает". Я также планировал сделать сообщение WM_GETTEXTFIELD, но теперь не совсем уверен, что это возможно. Подскажите, пожалуйста, что не так с кодом и/или что почитать на эту тему. Заранее спасибо!
-
строки контролируются компилятором, типа "компилед магик" памятью под них рулит... используй PChar, выделяй память сам при посылке, освобождай при получении.
-
> Илья_666 (04.11.18 17:29)
> Подскажите, пожалуйста, что не так с кодом и/или что почитать
RTFW: SendMessage with WM_COPYDATA
-- Regards, LVT.
-
> используй PChar, выделяй память сам при посылке, освобождай > при получении
Будет ли конструкция вида S := 'qwerty' считаться выделением памяти? Или надо именно через New делать? Освобождать обязательно? Я думал, компилятор сам это делает.
> RTFW: SendMessage with WM_COPYDATA
Спасибо, но WM_COPYDATA больше подходит для обмена между приложениями, а мне надо в рамках одного приложения.
-
@S - єто адрес строки S. Соответствено, должно бьіть FLabelField := String(Pointer(Message.WParam)^).
-
> @S - єто адрес строки S. Соответствено, должно бьіть FLabelField > := String(Pointer(Message.WParam)^).
Спасибо! Пример также рабочий. В общем, с первой проблемой все решено. Осталось решить вопрос возврата значения строки в сообщении. Подскажите еще одну вещь: если необходимо модифицировать значение строки по ссылке, то ее надо передавать как
SendMessage(MyComp.Handle, WM_GETTEXTFIELD, WParam(@S), 0);
или
SendMessage(MyComp.Handle, WM_GETTEXTFIELD, WParam(PChar(S)), 0);
PChar - это указатель на строку (если я правильно прочитал статьи). Но система запрещает модификацию данных по этому указателю, верно? Или я ошибаюсь? Еще один момент. Если использовать вариант с WParam(@S), то у меня получилось модифицировать строку следующим кодом (спасибо SeeMee):
String(Pointer(Message.WParam)^) := FLabelField;
Насколько это корректный метод? Подразумевается, что пременная S - это стандартный тип String.
-
Илья_666 (04.11.18 18:17) [3]
> Будет ли конструкция вида S := 'qwerty' считаться выделением памяти? > Или надо именно через New делать? > Освобождать обязательно? Я думал, компилятор сам это делает.
Проблема не столько в выделении, сколько в освобождении. Для начала замечу, что ReadOnly-строки-константы, напр. s:='qwerty', можно передавать без проблем, т.к. память под них отводится при компиляции и в процессе работы не меняется. Но для вычисляемых строк, напр. s:=IntToStr(i), это не так.
Чтобы упростить алгоритм, имеет смысл после присваивания строке нужного значения перед SendMessage сделать строку уникальной при помощи вызова Unique(s), чтобы ее счетчик ссылок стал равен 1. Передавать стоку надо как pointer(s). Затем сразу после SendMessage необходимо выполнить оператор pointer(s):=nil, чтобы предотвратить автоматическую финализацию переданной строки после передачи.
Прием строки нельзя выполнять присваиванием, т.к. это изменит ее счетчик ссылок. Необходимо последовательно выполнить операторы s:=''; pointer(s):=ПереданноеЗначение; чтобы обеспечить последующую автоматическую финализацию без утечек памяти в месте приема.
Материал предоставлен в ознакомительных целях, корректная работа не гарантируется ) При реализация легко допустить ошибку. Плюс такой код не прозрачен. Потому подобных приемов следует избегать.
-
> Материал предоставлен в ознакомительных целях, корректная > работа не гарантируется )
А я то сразу не понял для чего вы, Александр, приводите такие шаманские пляски в ответе :) По мне так при работе с WinAPI надёжнее всего изучить работу с типом PChar. И проблем не должно быть.
-
> Германн © (06.11.18 01:27) [7]
В умелых руках оно быстро, наглядно, надежно и высокоуровнево )
А вот с пчаром, там точно шаманские пляски )
-
Сейчас перечитал [6], обнаружил 2 неточности:
1. Упомянутая функция называется, конечно, UniqueString.
2. И нужна она не для того, чтобы упростить алгоритм, а чтобы упростить понимание алгоритма, т.е. чтобы его поведение было единообразным для всех непустых строк. Там поначалу было задумано длинное объяснение поведения счетчика ссылок, но потом решил закруглиться в виду позднего времени.
Т.е. вызывать UniqueString на самом деле не требуется, все будет работать и без нее, просто счетчик ссылок в момент передачи будет иметь произвольное значение.
-
> В умелых руках оно быстро, наглядно, надежно и высокоуровнево )
- но требует жесткой логики приёмника. Если в приёмнике появится ветвление без захвата отпущенной строки - будет утечка... Лишний инкремент/декремент счётчика погоды не делают... А для изоляции var-семантики - лучше явно объявить запись со строковым полем и передавать указатель на запись... Результирующий код одинаковый, но непоняток с собаками-крышками меньше.
type PGetTextField = ^TGetTextField; TGetTextField = record str: string; end;
{private} procedure TMyComp.GetTextField_impl(var fld: TGetTextField); begin ... end;
procedure TMyComp.WMGetTextField(var msg: TMessage);{message WM_GETTEXTFIELD;} begin GetTextFiled_impl(PGetTextField(msg.WParam)^); end;
{public} procedure TMyComp.GetTextFieldExplicit(var fld: TGetTextField); begin SendMessage(Handle, WParam(@fld), 0); end;
function TMyComp.GetTextField(): string; var fld: TGetTextField; begin GetTextFieldExplicit(fld); Result:= fld.str; end;
-
> han_malign © (07.11.18 12:24) [10]
>> В умелых руках оно быстро, наглядно, надежно и высокоуровнево )
> - но требует жесткой логики приёмника. > Если в приёмнике появится ветвление без захвата отпущенной строки - будет утечка...
Жесткая логика пишется один раз, комментируется - и 100 лет счастья. И нефиг туда лезть кому попало, а полезли - бить по рукам )
> Лишний инкремент/декремент счётчика погоды не делают...
Дело тут вовсе не в лишнем инкременте-декременте, а в гарантии правильной работы.
Иначе возможен сценарий, когда сначала происходит декремент и, как следствие, финализации строки, после чего приемник улетает в космос.
-
> а в гарантии правильной работы. > > Иначе возможен сценарий,
это отдельная тема оптимистического программирования
if( PostMessage(Handle, WParam(Pointer(hackStr)), 0) )then Pointer(hackStr):= nil;
-
> han_malign © (07.11.18 13:13) [12]
Что тут имеется в виду? Нельзя ли немного развернуть мысль?
-
вообще всех этих извращений следует избегать... ведь тут локальный внутри программный компонент, команда SendMessage синхронная, т.е. все чего добиваются тут это "крутая/как winapi" замена обычного присваивания MyComp.MyStr:= S; без цели/реальной необходимости в чем то подобном (в вопросе ее нет) все это не более чем извращения.
> это отдельная тема оптимистического программирования > if( PostMessage(Handle, WParam(Pointer(hackStr)), 0) )then > Pointer(hackStr):= nil; а вот это уже приведет к глюкам, т.к. PostMessage это с постановкой в очередь, и необязательно сразу прочитает из нее, немедленное "обниление" до прочтения = AV.
-
> sniknik © (08.11.18 10:40) [14] > вообще всех этих извращений следует избегать...
согласен
>> if( PostMessage(Handle, WParam(Pointer(hackStr)), 0) )then >> Pointer(hackStr):= nil; > а вот это уже приведет к глюкам ...
откуда такая уверенность? сначала не мешало бы проверить )
-
> откуда такая уверенность? по логике, знаю как работает PostMessage - делает типа очереди сообщений, если туда вклинится что-то "служебное", да что угодно, будет разрыв между освобождением памяти и ее чтением, память могут и "перезанять" - AV, или хуже тихая ошибка с прочтением не того.
> сначала не мешало бы проверить ) а вот на проверке, тем более простой, не обремененной другим функционалом-событиями скорее всего все будет работать.
самая сложная ошибка когда все работает и только иногда... в зависимости от фаз луны... в общем я бы такого не писал.
-
> sniknik © (08.11.18 11:01) [16] > по логике, знаю как работает PostMessage... > память могут и "перезанять" - AV, или хуже тихая ошибка с прочтением не того. > самая сложная ошибка когда все работает и только иногда...
Пример с перезанятием или тихой ошибкой будет?
Я не только знаю, как работает PostMessage, но и использую. Много лет такая передача работает независимо от фаз луны.
-
> команда SendMessage синхронная, т.е. все чего добиваются тут это "крутая/как winapi" замена обычного присваивания MyComp.MyStr:= S;
- синхронная, только WindowProc выполняется в контексте потока в котором вызывался CreateWindow (см. GetWindowThreadProcessId()), и - если освобождение строки потокобезопасно, то с обычным присваиванием будут проблемы - для строк присваивание гарантированно не атомарное, со всеми прелестями в виде утечек, повторного удаления и AV... C PostMessage vs PeekMessage/GetMessage - такая же фигня "... The window must belong to the current thread. ...". SendMessage: ... If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message. However, the sending thread will process incoming nonqueued messages while waiting for its message to be processed. ...
> Нельзя ли немного развернуть мысль? ... > будет разрыв между освобождением памяти и ее чтением
procedure post_string(const inputStr: string); var hackStr: string; begin {implicit compiler magic Initialize(hackStr); try } hackStr:= inputStr;//add inputStr reference count if( delegate_reference_owning(pointer(hackStr)) )then Pointer(hackStr):= nil { duplicates implicit compiler magic else // освобождаем память если владелец ссылки не поменялся hackStr:= '' // release string explicitly };
{implicit compiler magic finally Finalize(hackStr); end; } end;
procedure hook_string(looseString: pointer); var hackStr: string; begin {implicit compiler magic Initialize(hackStr); try } pointer(hackStr):= looseString; process_owned_string(hackStr); {implicit compiler magic finally Finalize(hackStr); end; } end;
Под оптимистичностью - здесь подразумевается, что пока новый владелец не обработал сообщение - ссылка ничья - потенциальная утечка памяти. То есть - должно подразумеваться, что неожиданное завершение обработки очереди сообщений - возможно только в случае завершения работы приложения. Иначе будет течь...
-
> То есть - должно подразумеваться, что неожиданное завершение обработки очереди сообщений > возможно только в случае завершения работы приложения. Иначе будет течь...
Ну это само собой. Обычно передают главному потоку, так что это требование выполняется автоматом.
|