вторник, апреля 13, 2010
вторник, декабря 29, 2009
google prettify
Например, C#:
class A
{
public abstract void Foo();
}
или Python:
def select_odd(list):
return [n for n in list if n % 2 == 0]
Сама либа здесь. Я
вторник, ноября 10, 2009
Доступ к классам С++ из C#
Иногда есть желание использовать из C# код на плюсах (т.е. построенный на классах). Традиционно варианта три:
- использовать С++/CLI, чтобы завернуть native классы в классы CLR.
- Сделать (вручную или с помощью SWIG) обертку вокруг классов, и использовать P/Invoke для доступа к ним.
- оформить классы в виде COM-объектов.
Первый вариант напряжен тем, что требует наличия компилятора Visual C++ (реализации для mono, например, нет). Кроме того, C++/CLI – язык специфичный, ему нужно учиться (я как-то огреб утечку памяти, не разобравшись в разнице между !Class() и ~Class()).
Второй проблемен уже тем, что на выходе вы получите библиотеку с процедурами, оперирующими хендлами объектов, и, скорее всего, вам придется заново строить объектную модель вокруг всего этого. Удовольствие то еще.
Недостаток третьего подхода в том, что COM, вообще говоря, штука тяжелая, со своими требованиями к оформлению кода, вдобавок, обычно требующая регистрации в реестре (хотя это можно обойти). Достоинство – прямое отображение COM-интерфейсов на интерфейсы .NET.
Вот тут я прочитал про интересный подход, “скрещивающий” второй и третий варианты взаимодействия. Принцип следующий - С++ объект допиливается так, чтобы реализовывать IUnknown (т.е. иметь в начале vtable методы QueryInterface, AddRef, Release), но создается посредством фабричной функции, экспортированной из DLL.
Выглядит это примерно так (я разбил код на два класса, один из которых инкапсулирует работу COM, а другой – полезную логику). Обратите внимание, что мы накручиваем счетчик ссылок каждый раз, когда QueryInterface срабатывает успешно, а когда счетчик ссылок уменьшается до нуля, мы освобождаем память.
Также обратите внимание, что сигнатура метода ComputePi() имеет привычный вид, а не безобразие типа
HRESULT __stdcall ComputePi(float* pResult); Клиентский код выглядит ещё проще. Обратите внимание, что для поддержки не-COM-овской сигнатуры на методе выставлен атрибут [PreserveSig]. Значение атрибута [Guid] должно совпадать с GUID-ом, обрабатываемым в QueryInterface.
При загрузке .NET создаст прокси, называемый RCW (runtime callable wrapper), вызовет QueryInterface для заданного GUID’а, после чего станет возможным работать RCW как с обычным объектом.
Достоинства:
- минимум кода пишется с обоих сторон
- никакого реестра, регистрации и пр.
- кроссплафтоменно (работает и в MS.NET и в mono). Максимум – придется описать самому GUID для IUnknown.
Недостатки:
- Обычный С++ класс (не содержащий в начале vtable методы IUnknown) так использовать нельзя – система тупо рассчитывает на то, что vtable имеет заявленную структуру. На крайний случай, вы можете делегировать вызовы своему объекту.
- В стандартном COM-е коды HRESULT используются для сообщений об ошибках. При interop-е по ним строятся исключения .NET. Здесь такая схема не работает, нужно использовать свой механизм сигнализации об ошибках. Исключения С++, по крайней мере, в случае MinGW, приводят к завершению работы программы (думаю, в случае VC++ они могут сработать, но не тестил).
- Сложное межобъектное взаимодействие (передачу объектов в виде параметров) делать таким образом можно задолбаться. Но это общее место любого interop-а.
четверг, августа 20, 2009
Про google Guice
Оказывается, guice умеет разрешать циклы в зависимостях.
Т.е. если есть острое желание, например, построить связку Presenter и View, в которой оба класса зависят друг от друга, он это разрулит, построив прокси для того, кого ему не получается построить.
Забавно, Castle.Windsor, например, этого не делает принципиально. А Ninject вообще зависает на циклических зависимостях.
четверг, августа 13, 2009
Реализация закладки для элемента управления PropertyGrid
Одной из важных фич Syringe является возможность перехватывать события, порождаемые тестируемым элементом управления.
Я решил реализовать настройку перехватчиков средствами того же PropertyGrid’а, что и используется для редактирования свойств. Тогда интерфейс оказывается максимально похож на VisualStudio и, следовательно, не вызывает удивления.
Логично было вынести список событий на отдельную закладку.
Я попробовал использовать стандартную закладку EventsTab, но не осилил. Для своей инициализации она требует слишком много зависимостей, запрашиваемых неявно через родительский контейнер. Даже если бы я уже приделал DI-контейнер (зря я это откладываю, ох, зря) и отследил все обращения, все равно я оказался бы перед необходимостью реализовывать целую кучу интерфейсов типа IDesignerHost, IEventsBindingService и пр., функциональность которых мне не очень-то нужна.
Поэтому я решил реализовать свою закладку с нуля. Ну, не совсем =) На самом деле, у родительского класса абстрактными являются только свойство TabName и метод GetProperties.
Я реализовал GetProperties так, чтобы он возвращал по PropertyDescriptor’у для каждого события в тестируемом объекте (а список событий получил через TypeDescriptor), причем для этих дескрипторов указал в качестве редактора использовать список доступных логгеров (я их реализовал два – пишущий в лог и показывающий MessageBox). Адаптацию логгеров к разным типам событий была сделана еще раньше.
Я добавил свою закладку в PropertyGrid, и, о чудо… она не появилась. Выяснилось (при просмотре в Reflector’е), что, кроме реализации абстрактных методов, есть еще одно неочевидное условие: закладка должна что-то возвращать по запросу свойства Bitmap, которое используется для выбора иконки закладки! OMFG! Перекрыв это свойство я таки достиг успеха.
Так что теперь Syringe может перехватывать события (правда, пока не умеет нормально форматировать их аргументы), а пользователь может удобно выбрать тип перехватчика. Это первая (и очень полезная, ИМХО) фича, которой принципиально нет в UserControlTestContainer.
Моя доволен =)
вторник, августа 11, 2009
Упрощение реализации INotifyPropertyChanged
Объект, корректно реализующий интерфейс INotifyPropertyChanged оказывается “наблюдаемым” (observable), т.е. изменения его состояния могут легко отслеживаться подписчиками и, например, отображаться на визуальных компонентах в актуальной форме.
Однако при реализации этого интерфейса следует явным образом указывать строку - имя свойства, которое было модифицировано. Это чревато ошибками.
Опять же, довольно много кода приходится писать по нескольку раз.
Общественность =) неоднократно предлагала различные решения этой проблемы – от построения прокси-INotifyPropertyChanged вокруг POCO-объекта и патча бинарников с помощью PostSharp-а до различных структур, упрощающих привязку.
Я, естественно, пошел своим путем, т.к. во всех вариантах меня что-то не устраивало =)
В итоге остановился на том, что создал интерфейс и пару реализаций (для отдельного свойства и для списка), которые можно увидеть здесь, здесь и здесь. Пример использования можно увидеть здесь. Как видно, boilerplate-а поубавилось.
Недостатки: текущим реализациям надо передать имя свойства и их события надо перенаправить классу-модели.
Первое можно забороть с помощью Reflection-а (смотреть, какое свойство вызвало метод Set()), второе – сделать класс ModelBase, а в нем метод, настраивающий подписку для всех свойств разом.
Но я пока не решил, надо ли мне это =)
Fluent data binding
В .NET есть возможность привязывать свойства графических элементов к свойствам некоторого объекта-модели.
В результате привязки изменения в одном месте отражаются в другом. Например, редактирование текста в TextBox-е может автоматически обновлять поле Text в модели. Или изменение в модели флага , привязанного к свойству Enabled может блокировать элемент управления.
Сампл можно посмотреть здесь (показана только т.н. “простая привязка”, “сложная” делается ), хотя, уверен, ни для кого ничего фундаментально нового в этом нет.
Чем это хорошо? Хорошо это тем, что существенно уменьшается объем кода, связанного с отображением данных.
Чем это плохо? Плохо это тем, что свойства как модели, так и представления кодируются строками. Со всеми вытекающими отсюда следствиями, от которых и велемудрый R# не всегда защитит. Пытается защищать от этого графический редактор в VS, но, опять же, только частично (к тому же, я лично предпочитаю писать код, а не тыкать мышкой).
Работая над Syringe, я решил защититься от возможных проблем со строками и набросал небольшой набор классов и методов-расширений, обеспечивающих устойчивую к опечаткам и типобезопасную привязку.
Для получения получения информации о свойствах я решил использовать деревья выражений. Они контролируют типы объекта-аргумента и результирующего значения на этапе компиляции, однако не позволяют автоматически проверить, действительно ли в теле выражения указано свойство аргумента. Это приходится делать мне. Однако уже налицо прогресс, по сравнению с привязкой по строкам.
Пример можно увидеть здесь. Как видно, код стал компактней, при его написании сложнее ошибиться.
Реализацию можно посмотреть в исходниках проекта на google.code. Там же можно посмотреть и пример привязки к спискам данных.
четверг, июля 30, 2009
Syringe UserControl testing container
Это такая утилита для тестирования и отладки элементов управления (UserControl’ов) WindowsForms (возможно, потом и WPF).
Похожа на поставляемый с VS UserControlTestContainer. То есть пользователь может выбрать тип контрола, контрол будет создан на форме и пользователь имеет возможность всячески глумиться над ним, в том числе напрямую редактировать его публичные свойства.
Основное отличие – возможность “подсовывать” контролу его зависимости с помощью механизма Dependency Injection (поэтому проект и называется “шприц” =)). Предусмотрено два способа:
- регистрация зависимости в контейнере для контролов, получающих их через стандартный механизм Component.GetService();
- инъекция зависимостей при конструировании контрола (в конструктор и в свойства).
В результате появится возможность тестировать гораздо более самодостаточные компоненты.
Текущая версия 0.1 (наваянная вчера часа за два =)) почти точно соответствует UserControlTestContainer (если не считать багов =))
Проект живет здесь. Буду рад комментариям.
понедельник, июня 22, 2009
Про генерацию кода
Лично мне второй вариант кажется предпочтительней, особенно, если разработчик я. Как следствие, клиент и сервер работают с одними и теми же (а не с похожими) типами, проще делать mock-и для клиента, а вся PITA, связанная с установкой и поддержанием соединения решается с помощью Castle (там, конечно, тоже будет прокси, но с заявленными интерфейсом).А вот MS, как будто бы, поощряет первый подход. А что предпочитаете вы, коллеги (необязательно для случая WCF, я так понимаю, что подходы похожи для разных технологий RPC)?
среда, апреля 01, 2009
В частности, как утверждают, председатель Гугл Эрик Шмидт заявил, что объединение с одной из самых уважаемых компаний США пойдет на пользу его компании. В частности, их привлекают возможности ОС по удаленному администрированию.
В рамках слияния ряд продуктов получит новые названия. Примерами могут служить новый Google Apple Engine и Google Apple Juice.
Также общественности предъявлен новый логотип объединенной компании:
Image Hosting.
четверг, октября 16, 2008
Наличествующие сущности:
- Dispatcher - набор потоков, в которых исполняется код.
- DispatcherQueue - очередь заданий, исполняемых потоками диспетчера в режиме round-robin.
- Port - типизированный вход для отправки сообщений на обработку. Порты могут группироваться в PortSet'ы (например, для связывания с операциями, результатом которых может быть значениие либо exception).
- Arbiter - собственно, обработчик сообщений, помещающий при срабатывании соответствующую задачу в очередь. Т.е. Арбитры напоминают оператор receive в erlang'е, но обладают большим разнообразием: есть арбитры для получения одного/нескольких сообщений с одного порта, обработки событий с любого или со всех портов из набора (т.е. объединение событий по ИЛИ/по И).
Что особенно приятно, все это довольно очевидным связывается с более традиционной Asynchronous Programming Model (которая "BeginXXX/EndXXX").
Надо будет эту штуку попробовать, создается впечатление, что с ее помощью удобно реализовывать многопоточные сервисы... =)
четверг, сентября 04, 2008
Исправляюсь.
Пользовательские впечатления:
- красивенько (и интерфейс, и особенности рендеринга, типа подсветки активного элемента HTML);
- шустренько;
- маловато функциональности для меня (я привык к RSS-агрегатору, почтовому клиенту и детальным настройкам в Опере), но jedem das seine (
- пока глючно (чего стоит падение от сочетания символов %:);
- не проникся идеей ресайза одних только шрифтов;
- проверка орфографии для русского языка отстойна (или "отстой на", как она сама предложила =))
Технические впечатления:
- Необычен (но достоин уважения) подход по выделению закладок в отдельные процессы-sandbox'ы (хотя от вышеупомянутой баги не защищает ;));
- памяти кушает больше, чем Опера
- напрягает периодический ломёж браузера на гугловские сайты (видимо, с сообщениями о перемещениях пользователя, пока не понял);
- напряг пункт в EULA насчет передачи всего сформированного контента в длинные руки гугла, но говорят, что этот пункт отменен;
Мнение:
Цель создания этой программы, скорее всего, все-таки не создание еще одного FLOSS-браузера, а построение клиентской части гугловской платформы разработки Web-приложений (в особенности, работающих на Google App Engine). Т.о., Chrome конкурирует не с IE/Firefox/Safari/Opera, а с Silverlight/JavaFX/AIR. Об этом говорит наличие встроенного отладчика JavaScript и инспектора HTML, использование Gears, упор на быстродействие движка JS.
Такое вот ИМХО.
четверг, августа 28, 2008
работа с C# Expression trees
Например, потому что этот класс - параметр дженерик-метода. А у дженериков в качестве ограничения типа можно задать только конструктор без параметров, что не гуд.
Тогда приходится использовать reflection.
Например, через класс Activator.
Но использование reflection'а имеет некоторый (хотя и обычно терпимый) оверхед, плюс, оно просто не интересно =), поэтому можно попробовать иначе.
Воспользоваться так называемыми деревьями выражений, появившимися в .NET BCL 3.5. Они позволяют динамически строить и анализировать (как это делают провайдеры LINQ) AST выражений (в том числе, соответствующих делегатам). А потом выражения для делегатов можно откомпилировать в CIL (который затем JIT откомпилирует в native-код).
Самое лучшее - когда типы, используемые в выражении известны.
Тогда построения дерева выражения можно возложить на компилятор, написав что-то вроде:
Expression<Func<char[],string>> e = chars => new string(chars);Но если бы типы были известны, вообще делать было бы нечего. Поэтому нам придется строить дерево вручную. Что, в общем-то, тоже несложно.
Легко увидеть, что дерево выражения для данного конструктора имеет вид (в псевдокоде)
(lambda (chars)(new (get-constructor (typeof string) (typeof chars[])) chars)
В реальном коде на C# это будет выглядеть как:
var p = Expression.Parameter(typeof(char[]), "chars");
var lambda = Expression.Lambda<Func<string>>(
Expression.New(typeof(string).GetConstructor(typeof(char[]), p);
аналогично и для других конструкторов:
new T(params) ->
(lambda (params) (new
(get-constructor (typeof T) (map (lambda (p) (typeof p)) params)
params)и дерево будет выглядеть как
var parameters = (from t in argTypes
select Expression.Parameter(t, "@"+t))
.ToArray();
Expression.Lambda(
Expression.New(targetType.GetConstructor(argTypes), parameters),
parameters);N.B.: ВАЖНО, чтобы параметры в теле лямбда-функции совпадали с ее параметрами, т.к. компилятор ищет их именно по совпадению, а не по имени.
Как видно, реальный код хотя и отличается, но не очень значительно.
Я наваял небольшой класс, кэширующий скомпилированные конструкторы. Замеры показали, что однократная компиляция+извлечение из словаря обгоняют чистый рефлекшен где-то с 16 тыс. созданий объектов, скэшированный же явным образом результат вообще вне конкуренции, т.к. это просто вызов конструктора.
В общем, expression trees могут оказаться хорошей заменой System.Reflection.Emit или System.CodeDom.Compiler там, где надо сгенерировать код на лету, но до порождения CIL или генерации кода в виде текста напрямую спускаться не хочется.
При этом, возможности expression trees ограничены - в них сложно обеспечить последовательное выполнение команд (разве что используя для этого AndAlso) и обработку исключений, нельзя породить класс (не для этого они созданы) и пр.
Опять же - не для того они были сделаны =)
понедельник, января 29, 2007
сериализация сериализовалась-cериализовалась да и десериализовалась
Платформа: .NET
Очевидное решение: десериализация.
Неочевидная проблема: если у сборки, где определен сериализованный тип меняется строгое имя (strong name), десериализация объета невозможна.
Пояснение: Любой тип в .NET характеризуется именем, структурой И сборкой, в которой он определен. Это сделано целенаправленно, чтобы избежать глюков/дыр в связи с наличием нескольких типов, имеющих одинаковые имена.
Для идентификации сборки используется ее строгое имя, т.е. совокупность имени, версии и
публичного ключа автора/издателя.
Если у сборки изменить строгое имя, то при десериализации ранее сериализованного объекта окажется, что система просто не знает, где определен тип, к которому он принадлежит.
Решения:
а) подписывать сборки проекта как можно раньше и не менять публичный ключ.
б) не использовать стандартный механизм сериализации для хранения данных, если есть вероятность, что публичный ключ может быть изменен впоследствии.
Первый вариант, безусловно, наиболее предпочтительный. Но не всегда реальный. Увы.
Второй хорош для RealProgrammer'а, который считает злом использование готовых решений. Можно, скажем, ручками писать поля объектов в поток. Но при необходимости сериализовать объекты разных классов издержки оказываются слишком велики. Еще один вариант - XML-сериализация - может оказаться неприемлемым по всем тем причинам, по которым XML
оказывается менее предпочтительным, нежели бинарный формат.
Остается последний вариант
в) найти способ ручного управления выбором типов.
Оказалось, что третий путь (как обычно ;)) наиболее простой и эффективный (с т.зр. расширяемости). Для этого достаточно создать свою реализацию класса System.Runtime.Serialization.SerializationBinder.
Конкретно - перекрыть в нем метод BindToType.
class MyBinder: SerializationBinder{
public override Type BindToType(string assemblyName, string TypeName){
// логика выбора типа на основе имени сборки, версии и пр.
else return null;
}
}А затем задать экземпляр своего класса в качестве значения свойства Binder.
BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new MyBinder();
Bingo! Привязка осуществляется корректно.
Причем, насколько я понимаю, даже не обязательно реализовывать в привязываемом типе ISerializable, достаточно атрибута
[Serializable].
воскресенье, января 21, 2007
Потому как ЖЖ - он по сути своей всё-таки вещь для более гуманных проявлений жизнедеятельности, нежели сравнение кода, порождаемого компиляторам Хаскелля, Немерля и Волта...
Ну, вы поняли =)
Посему там будут только краткие отрывки из описания моих программных эскапад, а здесь - более развернутые рассказы.
воскресенье, сентября 17, 2006
По итогам вчерашних полночных бдений за связкой FarEditor-csc
2) Использование вышеперечисленного и generic-ов вообще в языке, не поддерживающем type-inference - акт мазохизма;
3) отсутствие механизма макросов в Шарпе начинает напрягать... избаловался, блин!
Юзаю Nemerle и жду третьего шарпа. Хотя зачем?
воскресенье, августа 27, 2006
Продолжая тему языков ;)
Краткая характерстика:
- "чистый" (без побочных эффектов) функциональный язык;
- по синтаксису - смесь урезанного Haskell'а с Java;
- как и Хаскелл, реализует очень своеобразный ООП;
- компилится под .NET 1.1 (под 2.0, ес-сно, тоже);
- предполагаемая ниша - ASP.NET-программирование;
Также в пакет входит и компилятор для Haskell под .NET (для работы требуется наличие компилятора GHC, точнее, видимо, его либ), правда, скорее препроцессор Haskell->Mondrian.
Впрочем, сам компилятор Mondrian порождает код на C#, так что компиляция с Haskell представляет собой компиляцию Haskell->Mondrian->C#->.NET. Что само по себе забавно :)
Впрочем, это еще детский сад.
Дело в том, что, насколько мой рассудок смог определить, программа на Mondrian выполняется в собственной виртуальной машине(!), реализованной в .NET(!!!).
По кр.м. программка HelloWorld трансформировалась в 50 шарповых строк, программка с двумя версиями вычисления факториала (рекурсивной и оконечно-рекурсивной) - в 300 с лишним (что утешает, т.к. заметно, что объем генерируемого кода растет нелинейно ;)).
Самый шок наступил, когда я заглянул в их ран-тайм библиотечку (исходники качать было в лом, посему заюзал мегаинструмент Reflector, декомпилирующий .NET-код в CIL, C#, VB, Delphi, MC++ и Chrome).
Так вот, эти извращенцы мало того, что наваяли свою ВМ, так они еще и написали, скажем, своb класс ы Vector, Stack и т.п. (что назыается, для внутреннего пользования), причем всякие разные перечисления элементов они смело реализовали сами, отлично от подхода, реализуемого в .NET, так как видимо, решили, что при переборе содержимого коллекции гораздо лучше написать
while (enumerator.hasMoreElements())
{
doStuffOn(enumerator.nextElement());
}
нежели
foreach(object in collection)
{
doStuffOn(object);
}
притом, что при нормальной реализации доступны будут оба варианта.
Ну и по мелочам: разницы между Int32 и Int64 компилятор видит с трудом (если видит), большое количество ошибок оставляет на откуп компилятору Шарпа.
Но есть и плюсы:
1) компилятор шустр (шустрее, чем, скажем, Nemerle; впрочем, учитывая интеллектуальность последнего, сравнивать некорректно)
2) синтаксис в чем-то привычнее, чем у Haskell'а;
и 3) проект закрыт!!!!!! Вот это не может не радовать!
на этой оптимистической ноте автор откланивается, предчувствуя, каким невыспанным он будет с утра.
Adieu!
четверг, августа 17, 2006
А я молодец!..
Причем, после написания проверил, нет ли чего-то такого уже готового ;) во фреймворке. Как ни странно, нет! Даже велосипеда не получилось :)
Теперь устанавливать связи между компонентами можно в режиме визуальной разработки. Приятно!
Причем для подключения мегадевайсины надо всего лишь установить атрибут. Правда, достаточно корявый, так что тут есть над чем работать.
четверг, июля 13, 2006
Загадки дизайна .NET Base Class Library
Простой пример: пытаюсь (в N-ый раз) построить что-то вроде конвейера, чтобы можно было параллельно обрабатывать поток изображений.
Ну и мне влом описывать классы для этих самых наборов промежуточных результатов. А так как я последнее время загоняюсь по ФП, сразу захотелось поюзать кортежи, ведь это стандартная фича всех ФЯП. Однако пишется проект на Шарпе, а не на Nemerle ;), соответственно, встроенной поддержки кортежей нет :( Стал искать нечто подобное в библиотеке .NET.
Не поверите, но нашел. Но отнюдь не в System.Collections.Generic, не в System.Collections и не в System.Collections.Specialized.
Знаете, где?
В System.Web.UI! %)
Причем, увы, использование требует явного приведения типов (ибо хранятся там System.Object'ы), и имеются в наличии только пары (Pair) и триплеты (Triplet). Чего мне, в принципе, могло бы и хватить, но добавлять ссылки на System.Web меня не радует, так что лучше напишу сам ;)
вторник, мая 23, 2006
Третий путь ведет к просветлению...
Только вот маленькая незадача: TlbImp не всегда корректно распознает типы. Т.е., если в COM-овской библиотеке типов был в качестве параметра unsigned char* (т.е., как правило, С-шный массив), то наивная утилита его представит как ref byte, а не как byte[]. Что, в общем-то, логично. Информацию о том, что сие есть, ей взять неоткуда. Или, скажем, вместо всенародно любимого винапишного HANDLE (который суть void* и которому в .NET соответстует IntPtr) везде оказался int.
Проблема №2: как известно, все COM-объекты реализуют интерфейс IUnknown. А посему стандартной является ситуация, при которой во все методы передается как раз IUnknown, а там представляемый им объект проверяется на прочие интерфейсы. Я вот лично считаю, что сие в общем случае есть антипаттерн. Ибо: 1) ликвидирует остатки типобезопасности; 2) код ни хрена не самодокументируемый (блин, я вот догадался, что IUnknown* pDibImage - это IDibImage pDibImage; а вот tlbimp - нет); 3) снижает расширяемость кода.
Но вернемся к нашим
Короче, задача звучит так: прикрутить либы к программе.
Варианты: 1) использовать "как есть". Отметен, как отстойный. Во-первых, придется постоянно делать приведение типов от Object к реальным типам объектов. А это суть кака. Во-вторых, если все методы, работающие, скажем, с хендлом окна будут требовать int, то я задолбаюсь писать MyWin.Handle.ToInt32(). И, что самое существенное, мне не хочется даже думать о том, как передать byte[] вместо ref byte. Скорее всего, это требует грязных извратов.
2) Написать обертку самому. Десяток классов (методов по 20), плюс реализуемые ими интерфейсы, которые, кажись, тоже придется описывать... Извиняйте, ломает.
3) Путь воина. ОтILDASMить сборку, поправить нужные места, сассемблировать ILASM'ом. Вот оно!!! Править нужно далеко не все, причем, в связи с регулярной структурой сборки, можно все это автоматизировать.
Попробовал. Понравилось. Особенно грело душу, когда после компиляции тестовая прога продолжала работать правильно. А ObjectBrowser в Студии показывал результаты моих трудов =)
Восторг - непередаваемый! Видно, что не совсем ишо тупой. Мелочь, а приятно...
Теперь осталось сочинить прожку, которая будет делать такую конверсию автоматически, на основании имен в венгерской записи. Должна же быть от этого израта хоть какая-то польза!! Главное - не забить ;)
З.Ы.: Все-таки я не хакер. Меня не заставишь лезть куда-то на нижний уровень, пока не припрет производственная необходимость. :)))