Поиск по блогу

понедельник, 16 августа 2010 г.

Delphi: работа с RegExp в dll

Как работать с RegExp в библиотеках? Дело в том, что библиотека VBScript_RegExp_55_TLB — майкрософтская, без проблем тут не обошлось.

Вроде экземпляр объекта TRegExp создается, но при попытке с ним поработать вылазит ошибка:

Не был произведен вызов CoInitialize.


Если честно, столкнулась с этим впервые, пошла в интернет искать. Пришлось подключать ActiveX и использовать CoInitialize/CoUninitialize.

Нашла статью про подобную проблему. Попробовала так:

var
...
RE : TRegExp;
NeedToUninitialize : Boolean;
begin
NeedToUninitialize := Succeeded(CoInitialize(nil));
try
RE := TRegExp.Create(nil);
RE.IgnoreCase := true;
RE.Multiline := true;
RE.Global := true;
...

finally
RE.Free;
if NeedToUninitialize then CoUninitialize;
end;
...
end;


Но и тут не заладилось: если CoUninitialize писать без try...except, то на нем вываливается. И что-то мне эта ситуация совсем не понравилась, как-то мутно: коинициализируется — а потом что? Не будет ли проблем, если работать с библиотекой в несколько потоков и т.д.? Даже если CoInitialize/CoUninitialize делать не в функции, а при регистрации либы.

Кто-нибудь из читателей блога сталкивался с чем-нибудь подобным? Как решали?
___

Чтобы быть в курсе обновлений блога, можно подписаться на RSS.

Статьи схожей тематики:



