вступление

Как вы знаете, .NET предоставляет вызов платформы, который вызывает неуправляемые вызовы через область CLR. Однако эта операция очень опасна и, как правило, не рекомендуется, поскольку она может вызвать утечку памяти, поэтому управление должно выполняться безупречно, поскольку сборщик мусора отключен.

P/Invoke часто не является разумной стратегией. И если это реализовано неправильно, затраты намного превышают выгоды. Неважно, что вы пытаетесь вызвать из неуправляемого кода. CLR должна быть на первом месте и использовать преимущества обработки исключений, сборщика мусора и других функций…

Однако иногда мы не можем найти то, что ищем в экосистеме .NET, или стоимость реализации может подтолкнуть нас к риску, достаточному для вызова из неуправляемого кода. В этом случае лучше всего использовать разные платформы (Golang, Ruby, NodeJS и т. д.) в качестве еще одного сервиса. Однако, если ваша архитектура и структура дистрибутива не подходят для этого, в таких сценариях P/Invoke позволяет вам иметь возможности без повторной реализации функциональности в другой библиотеке.

Поток

1. Объявление подписи целевой функции с использованием атрибута DllImport, чтобы он предоставлял среде выполнения .NET метаданные о вызываемой нативной функции. Вы можете определить атрибут с различными полями.

[DllImport(“custom.dll”, CharSet = CharSet.Unicode)]

2- Необходимо тщательно объявить и сопоставить типы данных, используемые в собственной функции, с соответствующими управляемыми эквивалентами. Это обеспечивает правильное распределение данных между управляемым и неуправляемым кодом.

//Unmanaged code
extern “C” __declspec(dllexport) void __stdcall FreeDynamicString(char* dynamicString)
//Managed code
[DllImport("GetDynamicString.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void FreeDynamicString(IntPtr dynamicString);

3- Связанные .dll должны быть определены. Ввод пути напрямую уменьшит сложность кода. В противном случае перед выполнением выполняется поиск .dll в определенных местах (каталог приложения, системные каталоги и т. д.), а затем выдается ошибка, если не удалось найти.

4- Определите правильные параметры ранжирования. Среда выполнения .NET обрабатывает маршаллинг параметров между управляемым и неуправляемым кодом в операциях P/Invoke. Это включает в себя приведение типов данных, управление памятью и т. д.

5- Вызвать определенную неуправляемую функцию как управляемую функцию с помощью P/Invoke. Среда выполнения .NET обрабатывает базовый процесс перехода от управляемого к неуправляемому коду, выполняя собственную функцию в неуправляемой области и возвращая управление обратно управляемому коду.

Когда .NET возвращает результат вызова неуправляемой функции. Это могут быть результаты функций, типы значений, ссылочные типы, указатели и даже ошибки!

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

Пример

В следующем примере C++ у нас есть две функции.

Получить динамическую строку

  • Выделяет память для строки.
  • Копирует сообщение в него.
  • Возвращает указатель на динамически выделенную строку.

Свободная динамическая строка

  • Проверяет, является ли dynamicString обнуляемым или нет.
  • Освобождение памяти, выделенной для строки.
#include <Windows.h>

extern “C” __declspec(dllexport) char* __stdcall GetDynamicString()
{
  const char* message = “Hello from custom DLL”;
  int length = strlen(message) + 1;
  char* dynamicString = (char*)malloc(length * sizeof(char));
  strcpy_s(dynamicString, length, message);
  return dynamicString;
}

extern “C” __declspec(dllexport) void __stdcall FreeDynamicString(char* dynamicString)
{
  if (dynamicString != nullptr)
  {
   free(dynamicString);
  }
}

Итак, после операции сборки мы вызывали .Net из скомпилированной .dll

using System;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("GetDynamicString.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern IntPtr GetDynamicString();

    [DllImport("GetDynamicString.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void FreeDynamicString(IntPtr dynamicString);

    static void Main()
    {
        IntPtr dynamicStringPtr = GetDynamicString();
        string dynamicString = Marshal.PtrToStringAnsi(dynamicStringPtr);
        Console.WriteLine("Dynamic String: " + dynamicString);

        FreeDynamicString(dynamicStringPtr);
    }
}

Также возможно предоставить неуправляемый код с помощью метода объектной модели компонентов (COM), отличного от P/Invoke.

Ссылки :