Обновить
Комментарии 32
очень познавательно, спасибо.

unmanaged еще позволяет sizeof для reference type обьектов использовать
Не совсем по теме, как говорится, навеяло…

Жалко, что SizeParamIndex нельзя использовать в структурах, а то бывают структуры переменной длины. Ну это я уже привередничаю, конечно. :-) А просто SizeParamIndex используют так:

int CeRegQueryValueEx(
IntPtr hKey,
[In, MarshalAs(UnmanagedType.LPWStr)] String lpValueName,
IntPtr lpReserved,
[In, Out, MarshalAs(UnmanagedType.I4)] ref RegType lpType,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 5)] byte[] lpData,
[In, Out] ref int lpcbData);

Таким образом, после вызова функции среда копирует в наш массив, переданный в lpData, только lpcbData байт.
Кстати, метод описания передачи массивов переменной длины аналогичен таковому для языка IDL.

Там тоже в описании интерфейсов используется атрибут-указатель на размер данных.
Отличный пост! Достаточно глубоко затронута серьёзная и важная тема. С нетерпением жду следующей статьи. Спасибо.
Тема очень интересна. Я пишу на .Net с версии 1.1 но только недавно стал обращать внимание на все эти хитрости и вкусности, так как перешел с VB и считал все это ужасно сложным и страшным, но все оказалось гораздо более легким в освоении и что очень важно очень полезным. Например сейчас я разрабатываю shell extension интеграцию в контекстное меню проводника, ожидал как обычно от всех программ на дот нете большую скорость загрузки, но ничего подобного не произошло, так что иногда можно)
Прошу автора подробнее раскрыть тему ;) Спасибо!
При маршалинге сложных гитик всегда можно выполнить упаковку и распаковку обьекта вручную, используя низкоуровневые методы Masrhal.
«Передача структур, указателей на функции и прочих нетривиальных вещей.» — вот про это хотелось бы прочитать поподробнее. Ибо в данный момент приходится ускоренными темпами изучать .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.

Гадание на кофейной гуще, конечно же. :-( Да простят меня дельфисты…
Спасибо. Отлично собрали воедино большой материал по теме.
Интересно почитать про .NET Pipe RPC. Продолжайте, пожалуйста.
Хорошая статья. Спасибо. Мне, как человеку, съевшему на взаимодействии с unmanaged кодом не одну тонну говна проблем, это всё очень знакомо.
Хотелось бы, чтобы вы получше описали работу с хендлами в P/Invoke. Там всё очень интересно, и чтобы не получить Resource Leak, надо бы использовать всякие обертки над IntPtr. Людям, которые осваивают это, на мой взгляд очень полезно.
Кстати, в Windows Forms есть бага, выражающаяся в Resource Leak при работе с иконками, так что хочешь/не хочешь, а использовать P/Invoke надо :)
Это случайно не та, которая не релизит иконку в NotificationIcon?
Насколько я помню, проблема не напрямую связана с NotifyIcon. Лезет она вроде из Bitmap.GetHIcon() — когда получаем хендл, с которым система уже ничего не может сделать (она не знает когда он освободиться). Но могу ошибаться. Просто помню, что хендлы там улетали только в путь, и все попытки освобождения всё равно заканчивались потерянным хендлом, который приходилось вычищать через P/Invoke.
Был бы признателен за примеры кода, поковырять, т.к. в этой сфере я совсем неспециалист :(
Еще можно написать обертку на C++/CLI — это специальный язык в который родной двум средам — можно использовать как родные примеры кода C++ по использованию Unmanaged как есть так и реализовывать Managed классы.
Это тот, который раньше назывался Managed C++ (C++ with managed extensions?)

Кстати, мнение о нем почему-то очень противоречивое. Есть подозрение, что там тоже не все гладко.
MС++ — это старое название, я им немного баловался, никаких глюков не обнаружил. С++/CLI — новое название
Хорошая статья, спасибо.

Мне, также как и вам, много приходится работать с 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++? увы, не смог найти туториала на это тему… хотя если рантайм при байдинге грузится, то это неприемлемо в любом случае…

спасибо за статью, прочитал с интересом. побольше бы таких адвансед статей и эдвансед пипл на хабре)
Если прочитаешь мою следующую статью — поймешь, что это велосипед. Я это уже понял.

Там и про ComExport кстати подробно написано. NET-классы легко и непринужденно экспортирую ком-интерфейсы. И про outproc тоже.
шаринг данных между процессами — это 50-летний велосипед как и их обработка кодом. ну и что :) проще написать, чем мучиться велосипед/не велосипед

фреймворочную часть всегда надо стараться быстрее пройти — работа над ней — это всегда своеобразный time trash, юзер все-таки требует не этого, а собственно, бизнес логику…… программу )

лучше расскажи про Pipe RPC как обещал в конце этой статьи ;-)
Штука это сложная, нетривиальная, и не дает никаких преимуществ перед out-process COM. Потому я от нее отказался, когда узнал, что .NET умеет делать outproc.
в моем случае я не уверен, что в DB2 C++ хранимках получится юзать outprocess COM, поэтому пру со своим велосипедом до конца! 8)

aint it fun when you just cant seem to find your tounge
coz you stuck it too deep into something that really stung
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.