Невозможно использовать/отладить неуправляемую DLL из С#

У меня есть приложение C# и DLL C++, оба x86. Приложение — это стартовый проект, проект DLL находится внутри того же решения и упоминается как проект. DLL C++ выводит свой файл PDB с тем же именем, что и DLL внутри папки Debug приложения.

У меня есть функция с именем SomeFunction, которую я пытаюсь выполнить на С#. Когда код достигает этой строки, он останавливается на ней. Пользовательский интерфейс приложения C# продолжает реагировать, но точка останова никогда не покидает эту строку (и это в Form_Load).

Если я попытаюсь установить DLL в качестве запускаемого проекта и указать ей выполнить приложение C#, то C# завершится сбоем в этой строке с: System.EntryPointNotFoundException: невозможно найти точку входа с именем «SomeFunction» в DLL «SomeDLL.dll».

Это объявление функции, которую я пытаюсь вызвать:

[DllImport("SomeDLL.dll", EntryPoint = "SomeFunction", CallingConvention = CallingConvention.Cdecl)]
public static extern int SomeFunction(IntPtr hwnd);

Это объявление функции из заголовка C++:

#define MYDLL_API __declspec(dllexport)
MYDLL_API extern int SomeFunction(HWND hWnd);

Вот как я называю это из С#:

var someAnswer = SomeFunction(_hwnd);

ОБНОВЛЕНИЕ: поскольку приведенное ниже обсуждение может занять некоторое время, вот ответ в двух словах: мне не хватало внешнего "C". Кроме того, для отладки DLL (что тоже было проблемой) проект должен поддерживать нативную отладку как с C++, так и с C#, вот отличный список, который я прошел, и в конце все было хорошо!

Нет символов, загруженных в смешанном С# C (win32) проект с использованием VS2010


person Axonn    schedule 07.08.2016    source источник
comment
Вы пытались вызвать это SomeFunction во время отладки? (.net может динамически загружать С++ dll)   -  person Dmitriy Zapevalov    schedule 07.08.2016
comment
Добавьте ::DebugBreak() в dll. Это приведет к остановке отладчика и позволит вам установить точки останова. См. msdn.microsoft.com/ en-gb/library/windows/desktop/   -  person Richard Critten    schedule 07.08.2016


Ответы (2)


  1. Запустите код, который вызывает SomeFunction, чтобы обеспечить загрузку неуправляемой .dll.
  2. Перейдите в Debug -> Windows -> Modules и проверьте путь .dll и статус symbols. Вы можете попробовать загрузить symbols в контекстное меню вашей библиотеки С++ в этом окне.

(очень часто бывает, что .net загружает другую (не ожидаемую) версию неуправляемой .dll)

Теперь я почти уверен, что проблема в том, что имя вашей экспортируемой функции искажено. Попробуйте определить функцию следующим образом:

extern "C" MYDLL_API int PASCAL SomeFunction(HWND hWnd);

In C#:

[DllImport("SomeDLL.dll")]
public static extern int SomeFunction(IntPtr hwnd);
person Dmitriy Zapevalov    schedule 07.08.2016
comment
Я пытаюсь вызвать функцию. Точка останова никогда не переходит от этой строки в C#. На самом деле, DLL даже не отображается в модулях, что указывает на то, что она даже не загружена? Я не понимаю, почему это происходит. Он должен аварийно завершать работу при вызове функции. DLL есть в папке, почему не загружается?! - person Axonn; 07.08.2016
comment
.dll будет загружен только при первом вызове. .net вызывает LoadModule и так далее. Каково сообщение об аварии? Вы также можете настроить точку останова в функции Main dll. Таким образом, он сможет остановиться до фактического вызова функции .dll. Также посмотрите на окно Output перед сбоем и пришлите нам. - person Dmitriy Zapevalov; 07.08.2016
comment
Не забывайте, что .exe и .dll должны находиться в одной и той же папке и все зависимости .dll должны быть доступны. - person Dmitriy Zapevalov; 07.08.2016
comment
DLL использует некоторый Win32 API из ядра и т. д., но я полагаю, что мне не нужно копировать их в ту же папку, не так ли? В противном случае да, они находятся в одной и той же папке, но теперь при попытке запустить C# из DLL с DLL в качестве стартового проекта я получаю: System.EntryPointNotFoundException: не удалось найти точку входа с именем «SomeFunction» в DLL 'SomeDLL.dll'. Я просто не понимаю, почему это происходит... - person Axonn; 07.08.2016
comment
Может быть, вы забыли о искажении имени? Действительно ли SomeFunction экспортируется? (Вы можете проверить с помощью DependencyWalker или добавить extern "C" перед объявлением функции) - person Dmitriy Zapevalov; 07.08.2016
comment
Я смотрю с помощью Total Commander, и это действительно имя функции, которая экспортируется. То же самое в файле .lib. Я попытался добавить extern, и происходит то же самое :(. - person Axonn; 07.08.2016
comment
Давайте продолжим обсуждение в чате. - person Dmitriy Zapevalov; 07.08.2016
comment
Это было из-за отсутствия extern C :). Еще раз спасибо! О, и помните, что CallingConvention = CallingConvention.Cdecl? Это действительно необходимо. Это необходимо, потому что я передаю IntPtr как hwnd. Без этого он вылетает во время отладки с вызовом, который разбалансировал стек. Вероятно, это связано с тем, что управляемая подпись PInvoke не соответствует неуправляемой целевой подписи. - person Axonn; 08.08.2016

Я подозреваю, что проблема в том, что он не установит точку останова заранее, заключается в том, что DLL загружается по запросу через p-invoke. Если VS не считает, что DLL является зависимостью, она может не позволить вам заранее установить точку останова.

Вы можете добавить DebugBreak к вашему коду С++. Затем просто запустите свое приложение, не отлаживайте его. Затем, когда он выполнит этот оператор, вы получите оповещение Оперативная отладка, позволяющее перейти к отладчику C++.

C++ как стартап

В качестве альтернативы вы можете сделать DLL стартовым проектом, но изменить параметры отладки так, чтобы она запускала внешний процесс — в данном случае ваше приложение .exe.

Однако на этот раз вам не нужен DebugBreak(), а только обычная точка останова.

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

Я часто использовал этот трюк с ActiveX/COM.

person MickyD    schedule 07.08.2016
comment
Я пробовал использовать DebugBreak непосредственно в DLL Main. BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {DebugBreak();}. Ничего не произошло. Как я ответил ниже Дмитрию, кажется, что DLL какая-то недоступна... пока не понимаю, как и почему. Сейчас попробую то, что вы предложили. - person Axonn; 07.08.2016
comment
Я делаю что-то очень неправильно... где-то. Я просто не знаю, где. Я попробовал то, что вы сказали, и теперь мне удается получить точку останова в DllMain даже без DebugBreak. Оно работает. Однако, когда С# собирается вызвать функцию, она прерывается: System.EntryPointNotFoundException: невозможно найти точку входа с именем «SomeFunction» в DLL «SomeDLL.dll». - person Axonn; 07.08.2016
comment
Здесь кое-что похуже... Я даже не могу получить доступ к DLL из C#. Я доберусь до сути и обновлю вопрос соответственно. - person Axonn; 07.08.2016
comment
Не волнуйтесь. возможно, проверьте подпись p-invoke. Вы также можете убедиться, что функция С++ экспортируется правильно, просмотрев DLL в средстве просмотра зависимостей. Удачи - person MickyD; 08.08.2016