Рекомендации по организации кода .NET P/Invoke для Win32 API

Я занимаюсь рефакторингом большой и сложной базы кода в .NET, в которой интенсивно используются P/Invoke to Win32 API. Структура проекта не самая лучшая, и я повсюду нахожу операторы DllImport, которые очень часто дублируются для одной и той же функции, а также объявляются различными способами:

Директивы и методы импорта иногда объявляются как общедоступные, иногда как частные, иногда как статические, а иногда как методы экземпляра. Меня беспокоит то, что рефакторинг может иметь непредвиденные последствия, но это может быть неизбежно.

Существуют ли задокументированные рекомендации, которым я могу следовать и которые могут мне помочь?

Мой инстикт состоит в том, чтобы организовать статический/общий класс Win32 P/Invoke API, в котором перечислены все эти методы и связанные с ними константы в одном файле... EDIT Существует более 70 операций импорта в user32 DLL.

(База кода состоит из более чем 20 проектов с большим количеством передачи сообщений Windows и вызовов между потоками. Это также проект VB.NET, обновленный с VB6, если это имеет значение.)


person Paul Sasik    schedule 12.03.2010    source источник


Ответы (6)


Вы могли бы подумать, как это было сделано в среде .NET. Он неизменно объявляет статический класс (модуль в VB.NET) с именем NativeMethods, который содержит объявления P/Invoke. Вы могли бы быть более организованными, чем программисты Microsoft, здесь много повторяющихся объявлений. Разные команды работают над разными частями фреймворка.

Однако, если вы хотите поделиться этим со всеми проектами, вы должны объявить эти объявления Public вместо Friend. Что не очень хорошо, это должна быть деталь реализации. Я думаю, вы можете решить эту проблему, повторно используя файл исходного кода в каждом проекте, который в нем нуждается. Обычно это табу, но в данном случае, я думаю, все в порядке.

Я лично объявляю их по мере необходимости в файле исходного кода, которому они нужны, делая их частными. Это также очень помогает при лжи о типах аргументов, особенно для SendMessage.

person Hans Passant    schedule 12.03.2010
comment
Спасибо всем за отличное обсуждение. Жаль, что я могу. Выберите только одного победителя. Спасибо nobugz за очень практические соображения. - person Paul Sasik; 12.03.2010

Организуйте их в класс [Safe|Unsafe]NativeMethods. Отметьте класс как internal static. Если вам нужно предоставить их своим собственным сборкам, вы можете использовать InternalsVisibleTo, хотя было бы более уместно, если бы вы могли сгруппировать связанные в каждую сборку.

Каждый метод должен быть static - я, честно говоря, не знал, что вы можете даже пометить методы экземпляра DllImport.

В качестве первого шага я бы, вероятно, переместил все в сборку Core (если она у вас есть) или создал сборку Product.Native. Тогда вы сможете легко находить дубликаты и перекрытия и искать управляемые эквиваленты. Если ваши p/invokes в беспорядке, я не подозреваю, что у вас есть много способов наслоения других сборок, которые будут направлять вашу группировку.

person Mark Brackett    schedule 12.03.2010

Почему бы не создать отдельный файл с именем Win32.vb и внутри него логически сгруппировать пинвоки в отдельные пространства имен, например, пространство имен GDI может использовать все пинвоки GDI, пространство имен User32 может использовать все пинвоки, находящиеся в ядре User32, и так далее. .. сначала это может быть болезненно, но, по крайней мере, у вас будет централизованное пространство имен, содержащееся в этом файле? Посмотрите здесь, чтобы понять, что я имею в виду... Что вы думаете?

person t0mm13b    schedule 12.03.2010
comment
+1 Отличная ссылка! Определенно отличный пример того, что я мог бы захотеть сделать. - person Paul Sasik; 12.03.2010

Являются ли ваши вызовы P/Invoke артефактом миграции с VB6? Я перенес 300 000 строк кода с VB6 на C# (Windows.Forms и System.EnterpriseServices) и исключил все, кроме нескольких вызовов P/Invokes — почти всегда есть управляемый эквивалент. Если вы занимаетесь рефакторингом, вы можете подумать о том, чтобы сделать что-то подобное. Полученный код должен быть проще в обслуживании.

person Polyfun    schedule 12.03.2010
comment
Не только проще в обслуживании в долгосрочной перспективе, но и более производительный. - person Nick; 12.03.2010
comment
Некоторые вызовы являются артефактами, а некоторые нет. Я действительно заменяю управляемый код, но останется значительное количество p/invoke. - person Paul Sasik; 12.03.2010
comment
Даже P/Invokes, которые были в исходном коде VB6 (Declares), часто можно заменить управляемым эквивалентом. - person Polyfun; 12.03.2010
comment
@nobugs - это зависит от метода. Кроме того... постоянное перемещение туда-сюда должно быть связано с затратами. - person Nick; 12.03.2010

Рекомендуемый способ — иметь класс NativeMethods для каждой сборки со всеми методами DllImported с внутренней видимостью. Таким образом, вы всегда знаете, где находится ваша импортированная функция, и избегаете повторяющихся объявлений.

person munissor    schedule 12.03.2010
comment
Звучит интересно. Не могли бы вы дать ссылку или две на некоторые источники. - person Paul Sasik; 12.03.2010
comment
FxCop предлагает сделать это. of-movepinvoketonativemethodsclass.aspx" rel="nofollow noreferrer">blogs.msdn.com/fxcop/archive/2007/01/14/ - person munissor; 15.03.2010

Что я обычно пытаюсь сделать в этом случае, так это делать то, о чем вы говорите, создавать различные классы, статические или нет, которые обеспечивают функциональность, таким образом, ее можно повторно использовать по мере необходимости. В зависимости от характера вызовов я бы уклонился от реализации статического класса, но это будет зависеть от вашей конкретной реализации.

Расширение выше по запросу.

Учитывая характер P/Invoke, особенно если требуется несколько вызовов и они относятся к разным областям реализации, я считаю, что лучше группировать похожие элементы вместе, таким образом, вы не втягиваете много других беспорядков или других импортов DLL. когда не нужен.

Желание держаться подальше от статических методов связано с вызовами неуправляемых ресурсов и возможностью утечки памяти и т. д.

person Mitchel Sellers    schedule 12.03.2010
comment
Митчел, не могли бы вы расширить свой ответ? Зачем создавать разные классы вместо одного? И почему статическая реализация менее предпочтительна? - person Paul Sasik; 12.03.2010