ТКС>Так не бывает, в указанной строке нечему падать. Можно предположить, что на самом деле упало на call eax. eax чему равен — не 0x10004be0 (из первого сообщения) случаем? У lib __vptr или как его там не порушен ли уже?
Упало действительно "на call eax" но гораздо позже — внутри вызванного кода. Отладчик же посмотрел адрес возврата в стеке и показал cmp. В Студии довольно дубовый отладчик, его разработчики считают что незачем шляться по некоторым местам внутри CRT, отсюда и путанница. Анадогично с sysenter позже — это можно нормально отлаживать через kernel debug WinDbg, запуская отлаживаемый код на другой машине (проще всего виртуальной).
Студией можно пытаться отлаживать исключения, для этого приходится руками ставить точки останова на колбэке ntdll!KiUserExceptionDispatcher и ntdll!RtlUnwind...
.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
ТКС>>>На времени линковки можно сильно сэкономить, в случае разных dll. O>>И сильно потерять на времени загрузки приложения. ТКС>Да пофиг. У нас суммарное время линковки всех модулей текущего проекта минут сорок, не меньше.
вырубить link time code generaion не пробовали на девелоперских конфах?
Как много веселых ребят, и все делают велосипед...
Здравствуйте, ononim, Вы писали:
ТКС>>>>На времени линковки можно сильно сэкономить, в случае разных dll. O>>>И сильно потерять на времени загрузки приложения. ТКС>>Да пофиг. У нас суммарное время линковки всех модулей текущего проекта минут сорок, не меньше. O> O>вырубить link time code generaion не пробовали на девелоперских конфах?
Хе-хе-хе, издеваешься, да? У нас для некоторых модулей инкрементальную линковку в дебаге отключить пришлось, потому что она работать перестала — слишком большие ilk файлы получаются. C LTCG вообще думаю почти ничего не соберется... А ведь кода-то не так много, меньше миллиона строк. Зато шаблонов и прочих инлайнов полно, как своих, так и библиотечных. Они, судя по map-файлам, основной вклад в размер объектников и дают. Ну а на то, чтобы прошерстить гигабайты объектников и повыкидывать дубликаты, линкеру требуется время и память. Чем больше объектников в одном модуле — тем больше времени и памяти надо.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, Centaur, Вы писали:
C>Здравствуйте, blackhearted, Вы писали:
B>>Что я упустил?
C>Классы вообще и исключения в частности нельзя использовать через границу DLL.
А как же MFC, QT, и другая куча либин?
UNIX way — это когда тебе вместо туалетной бумаги дают топор, рубанок и карту близлежащего леса
Здравствуйте, catBasilio, Вы писали:
B>Здравствуйте, Centaur, Вы писали:
C>>Здравствуйте, blackhearted, Вы писали:
B>>>Что я упустил?
C>>Классы вообще и исключения в частности нельзя использовать через границу DLL.
Не так, правильно так: "Классы вообще и исключения в частности нельзя использовать через границу DLL, если модули собраны разными компиляторами"
B>А как же MFC, QT, и другая куча либин?
MFC для каждой версии компилятора идет своя, а "Qt и другая куча либин" — opensource, их всегда можно пересобрать своим компилятором.
так все-таки как поступать, если все компилируется в одном компиляторе, хочется динамическую загрузку библиотек и кидание исключений?
Допустим такой код:
{
ScopedLoadLibrary lib ( "component.dll" );
ComponentPtr comp = ComponentFactory::CreateInstance();
comp->DoSomething(); // и отсюда вылетает некий class myexception : public std::exception
}
Вроде бы нормальный с точки зрения клиентского вызова код.
Но код myexception::~myexception находится в component.dll, а это значит, что в catch-е который его отловит модуль уже будет выгружен.
И когда CRT-шный код пытается удалить объект — происходит AV.
Здравствуйте, Кодт, Вы писали:
К>void dec_ref() { if(--g_nRef) FreeLibrary(g_hDLL); }
К>struct dll_reference К>{ К> dll_reference() { add_ref(); } К> dll_reference(const dll_reference&) { add_ref(); } К> ~dll_reference() { dec_ref(); } К>};
К>struct my_exception : std::exception, dll_reference К>{ К> ..... К>}; К>// и так везде! К>[/c] К>Подобным образом обеспечивается живучесть Inproc COM Server'ов — DLL живёт до тех пор, пока есть COM-объекты, созданные ею.
К>Подсчёт ссылок удобно делать интрузивным, а то и вообще переложить на плечи ядра (LoadLibrary/FreeLibrary тоже считают ссылки в недрах).
Ха. код dec_ref находится в component.dll, вызывает FreeLibrary(себя же), в которой выгружается component.dll, return и мы оказываемся в вакууме
Я пробовал аналогичный вариант, только не через множественное наследование.
template< typename U >
class LockLibrary : public U
{
public:
LockLibrary()
: lib_( ::LoadLibraryA( g_strModuleFileName ) )
{
}
private:
ScopedLoadLibrary lib_;
};
// и в кодеthrow LockLibrary< my_exception >;
все именно так и происходит
В COM-е насколько я знаю есть глобальная функция DllCanUnloadNow, по которой клиентский модуль и принимает решение — выгружать/не выгружать.
On 12/03/2011 09:49 PM, Amor wrote:
> так все-таки как поступать, если все компилируется в одном компиляторе, хочется > динамическую загрузку библиотек и кидание исключений? >
Я не очень понимаю, в чём проблема. В смысле, что тут нет проблемы.
> Допустим такой код: > > { > ScopedLoadLibrary lib ("component.dll" ); > ComponentPtr comp = ComponentFactory::CreateInstance(); > > comp->DoSomething();// и отсюда вылетает некий class myexception : public std::exception > }
> Вроде бы нормальный с точки зрения клиентского вызова код. > Но код myexception::~myexception находится в component.dll, а это значит, что в > catch-е который его отловит модуль уже будет выгружен. > И когда CRT-шный код пытается удалить объект — происходит AV.
как бы если этот exception определён в этом модуле, то клиенты exception-а
(те, кто его использует) будут клиентами и этого модуля. Если модуль
выгружается, когда у него есть действующие (активные) клиентны, это , очевидно,
неправильно, это делать нельзя. Если у тебя такое есть, значит либо .ddl нельзя
выгружать до окончания обработки исключения, либо исключение это надо определять
не в этой .dll, одно из двух.
Либо откладывай момент выгрузки .ddl, в Win32 это можно например делать
отправкой сообщения (PostMessage) о необходимости выгрузки .ddl.
Либо выноси исключение в отдельный, используемый всеми и невыгружаемый модуль.
Можно ещё (но это вариация на тему выноса исключения) сделать залипуху,
ловить вокруг этого кода исключение, перекодировать его в другое, общее
исключение и перебрасывать. Но это для случаев, когда данную .ddl менять нельзя.
On 12/04/2011 09:40 AM, Amor wrote:
> Ха. код dec_ref находится в component.dll, вызывает FreeLibrary(себя же), в > которой выгружается component.dll, return и мы оказываемся в вакууме > Я пробовал аналогичный вариант, только не через множественное наследование.
Из кода библиотеки нельзя выгружать саму эту библиотеку, моментально получишь
защиту памяти, потому как указатель инструкций процессора после выхода из
FreeLibrary() будет указывать на код этой самой библиотеки, которая уже выгружена.
Нужно поручать выгружать данную библиотеку какому--то другому модулю,
после выхода из кода библиотеки.
Здравствуйте, MasterZiv, Вы писали:
MZ>как бы если этот exception определён в этом модуле, то клиенты exception-а MZ>(те, кто его использует) будут клиентами и этого модуля.
Так в том-то и дело, что приведенный мной код не является клиентом my_exception.
Ему нужен Component, он залочил библиотеку, попользовался компонентом, разлочил.
Точка.
Код catch является клиентом std::exception и ничего об этой DLL не знает.
MZ>Либо откладывай момент выгрузки .ddl, в Win32 это можно например делать MZ>отправкой сообщения (PostMessage) о необходимости выгрузки .ddl.
Тут не понял. Кто кому должен отправить это сообщение?
MZ>Либо выноси исключение в отдельный, используемый всеми и невыгружаемый модуль.
Я вообщем-то тоже прихожу к такому мнению.
Но это надо бы проверить.
Если я создам my_exception, который экспортируется из другого модуля, то где будет таблица виртуальных функций этого объекта?
MZ>Можно ещё (но это вариация на тему выноса исключения) сделать залипуху, MZ>ловить вокруг этого кода исключение, перекодировать его в другое, общее MZ>исключение и перебрасывать. Но это для случаев, когда данную .ddl менять нельзя.
Уффф... это мне кажется вообще не вариант. Оборачивать каждый вызов?
Легче уж отказаться от использования исключений между dll и делать все на HRESULT-ах.
On 12/04/2011 01:18 PM, Amor wrote:
> MZ>как бы если этот exception определён в этом модуле, то клиенты exception-а > MZ>(те, кто его использует) будут клиентами и этого модуля. > > Так в том-то и дело, что приведенный мной код не является клиентом my_exception. > Ему нужен Component, он залочил библиотеку, попользовался компонентом, разлочил. > Точка. > > Код catch является клиентом std::exception и ничего об этой DLL не знает.
Если он получает через ссылку на std::exception объект-наследник типа
конкретного эксепшена, который реализован в этом компоненте, то этот
код также является клиентом этого конкретного эксепшена и этого компонента.
В С++ так. Может в других языках было бы по-другому.
> > MZ>Либо откладывай момент выгрузки .ddl, в Win32 это можно например делать > MZ>отправкой сообщения (PostMessage) о необходимости выгрузки .ddl. > > Тут не понял. Кто кому должен отправить это сообщение?
Какому-то менеджеру компонент, который в нужный момент, потом, выгрузит
указанный компонент.
> > MZ>Либо выноси исключение в отдельный, используемый всеми и невыгружаемый модуль. > > Я вообщем-то тоже прихожу к такому мнению. > Но это надо бы проверить. > Если я создам my_exception, который экспортируется из другого модуля, то где > будет таблица виртуальных функций этого объекта?
В модуле, где реализован этот класс, отку да он экспортируется
class __declspec(dllexport) MyClass
{
//...
};
. тебе какая разница ?
> Уффф... это мне кажется вообще не вариант. Оборачивать каждый вызов? > Легче уж отказаться от использования исключений между dll и делать все на > HRESULT-ах.
Почему ? Чё за дурость ? Вечно все почему-то как-то связывают .dll с
невозможностью что-то исползовать. DLL-и ничего использовать не мешают.
Здравствуйте, MasterZiv, Вы писали:
MZ>Если он получает через ссылку на std::exception объект-наследник типа MZ>конкретного эксепшена, который реализован в этом компоненте, то этот MZ>код также является клиентом этого конкретного эксепшена и этого компонента. MZ>В С++ так. Может в других языках было бы по-другому.
Код
catch( std::exception& e )
{
}
не является клиентом my_exception. Это так в любом ООП-языке.
если ты думаешь иначе, то наверно мы не договоримся и тебе лучше уйти.
MZ>Какому-то менеджеру компонент, который в нужный момент, потом, выгрузит MZ>указанный компонент.
А кто должен его отправлять?
MZ>В модуле, где реализован этот класс, отку да он экспортируется MZ>. тебе какая разница ?
Ну это тогда хорошо, потому что выгрузка component.dll будет происходить из my_exception.dll и все это время таблица виртуальных функций класса my_exception будет жива.
Только при условии, что my_exception сам лочит component.dll.
MZ>Почему ? Чё за дурость ? Вечно все почему-то как-то связывают .dll с MZ>невозможностью что-то исползовать. DLL-и ничего использовать не мешают.
Выгружать DLL из блока catch при throw из DLL нельзя. Просто нельзя, потому что это неправильно. Даже если кто-то придумает какой-нибудь хитрый ход, который сработает на конкретной версии конкретного компилятора — всё равно нельзя. Потому что в другой версии это не сработает.
Дело не только во времени жизни вброшенного объекта exception.
Пока мы не вышли из последнего ("неперевбрасывающего") блока catch, обработка throw не завершена. И код DLL в этой обработке участвует (по крайней мере, имеет право участвовать — как вбрасывающая функция).
Здравствуйте, Amor, Вы писали:
A>Здравствуйте, MasterZiv, Вы писали:
MZ>>Если он получает через ссылку на std::exception объект-наследник типа MZ>>конкретного эксепшена, который реализован в этом компоненте, то этот MZ>>код также является клиентом этого конкретного эксепшена и этого компонента. MZ>>В С++ так. Может в других языках было бы по-другому.
A>Код A>
A>catch( std::exception& e )
A>{
A>}
A>
A>не является клиентом my_exception. Это так в любом ООП-языке.
Если my_exception порождён из std::exception, то с чего бы нет? В данном случае "std::exception" — это форма обращения, "внешний вид" того, что по своему происхождению есть "my_exception". И если они прилинкованы из двух разных DLL, то и виртуальные методы одного my_exception могут быть реализованы в разных dll (в зависимости от деклараций).
A>если ты думаешь иначе, то наверно мы не договоримся и тебе лучше уйти.
Про ООП думайте как хотите. FreeLibrary — это не ООП. Это "внезапно грохнуть кусок кода, даже если он сейчас выполняется". И вообще загрузка-выгрузка DLL — это не проблема ни C++, ни ООП вообще. Приложение берётся динамически реогранизовывать своё адресное пространство по ходу своей жизни. Естественно, это нужно делать с оглядкой на эту самую жизнь.
MZ>>Какому-то менеджеру компонент, который в нужный момент, потом, выгрузит MZ>>указанный компонент.
A>А кто должен его отправлять?
Его должен отправлять тот код, который принимает на себя ответственность убить DLL, но выдаёт предписание другому коду сделать это в другое время.
Это может быть не обязательно сообщение. Можно, например, в список какой-нибудь занести. Чтобы потом, когда гарантированно закончатся все обработки, связанные с этой DLL, можно было бы её грохнуть.
MZ>>В модуле, где реализован этот класс, отку да он экспортируется MZ>>. тебе какая разница ?
A>Ну это тогда хорошо, потому что выгрузка component.dll будет происходить из my_exception.dll и все это время таблица виртуальных функций класса my_exception будет жива. A>Только при условии, что my_exception сам лочит component.dll.
MZ>>Почему ? Чё за дурость ? Вечно все почему-то как-то связывают .dll с MZ>>невозможностью что-то исползовать. DLL-и ничего использовать не мешают.
DLLs ничему не мешают. Просто они динамические (а многие про них думают, как про обычные (т.е. статические) библиотеки. Ну, типа, велосипед — очень неустойчивая конструкция по сравнению с табуреткой).
Здравствуйте, ndemia, Вы писали:
N>Выгружать DLL из блока catch при throw из DLL нельзя. Просто нельзя, потому что это неправильно. Даже если кто-то придумает какой-нибудь хитрый ход, который сработает на конкретной версии конкретного компилятора — всё равно нельзя. Потому что в другой версии это не сработает.
N>Дело не только во времени жизни вброшенного объекта exception. N>Пока мы не вышли из последнего ("неперевбрасывающего") блока catch, обработка throw не завершена. И код DLL в этой обработке участвует (по крайней мере, имеет право участвовать — как вбрасывающая функция).
Поэтому я и думаю, можно ли найти грамотное решение с точки зрения С++ и ООП, чтобы о времени жизни объекта/библиотеки думали только клиенты этого объекта/библиотеки.
A>>Код A>>
A>>catch( std::exception& e )
A>>{
A>>}
A>>
A>>не является клиентом my_exception. Это так в любом ООП-языке.
N>Если my_exception порождён из std::exception, то с чего бы нет? В данном случае "std::exception" — это форма обращения, "внешний вид" того, что по своему происхождению есть "my_exception". И если они прилинкованы из двух разных DLL, то и виртуальные методы одного my_exception могут быть реализованы в разных dll (в зависимости от деклараций).
Я говорю, что этот код понятия не имеет о том, кто какой компонент там грузит, и далее — какие библиотеки грузит компонент, которого загрузили, и т.д. и т.п.
А ты, так понимаю предлагаешь примерно следующее
On 12/05/2011 01:51 PM, Amor wrote:
> Код > > catch( std::exception& e ) > { > } > > > не является клиентом my_exception.
Как ни пародоксально, является.
Реализация виртуальных методов где ?
Это так в любом ООП-языке.
В некоторых -- да. Но не в С++.
> если ты думаешь иначе, то наверно мы не договоримся и тебе лучше уйти.
Ха. Это как-то наивно выглядит. С чего я это уходить куда-то должен?
> А кто должен его отправлять?
Код, который хочет выгрузить .DLL и находится в этой .DLL.
В общем случае лучше -- любой код, который откуда-то хочет выгрузить .DLL
> Ну это тогда хорошо, потому что выгрузка component.dll будет происходить из > my_exception.dll и все это время таблица виртуальных функций класса my_exception > будет жива.
А, дошло таки.
> Только при условии, что my_exception сам лочит component.dll.
Кто его лочит, или отлочивает -- это без разницы. А он будет
РЕАЛИЗОВЫВАТЬ, обрабатывать запросы на загрузку и выгрузку
компонент.
A>main()
A>{
A> try
A> {
A> RunProgram();
A> }
A> catch( std::exception& e )
A> {
A> std::cout << "Exception occured: " << e.what() << std::endl;
A> }
A>}
A>
A>Я говорю, что этот код понятия не имеет о том, кто какой компонент там грузит, и далее — какие библиотеки грузит компонент, которого загрузили, и т.д. и т.п.
Этот — не имеет. Другой, может, будет иметь. Какая разница?
Например, есть вызов e.what(). Из этого кода не следует, что это не my_exception::what(). Т.е. данный код имеет реальный шанс вызвать код my_exception, не имея об этом никакого понятия. С точки зрения ООП/Си++ никто его ругать за это права не имеет. А вот с точки зрения организации процесса, если мы перед этим выгрузим код функции my_exception::what — всё накроется. Хотя объект my_exception будет ещё жив, и его VTBL в порядке.
A>А ты, так понимаю предлагаешь примерно следующее
A>
A>main()
A>{
A> ScopedLoadLibrary lib1( "component1.dll" );
A> ScopedLoadLibrary lib2( "component2.dll" );
A> ScopedLoadLibrary lib3( "component3.dll" );
A> try
A> {
A> RunProgram();
A> }
A> catch( std::exception& e )
A> {
A> std::cout << "Exception occured: " << e.what() << std::endl;
A> }
A>}
A>
Ну, если по простому, то да.
По жизни это может быть кучерявее.
Типа так:
потому что это может быть не main (программа может работать дальше), и условие может быть более сложным (например, можно ещё дождаться завершения каких-нибудь тредов и т.п.
On 12/05/2011 07:09 PM, ndemia wrote:
> Выгружать DLL из блока catch при throw из DLL нельзя. Просто нельзя, потому что > это неправильно. Даже если кто-то придумает какой-нибудь хитрый ход, который > сработает на конкретной версии конкретного компилятора — всё равно нельзя. > Потому что в другой версии это не сработает.
Ну, в стандарте таких запретов не будет, просто потому, что там нет .DLL.
Так что спорить можно до посинения и всё равно никому ничего не докажеш.
Тут есть более весомый и очевидный аргумент -- код класса выбрасываемого
объекта находится в этой самой .DLL. Он нужен для копирования объекта
исключения и последующей обработки его в блоке try.
> DLLs ничему не мешают. Просто они динамические (а многие про них думают, как про > обычные (т.е. статические) библиотеки. Ну, типа, велосипед — очень неустойчивая > конструкция по сравнению с табуреткой).
На самом деле они мало чем отличаются. На высоком уровне.
On 12/05/2011 08:06 PM, Amor wrote:
> Поэтому я и думаю, можно ли найти грамотное решение с точки зрения С++ и ООП, > чтобы о времени жизни объекта/библиотеки думали только клиенты этого > объекта/библиотеки.
Понятия "библиотека" нет ни там, ни там.
Ни в С++ , ни в ООП.
Так что как ты их средствами сможеш решить эту проблему -- не понятно.
Здравствуйте, MasterZiv, Вы писали:
MZ>Понятия "библиотека" нет ни там, ни там. MZ>Ни в С++ , ни в ООП. MZ> Так что как ты их средствами сможеш решить эту проблему -- не понятно.
Ну может попробовать воспринимать "библиотеку" как элемент предметной области.
А вообще если "не понятно", тогда и вправду лучше уйти.