Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WinForms, .Net Framework Новый топик    Ответить
 Как получить фактический адрес делегата?  [new]
iskatelsql
Member

Откуда:
Сообщений: 867
Есть замечательная статья, как можно использовать делегаты в качестве "callback"

https://docs.microsoft.com/ru-ru/dotnet/framework/interop/marshaling-a-delegate-as-a-callback-method

Но я так и не смог нагуглить, как получить адрес этого делегата, в смысле те циферки, что он передает функции.

Гуру, поделитесь Силой
30 апр 21, 00:56    [22316614]     Ответить | Цитировать Сообщить модератору
 Re: Как получить фактический адрес делегата?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6192
iskatelsql
Но я так и не смог нагуглить, как получить адрес этого делегата, в смысле те циферки, что он передает функции.Гуру, поделитесь Силой

Так адрес делегата, или циферки?
Если первое - то какой в этом вообще смысл? Его адрес - фактически адрес ссылки на класс делегата где-то в куче. Что вы с этим собираетесь делать? CLR сама выполнит маршаллинг этого делегата без всяких манипуляций с адресами, нужно просто передать его в unmanaged-метод как обычный делегат, точно так же, как это делается и для managed-методов.
Если второе - то эти циферки передаются в метод делегата в неуправляемом коде, на то он и коллбэк. Сами вы до них не доберётесь.
Вот простейший пример с винапишным EnumWindows:
public class Program
{
  static void Main()
  {
    foreach(var s in FindWindows("Visual"))
      Console.WriteLine(s);
  }

  static IEnumerable<string> FindWindows(string captionPart)
  {
    var found = new List<string>();
    // hwnd и lParam в делегат передаёт внутренняя реализация EnumWindows
    EnumWindows((hwnd, lParam) => 
    {
      var caption = GetWindowText(hwnd);
      if (caption.IndexOf(captionPart, StringComparison.Ordinal) != -1)
        found.Add(caption);
      return true;
    }, IntPtr.Zero);
    return found;
  }

  static string GetWindowText(IntPtr hWnd)
  {
    var size = GetWindowTextLength(hWnd);
    if (size <= 0) return string.Empty;
    var builder = new StringBuilder(size + 1);
    GetWindowText(hWnd, builder, builder.Capacity);
    return builder.ToString();
  }

  delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
  [DllImport("user32.dll", CharSet = CharSet.Unicode)]
  static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
  [DllImport("user32.dll", CharSet = CharSet.Unicode)]
  static extern int GetWindowTextLength(IntPtr hWnd);
  [DllImport("user32.dll")]
  static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
}

Microsoft Visual Studio
test (Running) - Microsoft Visual Studio
Visual Studio Application Management Window
30 апр 21, 07:01    [22316642]     Ответить | Цитировать Сообщить модератору
 Re: Как получить фактический адрес делегата?  [new]
fkthat
Member

Откуда:
Сообщений: 4880
Сон Веры Павловны
Так адрес делегата, или циферки?
Если первое - то какой в этом вообще смысл?

Особенно учитывая, что мусорщик в любой момент может все это передвинуть в другое место.
30 апр 21, 11:10    [22316755]     Ответить | Цитировать Сообщить модератору
 Re: Как получить фактический адрес делегата?  [new]
Где-то в степи
Member

Откуда: Под Таганрогом
Сообщений: 4370
имхо компилятор и может и заинлайнить если нет замыкания, тогда и искать нечего..
30 апр 21, 13:15    [22316856]     Ответить | Цитировать Сообщить модератору
 Re: Как получить фактический адрес делегата?  [new]
iskatelsql
Member

Откуда:
Сообщений: 867
Я понимаю, что при передаче делегата в качестве параметра с ним происходят некоторые преобразования, закрепление в памяти или что-то в этом роде. Удивительно что этого нельзя сделать вручную, столько инструментов в шарп понапихано...

Вообще я хочу впилить callback туда, где его изначально не задумано.
Т.е. я из нативной dll вызываю функцию f(), а она в своих недрах вызывает внутреннюю функцию x().
И я думал прямой записью в память поменять call x на call 'адрес делегата'

