Builder, CoUninitialize и IXMLDocument в отдельном потоке
От: SterhBy  
Дата: 08.04.05 10:57
Оценка:
Мастера, подскажите.
Есть класс, создаваемый статически, в котором нужно использовать XML parser.
Этот класс используется в отдельном потоке приложения. Это единственный поток, который порождается из основного и выполняет обработку данных.
Выглядит этот класс приблизительно так:

TPayrollParser PayrollParserVar;
TPayrollParser *PayrollParser = &PayrollParserVar;

.............................

class TPayrollParser
{
private:
_di_IXMLDocument x;
public:
__fastcall TPayrollParser(void);
__fastcall ~TPayrollParser();

};
.............................

// Constructor
__fastcall TPayrollParser::TPayrollParser(void)
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
x = NewXMLDocument();
}

// Destructor
__fastcall TPayrollParser::~TPayrollParser()
{
try
{
x->Release();
CoUninitialize(); //Вот здесь ошибка
} catch(...) {}
}
//------------------

Собственно проблема в том, что при вызове CoUninitialize (который вызывается при завершении программы) происходит ошибка, которую нельзя никак перхватить.
Винда выдает:
"Payroll.exe has encountered a problem and needs to close. We are sorry for the inconvenience."

Release практически не влияет на ситуацию.
Если убираю CoUninitialize — все без ошибок.

Приблизительно понимаю, что происходит. Видимо CoUninitialize реально вызывается до уничтожения СОМ-объекта парсера, но не пойму как должно быть правильно.
_di_IXMLDocument — это вроде дельфийский интерфейс и его не нужно удалять вручную.
Когда тогда делать CoUninitialize ?
И можно ли его не вызывать вообще (не повлияет ли это на другие приложения)?

Кстати, вот что пишет Борланд в хелпе:
"There is a situation which could cause crash-on-exit / crash-on-dll-unload problems. This applies if you are writing code that calls CoInitialize in the startup section, and CoUninitialize in the shutdown section. Though Microsoft documentation states that CoInitialize/CoUninitialize must always be called before using OLE code, the reality is that these routines must never be called in DLL startup or shutdown code. CoUninitialize can cause crashes or system deadlocks if called in DLL shutdown code (a unit finalization block)."

Получается, что это мой случай и не нужно вызывать CoUninitialize?

Извините, если вопрос тривиален или я сам себе на него уже ответил, но нужно быть уверенным в том, что решение не повлияет негативно на другие программы, так как работа нескольких программ должна вестись на одной машине в режиме 24х7.
Re: Builder, CoUninitialize и IXMLDocument в отдельном поток
От: Danchik Украина  
Дата: 08.04.05 12:08
Оценка:
Здравствуйте, SterhBy, Вы писали:

SB>Мастера, подскажите.

SB>Есть класс, создаваемый статически, в котором нужно использовать XML parser.
SB>Этот класс используется в отдельном потоке приложения. Это единственный поток, который порождается из основного и выполняет обработку данных.

Дружище, не пиши ты эти методы в конструкторе и деструкторе класса, они совсем к нему не относятся Это просто плохой подход.
CoInitilaize нужно вызывать единожды для потока а не для класса

В Execute методе потока напиши:


procedure TPayrollParserThread.Execute;
begin
  CoInitializeEx (nil, COINIT_MULTITHREADED);
  {если будет падать напиши CoInitialize (nil)}
  try
    ....
  finally
    CoUnitialize;
  end;
end;



Я Делфист и незнаю как там в CBuilder релизаются интерфейсы. Вполне возможно что точно также. Если да то x->Release()
в деструкторе вызовет креш. Потому что деструктор вызывает Release для своих членов неавно. Один будет явно лишним

Удачи!
Re[2]: Builder, CoUninitialize и IXMLDocument в отдельном по
От: SterhBy  
Дата: 08.04.05 12:35
Оценка:
Здравствуйте, Danchik, Вы писали:

D>Дружище, не пиши ты эти методы в конструкторе и деструкторе класса, они совсем к нему не относятся Это просто плохой подход.

D>CoInitilaize нужно вызывать единожды для потока а не для класса

D>В Execute методе потока напиши:


D>
D>procedure TPayrollParserThread.Execute;
D>begin
D>  CoInitializeEx (nil, COINIT_MULTITHREADED);
D>  {если будет падать напиши CoInitialize (nil)}
D>  try
D>    ....
D>  finally
D>    CoUnitialize;
D>  end;
D>end;
D>



D>Я Делфист и незнаю как там в CBuilder релизаются интерфейсы. Вполне возможно что точно также. Если да то x->Release()

D>в деструкторе вызовет креш. Потому что деструктор вызывает Release для своих членов неавно. Один будет явно лишним

D>Удачи!



Спасибо за совет.
Я не хотел так делать, чтобы не создавать экземпляр этого класса динамически в потоке. Это не слишком экономно для такой задачи.
Но я попробовал. Такой ошибки уже нет, зато вся программа работает нестабильно. Через пару десятков запусков потока программа слетает .
Re[3]: Builder, CoUninitialize и IXMLDocument в отдельном по
От: Danchik Украина  
Дата: 08.04.05 13:11
Оценка:
Здравствуйте, SterhBy, Вы писали:

SB>Спасибо за совет.

SB>Я не хотел так делать, чтобы не создавать экземпляр этого класса динамически в потоке. Это не слишком экономно для такой задачи.
Да тебе и не надо этого делать Просто что бы создать свой класс CoiInitialize уже должно быть вызвано! Для главного потока сделай это в DPR (ищи CBuilder аналог)


program SomeProgram;
uses
  ActiveX;
begin
  CoInitilize (nil);
  try 
    Application.Initialize;
    Application.CreateForm (TForm1, Form1);
    Application.Run;
  finally
    CoUninitialize;
  end;
end;


SB>Но я попробовал. Такой ошибки уже нет, зато вся программа работает нестабильно. Через пару десятков запусков потока программа слетает .

А теперь делай так. Ты используеш MSXML. Вызови сразу в конструкторе x->asynch = false (выключи асинхронную загрузку). На эти грабли часто становятся.
Re[4]: Builder, CoUninitialize и IXMLDocument в отдельном по
От: SterhBy  
Дата: 11.04.05 08:36
Оценка:
Здравствуйте, Danchik, Вы писали:

D>А теперь делай так. Ты используеш MSXML. Вызови сразу в конструкторе x->asynch = false (выключи асинхронную загрузку). На эти грабли часто становятся.


Попробовал. (только это в x->ParseOptions надо смотреть)
По умолчанию опция poAsyncLoad выключена.
Значит дело не в этом.

Короче, спасибо.
Я еще с знающим товарищем посоветовался и он подтвердил, что вызов Coinitialize(Ex) в начале процесса не обязательно завершать Couninitialize при завершении работы процесса. Эта ситуация корректно обрабатывается системой.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.