Разработка DLL на языке С++, страница 9

Вот вывод, который я получил от dumpbin.exe. Попытайтесь теперь понять причину неудачи.

Microsoft (R) COFF Binary File Dumper Version 6.00.8168

Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

Dump of file C:/Debug/MyDLL.dll

File Type: DLL

Section contains the following exports for MyDll.dll

0 characteristics

40A4A315 time date stamp Fri May 14 14:44:37 2004

0.00 version

1 ordinal base

12 number of functions

12 number of names

ordinal hint RVA      name

1    0 00012186 ??0Geometry@@QAE@HH@Z

2    1 0001287F ??0Geometry@@QAE@XZ

3    2 00012569 ??1Geometry@@QAE@XZ

4    3 000125DC ??4Geometry@@QAEAAV0@ABV0@@Z

5    4 000126F9 ?AllocMatrix@@YAXAAPAPANHH@Z

6    5 0001271C ?ClearMatrix@@YAXAAPAPANH@Z

7    6 000127CB ?Gauss@@YA_NPAPANPANH@Z

8    7 000126A4 ?Init@Geometry@@AAEXHH@Z

9    8 0001281B ?Say@Geometry@@QAEPADXZ

10    9 00012389 ?Solve@@YA_NPAVGeometry@@AAPAPANHH@Z

11    A 0003BB40 ?error@@3PADA

12    B 0003CF00 ?progress@@3HA

Summary

4000 .data

1000 .idata

5000 .rdata

2000 .reloc

24000 .text

11000 .textbss

Дело в том, что имена всех экспортируемых функций декорированы, поэтому они не могут быть вызваны по имени из управляемого кода. Неуправляемый код, как мы видели, прекрасно справляется с этой задачей, а управляемый — нет. Выхода два: отказаться от декораций или вызывать по порядковому номеру (см. в дампе колонку ordinal).

Опять сошлюсь на то, что время бежит неумолимо. В указанной выше статье приведено другое решение: Устранить декорирование имен автоматически (с помощью утилиты VisualDumpbin).

Второй способ напомнил мне о COM и Mighty Basic. Там для вызова функции с помощью метода Invoke, провозглашенного интерфейсом IDispatch, используется индекс типа DISPID. Суть та же, но DISPID'ами мы управляем одним способом, а этими номерками — другим. Например, мы можем их задать их в DEF-файле. Как вы помните, мы пошли другим путем — путем __declspec(dllexport), поэтому номерки присвоены функциям автоматически.

Немного о декорации. Например, функция void test() декорируется компилятором С++ следующим образом: ?test@@ZAXXZ (прелесть, неправда, ли?). А вот функция void __stdcall test() будет декорирована иначе: ?test@@YGXXZ (тоже красивая). Та же функция test, но с другим способом передачи параметров — через регистр (__fastcall) будет декорирована так: ?test@@YIXXZ. Если функция имеет совмещенную версию с другим набором параметров (например, void test(int)), то декорация опять изменится. В настоящее время нет стандарта на то, как декорировать имена, поэтому разные компиляторы делают это по-разному.

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

[DllImport("C:/Debug/MyDLL.dll", EntryPoint="#5",

CallingConvention=CallingConvention.StdCall)]

public unsafe static extern void AllocMatrix (ref double** a, int ny, int nx);

[DllImport("C:/Debug/MyDLL.dll", EntryPoint="#6")]

public unsafe static extern void ClearMatrix (ref double** a, int n);

[DllImport("C:/Debug/MyDLL.dll", EntryPoint="#7")]

public unsafe static extern bool Gauss (double** a, double[] x, int n);

Числа 5, 6 и 7 соответствуют декорированным именам, которые теперь не нужны для того, чтобы правильно определить точку входа. Атрибут CallingConvention можно опустить, он приведен с целью иллюстрации технологии. Запустите клиента, он должен работать.