Ну можно конечно на сях написать обертку для той dll, объявив для f() еще один параметр - адрес callback и переписывать память из обертки... А из шарпа правда чтоль никак? Он ведь даже смешанные dll умеет...

Сообщение было отредактировано: 30 апр 21, 13:37
30 апр 21, 13:45    [22316882]     Ответить | Цитировать Сообщить модератору
 Re: Как получить фактический адрес делегата?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6192
Ну можно конечно попробовать Marshal.GetFunctionPointerForDelegate. Только как бы с таким цирком не получилось выстрелить себе в ногу.
30 апр 21, 13:58    [22316894]     Ответить | Цитировать Сообщить модератору
 Re: Как получить фактический адрес делегата?  [new]
iskatelsql
Member

Откуда:
Сообщений: 867
Сон Веры Павловны,

Вот это похоже то что нужно. Но смущает одна там ремарка:

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

Появился второй вопрос... Как это изобразить?

Сообщение было отредактировано: 30 апр 21, 14:03
30 апр 21, 14:11    [22316901]     Ответить | Цитировать Сообщить модератору
 Re: Как получить фактический адрес делегата?  [new]
Где-то в степи
Member

Откуда: Под Таганрогом
Сообщений: 4370
iskatelsql,
наверное можно пробовать в не безопасном режиме, ( указатели цементируются в контексте GC), но имхо прокси лучше.
компромисный вариант - как костыль..( не типичный код )
30 апр 21, 14:27    [22316913]     Ответить | Цитировать Сообщить модератору
 Re: Как получить фактический адрес делегата?  [new]
fkthat
Member

Откуда:
Сообщений: 4880
iskatelsql
Сон Веры Павловны,

Вот это похоже то что нужно. Но смущает одна там ремарка:

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

Появился второй вопрос... Как это изобразить?


https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/fixed-statement
30 апр 21, 14:30    [22316916]     Ответить | Цитировать Сообщить модератору
 Re: Как получить фактический адрес делегата?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6192
iskatelsql
Как это изобразить?

Просто следить, чтобы код, в котором живёт экземпляр делегата, не мог быть подобран мусурщиком - закреплять ссылку на делегат в виде поля класса, инкапсулирующий ссылку класс закреплять в виде поля в другом классе, итд итп. Например, вот здесь
class Foo
{
  public void Bar()
  {
    SomeAction func = n => Console.WriteLine("Passed: {0}", n);
    var ptr = Marshal.GetFunctionPointerForDelegate(func);
    Task.Run(() => Zot(ptr));
  }

  void Zot(IntPtr ptr)
  {
    /*some long operation */
    CallFromNative(ptr);
  }

  delegate void SomeAction(int n);
  [DllImport("mylib.dll")]
  static extern void CallFromNative(IntPtr ptr);
}

в релизной конфигурации к моменту вызова CallFromNative мусорщик уже может убить делегат после выхода из метода Bar, т.к. никакой managed-код эту ссылку не использует. И переданный ptr будет ссылаться на несуществующий метод. И будут сюрпризы - access violation, молчаливый вылет, итд. Это именно то, что я имел в виду выше под выстрелом в ногу.
30 апр 21, 14:41    [22316926]     Ответить | Цитировать Сообщить модератору
 Re: Как получить фактический адрес делегата?  [new]
ЕвгенийВ
Member

Откуда: Москва
Сообщений: 4986
fkthat
iskatelsql
Сон Веры Павловны,

Вот это похоже то что нужно. Но смущает одна там ремарка:

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

Появился второй вопрос... Как это изобразить?


https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/fixed-statement

https://docs.microsoft.com/ru-ru/dotnet/api/system.runtime.interopservices.gchandle?view=net-5.0
30 апр 21, 14:47    [22316930]     Ответить | Цитировать Сообщить модератору
 Re: Как получить фактический адрес делегата?  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6192
Оказывается (это к вопросу про выстрел в ногу) у Marshal.GetFunctionPointerForDelegate и последующего вызова Marshal.GetDelegateForFunctionPointer есть интересные сайд-эффекты (да, это запрещено в документации, а на практике - ради б-га, на свой страх и риск).
14 май 21, 20:28    [22322411]     Ответить | Цитировать Сообщить модератору
Все форумы / WinForms, .Net Framework Ответить