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

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

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

Есть ли документальные рекомендации, которые я могу последовать, что может помочь мне?

Мой инстинкт состоит в том, чтобы организовать статический/общий класс API Win/P/Invoke API, в котором перечислены все эти методы и связанные константы в одном файле... РЕДАКТИРОВАТЬ В DLL пользователя32 содержится более 70 импортных файлов.

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

Ответ 1

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

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