12 комментариев:

  1. Привет Мария. С такой проблемой не сталкивался и у меня тут соответственно вопрос: почему вас (тебя и ещё одного штаЦкого) тянет прямо на эту майкрософтскую библиотеку? Чем она завоевала ваши сердца?

    ОтветитьУдалить
  2. CoInitialize/CoUninitialize - это, вообще-то, основы COM, а не какие-то там "грабли". Delphi часто вызывает его за вас (при подключении нужного модуля), но только не в дополнительных потоках.

    > на нем вываливается

    Как вываливается? Дело в том, что вызывать вы его должны, когда закончили работу с COM. Если CoInitialize выше был первым вызовом в потоке, да плюс в коде между try и finally у вас появляется временная переменная-интерфейс (которая освобождается в end), то неудивительно, что вы получаете ошибку.

    Обычно имеет смысл делать так (окей, лучше бы, конечно, использовать CoInitializeEx):

    CoInitialize;
    try
    DoSomething;
    finally
    CoUnInitialize;
    end;

    Где вся работа происходит в процедуре DoSomething - таким макаром все временные интерфейсы гарантировано уплывут.

    > Не будет ли проблем, если работать с библиотекой в несколько потоков и т.д.? Даже если CoInitialize/CoUninitialize делать не в функции, а при регистрации либы.

    Если вы - это сервисный код, который ещё кто-то вызывает, то вызывать CoInitialize должен он, а не вы. Обычно это работает хорошо. TRegExp, к примеру, не делает этот вызов, оставляя его вызывающему, т.е. вам.

    ОтветитьУдалить
  3. Добрый день.
    GunSmoker все правильно написал, но если лень сидеть разбираться с тем как все это работает. То вот мой вариант решения данной проблемы. Даже два.
    1. В заголовке проекта, который описывает dll добавляем в uses oleauto. Собственно это позволяет частично забыть о том кто и когда должен вызывать и отключать COM. Есть правда и минус - мы не можем контролировать этот процесс и порой натыкаемся на странные ошибки, особенно если вызов dll может происходить несколько раз за работу программы (в случае динамического подключения через LoadLibrary), тогда приходится изголяться и придумывать механизмы защиты. С другой стороны, если dll грузится при старте программы и выгржается в конце, то все в порядке, имхо. Чтобы все работало надо, что вот эти модули:
    OleAuto
    OleCtl
    Ole2
    были доступны при компиляции либо в доступных путях, либо в папке с программой. А и да Delphi будет ругаться на этот uses, говоря, что он деприкейтед. :)
    2. Путь более простой, в каком-то смысле, но более тяжелый по ресурсам - в заголовке dll указать в uses Forms, что так же произведет инициализацию COM, но тогда dll надо "вводить" в Application основной программы, и затем выводить. Однако опять же если dll грузится статично, то это становится. Проще.

    ОтветитьУдалить
  4. Егор, даже не знаю, что ответить: использую и использую, как-то не задумывалась... А что для таких целей лучше подойдет?

    GunSmoker, спасибо за развернутый ответ! Буду разбираться, я действительно в этом ни бум-бум, еще не приходилось сталкиваться, я больше по базам данных... :)

    Leechdraw, спасибо!

    ОтветитьУдалить
  5. Не удержался :).
    Я тоже задавался тем же вопросом, что и "Егор О.". И сам порядочно намучился с регулярками, перебрал массу вариантов.
    Вообщем я остановился на PerlRegEx: http://www.regular-expressions.info/delphi.html

    Из минусов, требуется DLL которая идет в комплекте.
    Из плюсов: полная поддержка синтаксиса регулярок Perl (по крайней мере я лагов не замечал, а у других библиотек нарывался).

    А вот если "честно" поставить RegexBuddy так и проблема с написанием/отладкой вообще пропадает, он может даже генерировать куски кода под Делфи.

    Вообщем рекомендую посмотреть ;).

    З.Ы. Кстати, есть баг с поддержкой юникодовской кирилицы, но это в принципе у Перловых регулярок. От этого никуда не денешся

    ОтветитьУдалить
  6. Mifody, спасибо за наводку, обязательно надо будет попробовать.

    ОтветитьУдалить
  7. Еще лучше - использовать обертку над PCRE из JCL. Там и dll подключать никакие не надо.
    А с COM - библиотеками есть маленькая деталь: CoUninitialize в конце можно не делать, система разрулит. А если делать, то бывают вылеты, никак не связанные с собственным кодом - у меня такие сюрпризы выдавал MSXML.

    ОтветитьУдалить
  8. >>Из минусов, требуется DLL которая идет в комплекте.
    Нo это - совсем масенький минус. Там можно и объектники подключить. Можно менять директивы условной компиляции в начале pcre.pas - тогда dll можно не включать в проект. По дефолту уже настроенно на компилятор версии >=21.0 (d2010)

    ОтветитьУдалить
  9. Еще не увидел упоминания TRegExpr от Андрея Сорокина.

    ОтветитьУдалить
  10. Вот возможная причина ваших сбоев. Но рецепт не поможет, если вы вызываете CoUninitialize до того, как освобождены все COM-объекты в текущем потоке. Вопрос правда, зачем вообще вызывать эту функцию? Если она у вас вызывается при завершении программы/потока (а случаев, когда это надо в других местах можно по пальцам одной руки пересчитать), то почему бы не отдать процесс на откуп Windows и просто не вызывать CoUninitialize?

    ОтветитьУдалить
  11. pda, за ссылку спасибо :) Правда, этот вопрос для меня уже давным-давно не актуален, но, может, кому-нибудь пригодится информация.

    ОтветитьУдалить
  12. Привет.
    в самой dll.regexp надо дописать
    "что отметил в кавычках"

    procedure TRegExp.Connect;
    var
    punk: IUnknown;
    begin
    "CoInitialize(nil);"
    if FIntf = nil then
    begin
    punk := GetServer;
    Fintf:= punk as IRegExp2;
    "CoUninitialize();"
    end;
    end;
    Спасибо. Удачи.

    ОтветитьУдалить

Комментарии модерируются, вопросы не по теме удаляются, троллинг тоже.

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

Поделиться