Жалко, что SizeParamIndex нельзя использовать в структурах, а то бывают структуры переменной длины. Ну это я уже привередничаю, конечно. :-) А просто SizeParamIndex используют так:
Тема очень интересна. Я пишу на .Net с версии 1.1 но только недавно стал обращать внимание на все эти хитрости и вкусности, так как перешел с VB и считал все это ужасно сложным и страшным, но все оказалось гораздо более легким в освоении и что очень важно очень полезным. Например сейчас я разрабатываю shell extension интеграцию в контекстное меню проводника, ожидал как обычно от всех программ на дот нете большую скорость загрузки, но ничего подобного не произошло, так что иногда можно)
Прошу автора подробнее раскрыть тему ;) Спасибо!
«Передача структур, указателей на функции и прочих нетривиальных вещей.» — вот про это хотелось бы прочитать поподробнее. Ибо в данный момент приходится ускоренными темпами изучать .NET и как раз столкнулся с проблемой вызова unmanaged Delphi функции из dll, которая возвращает результатам структуру (в Delphi это record). При вызове делегата вылетает в exception — Method's type signature is not PInvoke compatible. Надеюсь благодаря вашей следующей статье разберусь с этой проблемой :)
А за статью огромное спасибо.
Многое зависит от того, как делфи возвращает структуру. Попробую угадать и предложить три варианта:
1) by value (если размер её не превышает 8 байт)
И всё равно это не сильно упростит нам жизнь. Но можно попробовать описать возвращаемое значение как Int64 — по крайней мере, мы его получим. Дальше — либо через аналог union-ов, либо через BitConverter.
Случай маловероятный, поэтому код опущу.
2) by reference — возвращается указатель на временную область памяти.
Нам повезло! Описываем функцию как возвращающую IntPtr, а потом этот указатель скармливаем Marshal.PtrToStructure. И всё. :-)
3) в стиле COM — добавляется последний параметр типа T* или T**.
Пробуем описать функцию с дополнительным последним параметром типа ref T retval или ref IntPtr retval.
Гадание на кофейной гуще, конечно же. :-( Да простят меня дельфисты…
Хорошая статья. Спасибо. Мне, как человеку, съевшему на взаимодействии с unmanaged кодом не одну тонну говна проблем, это всё очень знакомо.
Хотелось бы, чтобы вы получше описали работу с хендлами в P/Invoke. Там всё очень интересно, и чтобы не получить Resource Leak, надо бы использовать всякие обертки над IntPtr. Людям, которые осваивают это, на мой взгляд очень полезно.
Кстати, в Windows Forms есть бага, выражающаяся в Resource Leak при работе с иконками, так что хочешь/не хочешь, а использовать P/Invoke надо :)
Насколько я помню, проблема не напрямую связана с NotifyIcon. Лезет она вроде из Bitmap.GetHIcon() — когда получаем хендл, с которым система уже ничего не может сделать (она не знает когда он освободиться). Но могу ошибаться. Просто помню, что хендлы там улетали только в путь, и все попытки освобождения всё равно заканчивались потерянным хендлом, который приходилось вычищать через P/Invoke.
Еще можно написать обертку на C++/CLI — это специальный язык в который родной двум средам — можно использовать как родные примеры кода C++ по использованию Unmanaged как есть так и реализовывать Managed классы.
Мне, также как и вам, много приходится работать с Interop, :-) поэтому добавлю свои пять копеек:
1) есть возможность не добавлять сборку в GAC для ComExport. Для этого при регистрации сборки через regasm.exe нужно указать ключи /codebase /tlb — этого будет достаточно.
Для релизов так делать не очень красиво, но для отладки или тестирования RC вполне можно. Ну или в случае, если referenced сборки не являются strong named.
2) однажды столкнулись со случаем, когда в Excel не получалось обратиться из VBA к COM-компоненту, написанному на C# (.NET 2.0). Выяснилось, что Excel использовал среду исполнения .NET 1.1, хотя в системе был весь набор фреймворков — от 1.1 до 3.5. Вылечилось установкой VSTO Runtime (хотя само VSTO не использовали).
Зато (в других наших проектах) без проблем получается использовать в новых фреймворках компоненты, написанные под 1.1. Доходит до курьёза: основное приложение написано на .NET CF 3.5, оно использует компоненту на .NET CF 2.0, а та юзает логгирование через log4net (.NET CF 1.1). И всё работает. :-)
Во-первых, спасибо за статью — еду сегодня на собеседование где будет крутиться затронутая вами тема.
Во-вторых — за нами гоняются клиенты с ссаными тряпками :) Действительно, всто увеличивает время загрузки ворда, но я с вами не согласен, что на такое большое количество времени. Другое дело, что мы это разрабатываем в рамках госпроекта, поэтому тут еще примешивается стыд перед Родиной.
В-третьих, отчего-то имеется острое желание напоить вас пивом в целях потырить знаний и поделиться своими:)
Спасибо, как раз не хватает материала, связанного с реализацией обращений из unmanaged кода к managed-подпрограммам. Обратный механизм достаточно прозрачен, и почитать есть что, а вот это как раз то, что нужно! Присоединяюсь ко всем, кому понравилось. Продолжайте, буду читать с удовольствием.
Замечания по переводу терминов. «Managed» и «unmanaged» код можно перевести как «управляемый» и «неуправляемый» код. «Boxing»/«unboxing» — «упаковка»/«распаковка». Такой перевод используется, например, в книге Джеффри Рихтера «Программирование на платформе Microsoft .NET Framework 2.0 на языке C#».
> Из этого подхода родилась общая архитектура подобного рода приложений, названная .NET Pipe RPC
хахахаха забавно, как раз реализую то же самое, тока не через pipes, а shared memory. правда при моем подходе, теоретически один внезапно отвалившийся клиент может завалить все оставшееся клиент-серверное взаимодействие (abort) например наложив spin блокировку на shared mem и внезапно умерев )) зато производительность должна быть повыше. ну и еще у меня не жестко rpc, а просто сообщения в свободном формате туда-сюда.
ComExport — не нашел описалова в сети, а вообще net classes экспортят com интерфейсы? ну тот же System.Net.Mail можно загрузить как com в unmanaged c++? увы, не смог найти туториала на это тему… хотя если рантайм при байдинге грузится, то это неприемлемо в любом случае…
спасибо за статью, прочитал с интересом. побольше бы таких адвансед статей и эдвансед пипл на хабре)
шаринг данных между процессами — это 50-летний велосипед как и их обработка кодом. ну и что :) проще написать, чем мучиться велосипед/не велосипед
фреймворочную часть всегда надо стараться быстрее пройти — работа над ней — это всегда своеобразный time trash, юзер все-таки требует не этого, а собственно, бизнес логику…… программу )
лучше расскажи про Pipe RPC как обещал в конце этой статьи ;-)
Штука это сложная, нетривиальная, и не дает никаких преимуществ перед out-process COM. Потому я от нее отказался, когда узнал, что .NET умеет делать outproc.
.NET в unmanaged окружении – использование и родовые проблемы