Конференция "KOL" » String + KOL не так прост как кажется?
 
  • Barloggg (14.04.08 09:47) [0]
    Есть DLL. в ней форма.
    хост консольный с аплетом.  в нем центральный объект.
    вызываю DLL, даю этой длл прямую ссылку на главный объект.
    далее ДЛЛ делает так
    O.вложенныйPObj.Name:=EditBox.text;
    где Name:string; О - пришедший по ссылке центральный объект.

    Все кажется нормальным, Dll выключается, все работает, но на выходе падает.
    причем не где-нибудь, а именно в деструкторе того злополучного объекта которому изменили строковое поле из DLL. по прямым ссылкам.

    юмор в том что если сделать так:
    O.вложенныйPObj.Name:='';
    O.вложенныйPObj.Name:=EditBox.text;
    то не бабахает.

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

    Причем Дельфийский хелп говорит что за строками следить не надо, все сделает компилятор.
    Книга о KOL между делом одной фразой упоминает кратенько что строки надо давить в деструкторе, а то текут.

    Но теперь получается что надо их обнулять при каждом назначении?
    Кстати еще на эту тему, если в процедуре перед завершением сделать так s:=''; где s - локальный стринг, то компилятор не выдаст hint о том, что значением переменной не используется. тогда как на остальные переменные выдает.

    а так полагаю за это ответственнен менеджер памяти?  никакими заменами системных пока не пользуюсь. Интересно, а в VCL тоже так глючит?

    Есть какой удобный безопасный аналог строки?
    Какие подводные камни у shortstring?
  • Sapersky (14.04.08 14:58) [1]
    Создай новый проект-DLL и прочитай комментарий, который Дельфи вставляет в dpr-файл.
  • Barloggg (14.04.08 16:15) [2]
    я же не передаю строки.
    я передаю ссылку, верно? которая не есть строка. А потом у этой ссылки изменяю информацию, ничего не добавляю и не убавляя.
    хмм... а вот здесь-то наверное собака и порылась.
    значит правильное решение:
    процедура навроде setname(var s:string) в теле которой что-то вроде этого
    setlength(name,length(s));
    for i=1 to length(s) do name[i]:=s[i];


    так, что-ли?
    или не стринг, а PChar?

    но почему тогда деструктор строки которая была предварительно обнулена выполняется нормально?
  • Sapersky (14.04.08 17:18) [3]
    я же не передаю строки. я передаю ссылку, верно?

    This applies to all strings passed to and from your DLL--even those that are nested in records and classes.

    AFAIK, проблема в в том, что если не использовать ShareMem - у DLL и основной программы будут разные менеджеры памяти. Выделять в одном менеджере и освобождать в другом - нельзя, там не только вызовы API, но и свои внутренние структуры, которые от этого портятся. А при обращении к строкам память перераспределяется постоянно, даже при невинном s[i] (вставляется UniqueString).

    Рекомендую проштудировать длинную и подробную статью по строкам на Королевстве. Про передачу параметров в DLL там тоже, наверное, что-нибудь есть.

    Книга о KOL между делом одной фразой упоминает кратенько что строки надо давить в деструкторе, а то текут.

    Это относится только к object, из-за его неполной совместимости с LongString. И "давить" их нужно только если планируется наследование над данным объектом (т.е. строки на последнем уровне иерархии уничтожаются нормально).
  • Danger © (14.04.08 17:28) [4]

    > Barloggg   (14.04.08 16:15) [2]
    >
    > я же не передаю строки.
    > я передаю ссылку, верно? которая не есть строка. А потом
    > у этой ссылки изменяю информацию, ничего не добавляю и не убавляя.


    Скорее всего, менеждер памяти не совсем корректно обработает ситуацию с передачей строк (без разницы, по ссылке ли вы меняли строку или напрямую). А конкретно, на каком-то этапе память криво выделяется.

    Я вообще думаю, что он не может (да и не должен) правильно отработать в этом случае,  как вариант: попробуйте собрать DLL и хост-приложение под разными компиляторами (или менеджерами памяти), получите результат еще веселее.


    > хмм... а вот здесь-то наверное собака и порылась.
    > значит правильное решение:
    > процедура навроде setname(var s:string) в теле которой что-
    ......
    > так, что-ли?
    > или не стринг, а PChar?
    >


    Зачем вам string вообще, и жесткая привязка к объектам? Если вы хотите получить гибкую plugin-модель, то экспортируйте в DLL определенные простые типы (BOOL, WORD, DWORD aka PChar etc.), и не работайте с элементами ООП хост-приложения напрямую. Просто обменивайтесь между хостом и плагином данными статической длины, не требующими какой-то специфики работы с памятью от менеджера памяти. Тогда даже сможете при разработке DLL и хост-приложения применять различные компиляторы/среды разработки.
  • zldo (16.04.08 03:22) [5]
    Все подобные глюки происходят по одной причине - у DLL и у приложения которое его использует РАЗНЫЕ менеджеры памяти, то есть менеджер может быть один и тот же, только две копии :) Поэтому и кучи разные.
    Например ошибкой будет следующий ход:
    exe:
    ...
    GetMem(var1, 100)
    передаем эту переменную в dll и там
    FreeMem(var1, 100)
    и тут может случится все что угодно :)
    Со строками таже канитель, только вот работа с ними идет в "автоматическом" режиме :( Использовать их и не таскать с собой BORLNDMM.DLL в для связки dll/exe можно, но осторожно.

    А BORLNDMM.DLL как я понял хук менеджера памяти, который сводит вызовы менеджера памяти в exe и подгруженной ею dll в одно место.
  • Barloggg (17.04.08 15:41) [6]
    эмм... а как насчет того чтобы DLL управляла программой и сама программа об этом не заботилась вообще?

    в плагинах к винампу я заметил что DLL заполняет некую структуру ссылками на свои функции и единым списком их выдает. А эти внутренние функции без каких-либо экспортов потом вызываются самим винампом по прямым ссылкам.

    отсюда и решил что можно пользоваться прямыми ссылками. ведь это же по сути просто адреса в памяти.
  • Danger © (17.04.08 17:06) [7]

    > Barloggg   (17.04.08 15:41) [6]
    > в плагинах к винампу я заметил что DLL заполняет некую структуру
    > ссылками на свои функции и единым списком их выдает. А эти
    > внутренние функции без каких-либо экспортов потом вызываются
    > самим винампом по прямым ссылкам.
    >
    > отсюда и решил что можно пользоваться прямыми ссылками.
    > ведь это же по сути просто адреса в памяти.


    Про тип вызова - это уже другой разговор, не относящийся к особенностям выделения/высвобождения памяти. Сам факт доступности в рамках одного адресного пространства не означает использования одного менеджера памяти.

    Если будете пытаться использовать string и т.п. сложные типы, то хоть по адресам функции вызывайте, хоть через механизм экспорта - работать не будет. Несмотря на то, что DLL будет отображаться в адресное пространство вашего процесса, и функции будут доступны по адресам, в основном процессе и DLL все равно будут использоваться разные экземпляры менеджеров памяти.

    Насчет типа вызова - механизм экспорта предоставляет удобный механизм вызова функции по имени (чтоб адрес не запоминать, или заранее не знаешь). А пользоваться им или нет - это программист сам решает.
  • Barloggg (29.04.08 11:05) [8]
    гм, а вот у Thaddy я встретил com memory manager который так прямо и заявляет что все вызовы выделения памяти глобально переадресуются в com-вызовы и что это избавляет от проблем различных менеджеров памяти одним магическим кликом.

    здесь
    http://thaddy.co.uk/commm.pas

    в общем наш ответ sharemem'у
    кто-нибудь еще пробовал? или я буду второй? знать бы куда смотреть...

    кстати, а для обмена строками sharememory хорошо подходит? и есть где-нибудь популярный урок по пользованию? по намекам, надерганным то тут, то там,  я понял что это что-то вроде файла к которому все, кому не лень могут обращаться и работать как с файлом, правильно?
  • mdw © (29.04.08 11:47) [9]
    Это BORLNDMM.DLL + ShareMem.pas, который нужно подключить к проекту  первым в uses. Вот и все использование, собственно.
  • Danger © (30.04.08 07:31) [10]

    > кстати, а для обмена строками sharememory хорошо подходит?

    И все же, если требуется только строки передавать, почему такой упор именно на string? почему бы просто PChar не воспользоваться? Зачем усложнять себе жизнь, втыкать костыль ShareMem, если можно прекрасно обойтись без него, ведь вариант с PChar'ом универсальнее, и не сложнее в реализации.
  • Barloggg (05.05.08 11:14) [11]
    эмм. вопрос привычки я полагаю.
    а в чем разница между string и pchar? я частенько встречаю в коде прямые подстановки типов на лету. Но я не знаю подводных камней Pchar. Они есть?

    дельфийский хелп оперирует массивами [0..maxbuf]of char. статическими.
    из этого буфера стринг делает легко одним лишь знаком "=". а вот обратно без setlength говорит что можно получить грабли.

    самое простое решение это все стринги перевести в жестко ограниченные массивы с четко фиксированным размером. так что-ли? тогда грабли исчезнут, да. изменение числовых значений по прямым ссылкам вроде не бабахает.

    теперь понял. надо создавать хранилище строки и только после этого можно работать через Pchar. причем хранилищем должен быть только array of char. он может быть динамическим но его размер прямым High(pchar(x)) не узнать. надо хранить размер рядом. как узнать размер Pchar? присвоить его локальному string и спросить уже у него?

    ужас.

    но таскать с собой dll ой как не хочется.
  • Palladin © (05.05.08 18:24) [12]
    pchar просто указатель на данные, string - compiler magic тип с подсчетом ссылок размером... время жизни управляется rtl....
  • Danger © (05.05.08 20:42) [13]
    Barloggg   (05.05.08 11:14) [11]
    а в чем разница между string и pchar? я частенько встречаю в коде прямые подстановки типов на лету. Но я не знаю подводных камней Pchar. Они есть?


    На сайте Королевства Делфи есть замечательная статья про типы string и PChar, к сожалению, ссылку не могу привести. Поищите, там все хорошо описано. Насчет привычки: конечно, Вы можете использовать любой тип, какой хотите, но только если четко представляете все нюансы его использования. Тип string предоставляет массу удобств, но за это иногда приходится расплачиваться.

    самое простое решение это все стринги перевести в жестко ограниченные массивы с четко фиксированным размером. так что-ли?

    В этом необходимости нет. Вкратце: работайте в программе как вам привычно, со string'ами, но когда нужно получать/передавать строковые данные, преобразуйте их к типу PChar.
    То есть, работаете как обычно со string'ом - понадобилось его передать - преобразуете в PChar и передаете в плагин. Если надо, плагин может считать из PChar'a данные в свой локальный string. И все довольны: каждый менеджер памяти занимается только *своими* string'ами, и передача строковых данных работает. Правда, передавать данные как переменную нельзя.

    теперь понял. надо создавать хранилище строки и только после этого можно работать через Pchar. причем хранилищем должен быть только array of char. он может быть динамическим .... надо хранить размер рядом.

    Примерно таким же образом действует менеджер памяти ;) так что не надо повторять его работу. Если по-простому, то любой string представляет собой тип, для которого хранится указатель на хранилище (array of char) и длина этого хранилища. И менеджер памяти, при работе со строкой, делает все за нас, динамически изменяя размер хранилища по необходимости.

    как узнать размер Pchar?</>
    Размерность самого типа PChar, как и любого указателя - DWORD.
    А если вы хотите по PChar'у узнать длину строки (string), можете воспользоваться winapi-функцией lstrlen() или любой аналогичной.
  • Danger © (05.05.08 20:49) [14]

    >  String + KOL не так прост как кажется?  


    Название топика следовало бы сделать таким: "String не так прост как кажется?" ;)
  • Barloggg (20.05.08 10:35) [15]
    да, точно.
    в общем узнал много нового и интересного про стринг.

    в общем сделал так
    strarr:array of char
    str:pchar;
    strlng:integer;

    как выяснилось надо делать так str:=@strarr[0]; а не так (str:=@strarr);

    еще замечание в эту кучу.
    я наращиваю динамический массив у объекта setlength(ссылка_на_сам_объект_точка_динамический массив, новая длина). бабахает. тогда я сделал локальный метод у объекта, который делает тоже самое, но уже самостоятельно. на выходе опять бабахает. :( к этому может быть причастен менеджер памяти?
  • Sapersky (20.05.08 12:57) [16]
    Говорят же тебе, array of char и string это практически "одно и то же лицо". Соответственно и проблемы те же самые, разве что compiler magic при работе с дин. массивами используется пореже.
    http://www.rsdn.ru/article/Delphi/dynarrays.xml
  • Palladin © (20.05.08 13:42) [17]

    > я наращиваю динамический массив у объекта setlength(ссылка_на_сам_объект_точка_динамический
    > массив, новая длина). бабахает. тогда я сделал локальный
    > метод у объекта, который делает тоже самое, но уже самостоятельно.
    >  на выходе опять бабахает. :( к этому может быть причастен
    > менеджер памяти?

    а если перевести на человеческий язык?
  • Barloggg (20.05.08 15:44) [18]
    > Говорят же тебе, array of char и string это практически "одно и то же лицо".
    кажись дошло. То есть я мог не заморачиваться и делать так
    strArr:String;
    str:Pchar;
    str:=@Strarr[1];
  • Palladin © (20.05.08 15:53) [19]

    > str:=@Strarr[1];

    ни в коем случае...

    str:=PChar(strArr);
  • Freeman © (15.07.08 05:06) [20]
    Вот я не понимаю, столько нафлудили, кучу геморроя себе придумали... Что мешает, например, предусмотреть инициализационную функцию в DLL, вызываемую при её загрузке, принимающую значение MemoryManager из приложения и устанавливающее его себе в качестве рабочего? При выгрузке операция обратная. Раз сборка с BPL-ами не подходит как решение. ;)

    ИМХО, HeapMM © Владимир Кладов, встроенный в замену системных модулей, прозрачен к использованию в DLL, благодаря функции GetProcessHeap. На практике пока не проверял.

    Ищите да обрящете.
  • Danger © (17.07.08 22:10) [21]
    > Freeman ©   (15.07.08 05:06) [20]

    > Вот я не понимаю, столько нафлудили, кучу геморроя себе
    > придумали...

    У человека был конкретный вопрос; как могли подробно разъяснили, почему так, как он делал, сделать нельзя (попутно затронув string-и вообще и вскользь про менеджеры памяти). Проще один раз все подробно обсудить, и проблем не возникнет в последующем.


    >Что мешает, например, предусмотреть инициализационную
    > функцию в DLL, вызываемую при её загрузке, принимающую значение
    > MemoryManager из приложения и устанавливающее его себе в
    > качестве рабочего? При выгрузке операция обратная. Раз сборка
    > с BPL-ами не подходит как решение. ;)
    Возвращаясь к тому, что уже сказано: не вижу смысла вообще тащить менеджер памяти из хост-приложения в ДЛЛ. Я уж не говорю о том, что такой подход ограничивает в использовании средств разработки, и получается тот самый геморрой, о котором вы говорите.

    Отвязка от "сложных" типов данных решает проблему на корню - не надо задумываться об особенностях конкрентого менеджера, - используйте в хост-приложении и в дочернем модуле любой менеджер памяти по своему желанию, и передавайте любые данные через указатели (PChar, Pointer etc..) или фиксированной длины (Boolean, Word, DWord etc.). Этот способ универсален. Тем более, в случае string'a, никаких манипуляций даже не потребуется, чтобы скопировать данные из переданного PChar'a в локальный string (преобразование идет прозрачно).

    ЗЫ. Оказывается, тема еще актуальна...
 
Конференция "KOL" » String + KOL не так прост как кажется?
Есть новые Нет новых   [134431   +15][b:0][p:0.001]