Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, jazzer, Вы писали:
J>>Каким образом? Или речь прерывании потока каким-нибудь хакерством за пределами языка, типа KillThread какой-нибудь? Ну тогда да, против лома нет приема. S>А что у вас вместо лома? В managed environment даже при аборте потока есть шанс отработать хэндлеры и прибрать за собой ресурсы. В С++ такого просто вообще нет.
Ну то есть это единственный сценарий утечки? Низкоуровневый KillThread сразу после выделения памяти?
Ну ОК, осталось только понять, при чем тут исключения
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, jazzer, Вы писали: J>>А Синклер вон до сих пор, в 2015, спустя 16 лет (!!!), какой-то трэш на эту тему пишет S>В прошлый раз, когда поднималась тема C++, в первом же взятом открытом проекте мы нашли всё подряд — и выделение на голых malloc (хотя "конечно же все серъёзные проекты используют пулы, арены, и прочие продвинутые стратегии выделения/очистки), и raw pointers, и вызовы delete.
S>Но это да, Синклер виноват в том, что программисты С++ пишут вовсе не так, как о себе думают.
Не надо кивать на открытые проекты. Это феерическое ведь ты написал своими руками: "В С++ основная дупа — в том, что нет сборки мусора, а на динамически созданные объекты раскрутка стека не влияет." и дальше рукопашная работа с памятью, как будто ты о RAII вообще никогда не слышал.
J>Что значит "надежда на RAII"? Это все равно что "надежда на то, что sin(x) вернет синус, а не косинус" J>RAII — это же не финализаторы в управляемых языках, которые хз когда позовутся и позовутся ли вообще. J>Тут все гарантировано самим языком, безо всяких надежд.
Надежда в основном на то что RAII будет достаточно. Порядок свёртки какбы тоже часть RAII, и если он вдруг не устраивает крутись как хочешь. У finally с произвольным кодом гибкости всё-таки больше.
_>>2) можно кидать и ловить любую ерунду, с которой потом фиг поймёшь что делать. Вместе с другими зажравшимися программистами из мира интерпретаторов я уже привык к удобному объекту "исключение", с полями, методами, вот этим всем. J>Все вменяемые разработчики бросают производные от std::exception аж с 98 года.
И тем не менее в том 98-году не сделали это поведение единственно возможным и ключа компилятора --allowNonC98exceptions что бы приучить старообрядцев к порядку.
_>>3) разные команды используют разные базовые классы для исключений, и ни в стандарте ни в популярных либах нету самого главного — стэк трэйса. В результате на разных платформах и в разных компиляторах нужен разный код для сбора стэктрэйса, котрый вроде как нужно вызывать именно в методе где летело само исключение, потом уже поздно. На MSVC точно было именно так, на GCC чуть легче, на Intel не помню.
J>Во-вторых, "вызывать именно в методе, где летело" означает "вызывать в конструкторе исключения" — именно так все и делают (несколько тем на RSDN было об этом) и никакого лишнего кода в "методе, где летело", не появляется — все спрятано в конструкторе.
Всякие AV или Division By Zero уже вызывают правильный конструктор?
J>То ли дело возвращаемые значения. Так и вижу, как из конструктора/деструктора возвращаются коды ошибок
Ну я как бы не адепт возвращаемых значений, мне просто кажется что исключения по версии C++ могли бы быть лучше.
J>Как раз исключения в конструкторах гарантируют, что объект не будет создан в кривом состоянии. J>При этом RAII прибьет то, что успело создаться к моменту вылета исключения.
И как мне помнится всё ляжет если в процессе прибивания что-то пойдёт ещё более не так.
В общем пока все возражения мне видятся такими же как возражения фанатов .NET когда ругаешь косяки их любимой цацки — "никаких проблем нет а если что-то и есть то в языке полно средств выкрутиться, а значит проблемы нет".
Здравствуйте, hi_octane, Вы писали:
J>>Что значит "надежда на RAII"? Это все равно что "надежда на то, что sin(x) вернет синус, а не косинус" J>>RAII — это же не финализаторы в управляемых языках, которые хз когда позовутся и позовутся ли вообще. J>>Тут все гарантировано самим языком, безо всяких надежд. _>Надежда в основном на то что RAII будет достаточно. Порядок свёртки какбы тоже часть RAII, и если он вдруг не устраивает крутись как хочешь. У finally с произвольным кодом гибкости всё-таки больше.
Это как посмотреть. Во-первых, RAII работает внутри области видимости try, а finally — снаружи, и поэтому никакого доступа к внутренней области видимости не имеет. Как следствие, все надо вытащить за пределы try — гемор номер раз.
Отсюда сразу вытекает гемор номер два — далеко не все объекты разрешают копирование и/или отложенное конструирование.
Ну и гемор номер три — ты не знаешь внутри finally, что именно отвалилось, и что надо чистить, а что — нет. Так что придется придумывать всякие левые флаги и прочие обходные костыли.
С RAII же у тебя, во-первых, все оказывается внутри try (да и самого try обычно нет, кстати), во-вторых, сработают только те откатные RAII, которые успели сконструироваться к этому моменту — все, что ниже, срабатывать не будет.
J>>Все вменяемые разработчики бросают производные от std::exception аж с 98 года. _>И тем не менее в том 98-году не сделали это поведение единственно возможным и ключа компилятора --allowNonC98exceptions что бы приучить старообрядцев к порядку.
Ну да, наверное, это было бы неплохо, потому что нормальные разработчики все так делают все равно, а ненормальных надо к ногтю.
_>Всякие AV или Division By Zero уже вызывают правильный конструктор?
Всякие AV или Division By Zero находятся за пределами языка, это UB.
J>>То ли дело возвращаемые значения. Так и вижу, как из конструктора/деструктора возвращаются коды ошибок _>Ну я как бы не адепт возвращаемых значений, мне просто кажется что исключения по версии C++ могли бы быть лучше.
Как именно лучше? Критикуя — предлагай.
J>>Как раз исключения в конструкторах гарантируют, что объект не будет создан в кривом состоянии. J>>При этом RAII прибьет то, что успело создаться к моменту вылета исключения. _>И как мне помнится всё ляжет если в процессе прибивания что-то пойдёт ещё более не так.
Это уж как напишешь. Если следовать стандартной практике не бросать исключений из деструкторов, то ничего не ляжет.
Но можно и бросать, если знаешь, что делаешь, и правильно отлавливаешь (есть несколько движков, позволяющих разруливать исключения из деструкторов, и у меня свой собственный тоже есть).
_>В общем пока все возражения мне видятся такими же как возражения фанатов .NET когда ругаешь косяки их любимой цацки — "никаких проблем нет а если что-то и есть то в языке полно средств выкрутиться, а значит проблемы нет".
Я пока не вижу реальных возражений, кроме того, что нет стандартизированного базового типа исключения.
Вернее, он есть, но нет запрета им не пользоваться.
finally мне скорее не нравится, чем нравится — я считаю RAII гораздо более удобной вещью, чем finally (см. выше).
Здравствуйте, Sinclair, Вы писали:
J>>Каким образом? Или речь прерывании потока каким-нибудь хакерством за пределами языка, типа KillThread какой-нибудь? Ну тогда да, против лома нет приема. S>А что у вас вместо лома?
Например cancellation points раскручивающие стэк.
S>В managed environment даже при аборте потока есть шанс отработать хэндлеры
А что с finally?
S>и прибрать за собой ресурсы.
В недетерминированном порядке? Да ещё и в другом потоке.
согласен, в ерланге сделано по-человечески, но это не отменяет уродского синтаксиса в мейнстрим языках, и конечно не отменяет провоцирование невалидного стейта в принципе.
MTD>Исключения — это исключительные ситуации игнорирование которых делает дальнейшую работу бессмысленной, это если ты вдруг не в курсе.
капитан, капитан, улыбнитесь
MTD>Все-таки смешные вы, уже второй раз пишете говнокод, который будет говнокодом хоть с кодами ошибок, хоть с исключениями и считаете, что это что-то должно доказать.
смешные как раз вы, ты так и не привёл отчего-то рефакторинг "говнокода от adobe" с исключениями. а всё потому что это будет ещё больший говнокод. Вот mamut привёл, молодец, трудно спорить, в эрланге работа с исключениями удобна. В .NET/Java и тем более в C++ — нет.
а ты милок так и не понял, что я тебе хотел показать в примере. А хотел я тебе показать то — что любой вызов метода — потенциально бросает исключение, поэтому любое действие должно быть транзакционно. И с кодом ошибок всё очевидно — где return, надо зачекать состояние логики. Это можно прочекать просто глядя на код. Теперь полюбуйся на свой собственный код и скажи, а тебе тоже Гэндальф зарплату платит?
Здравствуйте, MTD, Вы писали:
MTD>Здравствуйте, antropolog, Вы писали:
A>>бросать исключение.
MTD>Не понял, ты же кричал, что код с исключениями — УГ по определению.
да, ты же не слушаешь собеседника, и не понимаешь, что кто-то УЖЕ УМЕЕТ НЕ ИСПОЛЬЗОВАТЬ исключения, надеюсь ты дойдёшь когда-нибудь до этого уровня понимания. И да, я использую исключения там, где в коде нарушается утверждение, т.к. альтернатива этому только одна — завершение работы приложения.
Здравствуйте, antropolog, Вы писали:
MTD>>И где здесь логика на исключениях?
A>покажи код
Ты как себя чувствуешь? Как из моих слов про то, что у итератора удобно иметь 2 метода вместо 1, ты сделал вывод про логику на исключениях? Давай поделись, как твой интеллект к такому выводу пришел, для этого мне показывать какой-то код совершенно лишнее.
Здравствуйте, antropolog, Вы писали:
MTD>>Не понял, ты же кричал, что код с исключениями — УГ по определению.
A>да, ты же не слушаешь собеседника
Нет, я слушаю:
пожалуйста, приведи пример хоть немного реального кода с кодами возврата, который можно переписать на исключения и он не будет выглядеть унылым говном.
Здравствуйте, antropolog, Вы писали:
A>смешные как раз вы, ты так и не привёл отчего-то рефакторинг "говнокода от adobe" с исключениями.
Я могу отрефакторить, но за деньги. Давай, например, так — я отрефакторю, выложим два куска кода на голосование, если адобовский код признают лучше я тебе переведу 100 долларов, если нет, то ты мне.
A>Вот mamut привёл, молодец, трудно спорить, в эрланге работа с исключениями удобна. В .NET/Java и тем более в C++ — нет.
Увы, с кодами еще хуже.
A>А хотел я тебе показать то — что любой вызов метода — потенциально бросает исключение, поэтому любое действие должно быть транзакционно.
Ну так я тебе и показал, что код ошибки никак это не решает.
A>И с кодом ошибок всё очевидно — где return, надо зачекать состояние логики.
То есть везде.
A>Теперь полюбуйся на свой собственный код и скажи, а тебе тоже Гэндальф зарплату платит?
У меня все отлично, где транзакционность нужна, она там есть.
Здравствуйте, antropolog, Вы писали:
A>отличный код. супер. реально. теперь попробуй его переделать на исключения. могу дать подсказку, в одних случаях наличие error ведёт сразу к возврату из функции ( это легко заменить на единственный try/catch ), а в других ( примерно с середины твоего кода ) error в некоторых случаях игнорируется, например здесь:
Да, на этот баг указали сразу. Просто терпения писать if(error) после каждой строчки не хватило, поэтому мы имеем код, который иногда добавляет элементы, а иногда — нет.
Точнее, программист решил, что в реальности эти методы "небросающие", т.к. там нечему ломаться. Через десять лет, когда реализацию AppendItem изменят, его наследников ждёт большой сюрприз.
A>здесь любая строка может не отработать, но это игнорируется.
Да, при этом если у нас не отработала вторая строка, то третья и четвёртая гарантированно сфейлятся, т.к. закладываются на её результат. И таких кросс-зависимостей там ещё несколько.
A>Покажи теперь как это (это — весь твой код ) будет на исключениях, аххахаха (зловещий хохот ).
Действительно — исключения затрудняют написание конкретно этого кода с ошибками. Чтобы проигнорировать ошибку, программист будет вынужден написать try {} catch(...){}.
Но пример действительно показательный — сплошной бойлерплейт.
Я плохо знаком с функциональными языками, но мне кажется очевидным вариант, при котором и бойлерплейта меньше, и с валидностью всё хорошо.
Что мы тут делаем? Конструируем структуру меню.
Пусть каждая функция возвращает либо узел, либо ошибку. И алгебра достаточно тривиальная — объединение (узел & ошибка) даёт ошибку, объединение (узел & узел) даёт узел.
Если мы хотим проигнорировать ошибку, то нам придётся вручную заматчить ошибку и превратить её в void.
Результатом функции, естественно, тоже является либо ошибка либо меню.
Основная фишка исключений — "неявная передача информации о сбое выше по стеку" — будет прекрасно работать и в таком варианте, потому что так устроена алгебра.
Примерный аналог — NaN в операциях с плавающей запятой. Как только у нас где-то в выражении встретился NaN, он сам распространится выше по стеку вплоть до того места, где мы хотим и готовы его обработать.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Но пример действительно показательный — сплошной бойлерплейт.
Бойлерплейта здесь больше с кодами ошибки — все эти if ... return. С исключениями у нас один блок try, если мы что-то здесь можем обработать или вообще его отсутствие. Что касается молчаливого игнорирования — это явная диверсия. Если добавление элемента в меню операция не обязательная, то должнен быть метод добавления элемента в приставкой Try или что-то в этом роде, который будет сам глотать исключения, тогда у нас с одной стороны и писанины мало и сразу видна логика.
Здравствуйте, MTD, Вы писали:
MTD>Бойлерплейта здесь больше с кодами ошибки — все эти if ... return. С исключениями у нас один блок try, если мы что-то здесь можем обработать или вообще его отсутствие. Что касается молчаливого игнорирования — это явная диверсия. Если добавление элемента в меню операция не обязательная, то должнен быть метод добавления элемента в приставкой Try или что-то в этом роде, который будет сам глотать исключения, тогда у нас с одной стороны и писанины мало и сразу видна логика.
Да, совершенно верно. Данный конкретный код с исключениями выглядит гораздо чище, плюс он "надёжнее" в том смысле, что если в спорном фрагменте что-то упало, мы всё же узнаем об этом где-то выше по стеку.
Проблема решения с исключениями — в том, что к моменту отлова исключения мы находимся в каком-то непонятном состоянии: меню уже перепахано, какие-то элементы добавлены, какие-то нет.
Решительно непонятно, как же вернуться к стабильному состоянию.
immutable и сборка мусора здесь сильно помогли бы: даже если у нас формирование меню было готово на 99%, и упала какая-то финальная операция, то мы возвращаем ошибку, а вся кунсткамера, "созданная" к этому моменту, как будто никогда и не существовала.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, MTD, Вы писали:
S>>Но пример действительно показательный — сплошной бойлерплейт. MTD>Бойлерплейта здесь больше с кодами ошибки — все эти if ... return. С исключениями у нас один блок try, если мы что-то здесь можем обработать или вообще его отсутствие.
Если говорить о бойлерплейте, то в данном случае он просто из-за говнокода, а не по вине кодов ошибок.
Я уже писал тут: если классы построены грамотно (код ошибки сидит внутри и проверяется по необходимости), или как предложил Sinclair — с возвратом алгебраического типа, то никакого бойлерплейта не будет — будет вызов цепочки методов, как обычно.
Ага, т.е. исключение первым делом конвертируется в возвращаемое значение вроде растовского Result, и с ним уже дальше вся работа. В Расте будет то же, только без вот этого дополнительного этапа конверсии исключения в Result. Просили код на исключениях, получили код на кодах возврата, по сути.
Здравствуйте, D. Mon, Вы писали:
DM>Ага, т.е. исключение первым делом конвертируется в возвращаемое значение вроде растовского Result, и с ним уже дальше вся работа.
Да Mamut зачем-то повелся на призыв некоего троля совершать диверсию игнорировать ошибки.
Здравствуйте, AlexRK, Вы писали: ARK>Если говорить о бойлерплейте, то в данном случае он просто из-за говнокода, а не по вине кодов ошибок. ARK>Я уже писал тут: если классы построены грамотно (код ошибки сидит внутри и проверяется по необходимости), или как предложил Sinclair — с возвратом алгебраического типа, то никакого бойлерплейта не будет — будет вызов цепочки методов, как обычно.
Никакого говнокода. Код либо будет обрабатывать ошибки — либо нет. С кодами возврата — добавляется лишь только ахеревший рукописный бранчинг на ровном месте и в каждом месте, который ведет преимущественно на выход, как в этом примере. А именно это исключения и дают.
Если класс построен грамотно, — он не будет привносить ненужное состояние к себе внутрь, до тех пор, пока в этом не возникнет реальной необходимости. Какая ошибка внутри класса? Классу то оно зачем? А пользователю класса зачем? Поиметь третюю пару Get(Set)LastError, что б было и было грамотно?
Это конечно по всей видимости не супер код, но и ничего криминального. В сущности нам такой и нужен был.
Я вообще его взял, только потому, что он мне последним попался на глаза.
// stdafx.h
// Ха-ха. На самом деле после этого - дальше можно не смотреть было бы-то, C++-ники называется. :)#define IfFailGo(x) { hr=(x); if (FAILED(hr)) goto Error; }
#define IfNullGo(p) { if(!p) {hr = E_FAIL; goto Error; } }
Код:
Скрытый текст
// CLRLoader.cpp#include"StdAfx.h"#include"clrloader.h"#include"metahost.h"// CLR 40 hosting interfaces
// When loading assemblies targeting CLR 4.0 and above make sure
// the below line is NOT commented out. It enables starting
// CLR using new hosting interfaces available in CLR 4.0 and above.
// When below line is commeted out the shim will use legacy interfaces
// which only allow loading CLR 2.0 or below.#define USE_CLR40_HOSTING
#ifdef USE_CLR40_HOSTING
static LPCWSTR g_wszAssemblyFileName =
L"MyAddin.dll";
#endif
using namespace mscorlib;
static HRESULT GetDllDirectory(TCHAR *szPath, DWORD nPathBufferSize);
CCLRLoader::CCLRLoader(void)
: m_pCorRuntimeHost(NULL), m_pAppDomain(NULL)
{
}
// CreateInstance: loads the CLR, creates an AppDomain, and creates an
// aggregated instance of the target managed add-in in that AppDomain.
HRESULT CCLRLoader::CreateAggregatedAddIn(
IUnknown* pOuter,
LPCWSTR szAssemblyName,
LPCWSTR szClassName,
LPCWSTR szAssemblyConfigName)
{
HRESULT hr = E_FAIL;
CComPtr<_ObjectHandle> srpObjectHandle;
CComPtr<ManagedHelpers::IManagedAggregator > srpManagedAggregator;
CComPtr<IComAggregator> srpComAggregator;
CComVariant cvarManagedAggregator;
// Load the CLR, and create an AppDomain for the target assembly.
IfFailGo( LoadCLR() );
// DebugBreak();
IfFailGo( CreateAppDomain(szAssemblyConfigName) );
// Create the managed aggregator in the target AppDomain, and unwrap it.
// This component needs to be in a location where fusion will find it, ie
// either in the GAC or in the same folder as the shim and the add-in.
IfFailGo( m_pAppDomain->CreateInstance(
CComBSTR(L"ManagedAggregator, PublicKeyToken=d51fbf4dbc2f7f14"),
CComBSTR(L"ManagedHelpers.ManagedAggregator"),
&srpObjectHandle) );
IfFailGo( srpObjectHandle->Unwrap(&cvarManagedAggregator) );
IfFailGo( cvarManagedAggregator.pdispVal->QueryInterface(
&srpManagedAggregator) );
// Instantiate and aggregate the inner managed add-in into the outer
// (unmanaged, ConnectProxy) object.
IfFailGo( pOuter->QueryInterface(
__uuidof(IComAggregator), (LPVOID*)&srpComAggregator) );
IfFailGo( srpManagedAggregator->CreateAggregatedInstance(
CComBSTR(szAssemblyName), CComBSTR(szClassName), srpComAggregator) );
Error:
return hr;
}
#ifdef USE_CLR40_HOSTING
// Convert "vN.N.N" into an array of numbersstatic void ParseClrVersion(LPCWSTR wszVersion, int rgiVersion[3])
{
rgiVersion[0] = rgiVersion[1] = rgiVersion[2] = 0;
LPCWCH pwch = wszVersion;
for (int i = 0; i < 3; i++)
{
// skip the firtst character - either 'v' or '.' and add the numbersfor (pwch++; L'0' <= *pwch && *pwch <= L'9'; pwch++)
rgiVersion[i] = rgiVersion[i] * 10 + *pwch - L'0';
if (*pwch == 0)
break;
assert ( *pwch == L'.' && L"we should expect a period. Otherwise it is not a proper CLR version string");
if (*pwch != L'.')
{
// the input is invalid - do not parse any furtherbreak;
}
}
}
// compare order of CLR versions represented as array of numbersstatic BOOL IsClrVersionHigher(int rgiVersion[3], int rgiVersion2[3])
{
for (int i = 0; i < 3; i++)
{
if (rgiVersion[i] != rgiVersion2[i])
return rgiVersion[i] > rgiVersion2[i];
}
return FALSE;
}
static HRESULT FindLatestInstalledRuntime(ICLRMetaHost* pMetaHost, LPCWSTR wszMinVersion, ICLRRuntimeInfo** ppRuntimeInfo)
{
CComPtr<IEnumUnknown> srpEnum;
CComPtr<ICLRRuntimeInfo> srpRuntimeInfo, srpLatestRuntimeInfo;
ULONG cFetched;
WCHAR rgwchVersion[30];
DWORD cwchVersion;
int rgiMinVersion[3]; //Major.Minor.Buildint rgiVersion[3]; // Major.Minor.Build
HRESULT hr = S_OK;
*ppRuntimeInfo = NULL;
// convert vN.N.N into an array of numbers
ParseClrVersion(wszMinVersion, rgiMinVersion);
IfFailGo( pMetaHost->EnumerateInstalledRuntimes(&srpEnum) );
while (true)
{
srpRuntimeInfo.Release();
IfFailGo( srpEnum->Next(1, (IUnknown**)&srpRuntimeInfo, &cFetched) );
if (hr == S_FALSE)
break;
cwchVersion = ARRAYSIZE(rgwchVersion);
IfFailGo( srpRuntimeInfo->GetVersionString(rgwchVersion, &cwchVersion) );
ParseClrVersion(rgwchVersion, rgiVersion);
if (IsClrVersionHigher(rgiVersion, rgiMinVersion) == FALSE)
continue;
rgiMinVersion[0] = rgiVersion[0];
rgiMinVersion[1] = rgiVersion[1];
rgiMinVersion[2] = rgiVersion[2];
srpLatestRuntimeInfo.Attach(srpRuntimeInfo.Detach());
}
if (srpLatestRuntimeInfo == NULL)
{
hr = E_FAIL;
goto Error;
}
hr = S_OK;
*ppRuntimeInfo = srpLatestRuntimeInfo.Detach();
Error:
return hr;
}
static HRESULT BindToCLR4OrAbove(ICorRuntimeHost** ppCorRuntimeHost)
{
HRESULT hr;
CComPtr<ICLRMetaHost> srpMetaHost;
CComPtr<ICLRRuntimeInfo> srpRuntimeInfo;
WCHAR rgwchPath[MAX_PATH + 1];
WCHAR rgwchVersion[30];
DWORD cwchVersion = ARRAYSIZE(rgwchVersion);
*ppCorRuntimeHost = NULL;
IfFailGo( CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (void**)&srpMetaHost) );
// Get the location of the hosting shim DLL, and retrieve the required
// CLR runtime version from its metadata
IfFailGo( GetDllDirectory(rgwchPath, ARRAYSIZE(rgwchPath)) );
if (!PathAppend(rgwchPath, g_wszAssemblyFileName))
{
hr = E_UNEXPECTED;
goto Error;
}
IfFailGo( srpMetaHost->GetVersionFromFile(rgwchPath, rgwchVersion, &cwchVersion) );
// First try binding to the same version of CLR the add-in is built against
hr = srpMetaHost->GetRuntime(rgwchVersion, IID_ICLRRuntimeInfo, (void**)&srpRuntimeInfo);
if (FAILED(hr))
{
// If we're here - it means the exact same version of the CLR we built against is not available.
// In this case we will just load the highest compatible version
srpRuntimeInfo.Release();
IfFailGo( FindLatestInstalledRuntime(srpMetaHost, rgwchVersion, &srpRuntimeInfo) );
}
// we ignore the result of SetDefaultStartupFlags - this is not critical operation
srpRuntimeInfo->SetDefaultStartupFlags(STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST, NULL);
IfFailGo( srpRuntimeInfo->GetInterface(CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (void**)ppCorRuntimeHost) );
Error:
return hr;
}
#endif// LoadCLR: loads and starts the .NET CLR.
HRESULT CCLRLoader::LoadCLR()
{
HRESULT hr = S_OK;
// Ensure the CLR is only loaded once.if (m_pCorRuntimeHost != NULL)
{
return hr;
}
#ifdef USE_CLR40_HOSTING
hr = BindToCLR4OrAbove(&m_pCorRuntimeHost);
#else
#pragma warning( push )
#pragma warning( disable : 4996 )
// Load the CLR into the process, using the default (latest) version,
// the default ("wks") flavor, and default (single) domain.
hr = CorBindToRuntimeEx(
NULL, NULL, STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST,
CLSID_CorRuntimeHost, IID_ICorRuntimeHost,
(LPVOID*)&m_pCorRuntimeHost);
#pragma warning( pop )
#endif// If CorBindToRuntimeEx returned a failure HRESULT, we failed to
// load the CLR.if (FAILED(hr))
{
return hr;
}
// Start the CLR.return m_pCorRuntimeHost->Start();
}
// In order to securely load an assembly, its fully qualified strong name
// and not the filename must be used. To do that, the target AppDomain's
// base directory needs to point to the directory where the assembly is.
HRESULT CCLRLoader::CreateAppDomain(LPCWSTR szAssemblyConfigName)
{
USES_CONVERSION;
HRESULT hr = S_OK;
// Ensure the AppDomain is created only once.if (m_pAppDomain != NULL)
{
return hr;
}
CComPtr<IUnknown> pUnkDomainSetup;
CComPtr<IAppDomainSetup> pDomainSetup;
CComPtr<IUnknown> pUnkAppDomain;
TCHAR szDirectory[MAX_PATH + 1];
TCHAR szAssemblyConfigPath[MAX_PATH + 1];
CComBSTR cbstrAssemblyConfigPath;
// Create an AppDomainSetup with the base directory pointing to the
// location of the managed DLL. We assume that the target assembly
// is located in the same directory.
IfFailGo( m_pCorRuntimeHost->CreateDomainSetup(&pUnkDomainSetup) );
IfFailGo( pUnkDomainSetup->QueryInterface(
__uuidof(pDomainSetup), (LPVOID*)&pDomainSetup) );
// Get the location of the hosting shim DLL, and configure the
// AppDomain to search for assemblies in this location.
IfFailGo( GetDllDirectory(
szDirectory, sizeof(szDirectory)/sizeof(szDirectory[0])) );
pDomainSetup->put_ApplicationBase(CComBSTR(szDirectory));
// Set the AppDomain to use a local DLL config if there is one.
IfFailGo( StringCchCopy(
szAssemblyConfigPath,
sizeof(szAssemblyConfigPath)/sizeof(szAssemblyConfigPath[0]),
szDirectory) );
if (!PathAppend(szAssemblyConfigPath, szAssemblyConfigName))
{
hr = E_UNEXPECTED;
goto Error;
}
IfFailGo( cbstrAssemblyConfigPath.Append(szAssemblyConfigPath) );
IfFailGo( pDomainSetup->put_ConfigurationFile(cbstrAssemblyConfigPath) );
// Create an AppDomain that will run the managed assembly, and get the
// AppDomain's _AppDomain pointer from its IUnknown pointer.
IfFailGo( m_pCorRuntimeHost->CreateDomainEx(T2W(szDirectory),
pUnkDomainSetup, 0, &pUnkAppDomain) );
IfFailGo( pUnkAppDomain->QueryInterface(
__uuidof(m_pAppDomain), (LPVOID*)&m_pAppDomain) );
Error:
return hr;
}
// GetDllDirectory: gets the directory location of the DLL containing this
// code - that is, the shim DLL. The target add-in DLL will also be in this
// directory.static HRESULT GetDllDirectory(TCHAR *szPath, DWORD nPathBufferSize)
{
// Get the shim DLL module instance, or bail.
HMODULE hInstance = _AtlBaseModule.GetModuleInstance();
if (hInstance == 0)
{
return E_FAIL;
}
// Get the shim DLL filename, or bail.
TCHAR szModule[MAX_PATH + 1];
DWORD dwFLen = ::GetModuleFileName(hInstance, szModule, MAX_PATH);
if (dwFLen == 0)
{
return E_FAIL;
}
// Get the full path to the shim DLL, or bail.
TCHAR *pszFileName;
dwFLen = ::GetFullPathName(
szModule, nPathBufferSize, szPath, &pszFileName);
if (dwFLen == 0 || dwFLen >= nPathBufferSize)
{
return E_FAIL;
}
*pszFileName = 0;
return S_OK;
}
// Unload the AppDomain. This will be called by the ConnectProxy
// in OnDisconnection.
HRESULT CCLRLoader::Unload(void)
{
HRESULT hr = S_OK;
IUnknown* pUnkDomain = NULL;
IfFailGo(m_pAppDomain->QueryInterface(
__uuidof(IUnknown), (LPVOID*)&pUnkDomain));
hr = m_pCorRuntimeHost->UnloadDomain(pUnkDomain);
// Added in 2.0.2.0, only for Add-ins.
m_pAppDomain->Release();
m_pAppDomain = NULL;
Error:
if (pUnkDomain != NULL)
{
pUnkDomain->Release();
}
return hr;
}
CCLRLoader::~CCLRLoader(void)
{
if (m_pAppDomain)
{
m_pAppDomain->Release();
}
if (m_pCorRuntimeHost)
{
m_pCorRuntimeHost->Release();
}
}
Здравствуйте, Sinclair, Вы писали:
S>Решительно непонятно, как же вернуться к стабильному состоянию. S>immutable
А как бы оно здесь помогло? У нас здесь изначально не-immutable API, которое скорей всего под капотом ещё и дёргает императивные системные вызовы.
S>и сборка мусора здесь сильно помогли бы: даже если у нас формирование меню было готово на 99%, и упала какая-то финальная операция, то мы возвращаем ошибку, а вся кунсткамера, "созданная" к этому моменту, как будто никогда и не существовала.
Сборка мусора более-менее подходит только для памяти, для остальных ресурсов не очень — именно поэтому во многих языках появляются конструкции для scope-based lifetime managmenet, а-ля using в C#, try-with-resources в Java, и with в Python, правда им всем до полноценного RAII далеко.