Comments 14
Для экспортов, теоретически, есть UnmanagedExports, но у меня оно слёту не завелось, да необходимость и собирать 2 различных по битности варианта managed сборки (AnyCPU не поддерживается), меня оттолкнуло.
UnmanagedExports по ходу вообще нерабочий пакет, у меня тоже не заработал, разбираться в чужих багах не хотелось и в итоге делал свою реализацию в postbuild event. Две различных по битности managed сборки по моему не более отталкивающие чем 2 различных по битности unmanaged.
Не увидел проверку очевидного варианта: написать загрузчик приложения, который инициализирует логи и передаст управление основной программе.
public static void Main(){
InitLogs();
YourApp.Program.Main();
}
Первое и самое очевидное — надо добиться, чтобы использовалась ровно та же версия рантайма, что и в приложении, которое запускаем. Значит как минимум шаманить с конфигами вручную.
Второе, те самые конфиги. Приложение будет пытаться подхватить не свой конфиг, а конфиг загрузчика. Решение (не уверен, что полноценное) — заменить конфиг загрузчика копией конфига приложения.
Третье — точки входа в приложение часто помечаются атрибутами, задающими COM threading model, например STAThread. «Решение» — писать по отдельному загрузчику на каждую threading model. Какой из них использовать, выбирать или вручную, или писать ещё один, головной загрузчик.
Четвёртое. Если приложение где-то в своих потрохах использует Assembly.GetEntryAssembly, то его может ждать большой сюрприз.
Будут ещё технические моменты по правильному определению точки входа приложения (а они разные бывают, взять хотя бы приложение на VB). По запуску приложения через reflection, передаче ему командной строки — но это всё решаемо в рабочем порядке.
Полагаю, что если не пытаться делать универсальный загрузчик, а делать его разово под конкретное приложение, с учётом особенностей этого приложения — то, при аккуратной реализации, предложенный подход вполне применим.
PS:
Есть хорошая статья, описывающая, что делает CLR, перед тем как стартовать приложение. Не так уж и мало мест, где что-то ещё может пойти не совсем так, как ожидалось.
А замена конфига загрузчика конфигом приложения заодно решит проблему правильного рантайма :-)
Кстати, можно создать новый AppDomain и запустить сборку в нем. В нем можно заменить главный конфиг и входную сборку (а вот версию рантайма придется копировать в конфиг загрузчика).
Выглядит не так уж страшно, и вроде как обещает даже сделать вызов в AppDomain-е по умолчанию. Непонятно, правда, в каком thread, но и на том спасибо.
А в каком потоке кроме текущего может выполняться блокирующий вызов?
Кстати, с этим могут быть проблемы: текущий же поток в DllMain висит, а там не все winapi разрешено использовать. Надо не забыть про трюк с APC на свой же поток, в котором уже делать основную работу.
Кстати, нет ничего сложного и в том чтобы в другом процессе на лету написать новую функцию. Это снимет ограничение в 1 строковый параметр для передачи.
По поводу поведения AppDomain.UnhandledException: так исключение-то не является Unhandled! Оно перехватывается и обрабатывается в цикле обработки сообщений.
Чтобы его перехватить, надо подписываться на Application.ThreadException (что и делает библиотека). Но это событие — локальное для потока!
Так что идея перейти в основной поток перед инициализацией библиотеки была правильная. Только надо не забыть обработать случай безоконного приложения (в таком случае надо инициализировать библиотеку в текущем потоке), а также WPF приложения (у WPF своя реализация цикла обработки сообщений).
Наша ситуаций такова. Мы разрабатываем компоненты, продаём их нашим клиентам, программистам. Те, в свою очередь, пишут с их использованием софт для своих заказчиков. Далее, довольно типовая тупиковая ситуация. Из 50 заказчиков у одного софт подпадывает. Заказчик жалуется своему поставщику, тот в свою очередь жалуется нам в саппорт. При этом проблема не поспроизводится нигде, кроме как на машине у того самого заказчика. Teamviewer к машине не дают, студию и прочие нужные тулзы поставить тоже нельзя, передеплоить новую версию приложения или нельзя вообще или не получается сделать быстро, а разобраться и пофиксить надо уже вчера.
В наших общих интересах, в создавшихся условиях, собрать максимально объективную информацию о возникновении проблемы и максимально быстро её поправить. Наша служба поддержки в таких случаях пытается объяснить, какая инфа нужна, и как её собрать (те же дампы и прочее). Это все занимает время, и немало. Для экономии времени, они просили нас сделать им тулзу, которая автоматически сделает всё, что необходимо. От программиста лишь требуется дописать батник для запуска его приложения и отправить заказчику вместе с тулзой. От заказчика требуется лишь скачать и запустить в один клик.
Предлагаемые вами сбор дампов руками заказчика и прочие манипуляции, которые надо провести его силами, вполне применимы и возможны, но обычно уходит довольно много времени, чтобы объяснить заказчику, что и как ему делать. А наша служба подддержки тут в лучшем случае 3я в цепочке заказчик <-> программист <-> devexpress.
Может следует добавить возможность логироания в компоненты, активируемую например параметром в конфиг файле, или фактом наличия файла или ключем в реестре уж если совсем все плохо и самим создавать нерадивым клиентам дампы :)…
Иногда просто пытаются перекинуть свои проблемы на нас (особенно этим индусы грешат), и даже в этом случае стараемся сначала добыть объективную информацию, и на её основании уже вежливо указать клиенту, где он неправ.
Логировать было бы неплохо, но тут надо волевое политическое решение принимать, на уровне фирмы.
А больше всего радуют (но нечасто) реально продвинутые ребята, которые приходят с проблемой типа «в методе X класса Y у вас в N-ной строке косяк, надо исправить вот так. поправьте плиз, чтобы мне из исходников ваши компоненты не собирать». Или, «я хочу что-то этакое закастомизить, почти всё сделал, но не хватает virtual в методe X класса Y, чтобы перекрыть и получить искомое. впишите, не сочтите за труд.»
Противоестественная диагностика