я столкнулся с проблемой что в моей dll библиотеке...
创建于:2025年9月3日
使用 GPT-5 Thinking 回答 Chat01
创建于:2025年9月3日
使用 GPT-5 Thinking 回答 Chat01
я столкнулся с проблемой что в моей dll библиотеке есть экспортируемая функция и она в свою очередь использует функцию которая использует ещё одну функцию и каждая из этих функций как принимает так и передаёт аргументы - проблема в том что при таком уровне вложенности я получаю SEH исключение в вызывающей стороне, вызывающая сторона - это управляемый код и дело 100 процентов именно в этом, я пробовал передавать аргументы по ссылкам но результат тот же, ещё раз повторюсь что 100 процентов дело именно в этом так как все остальные участки кода я идеально отладил и протестировал в консольном приложении и там всё работает идеально - проблема возникает именно когда делаю функцию экспортитруемой. И управляемый код и нативный код dll библиотеки на c++/c# используют соглашение о вызовах __cdecl
Похоже, это типичный P/Invoke-казус: стек/ABI или маршалинг где-то не совпадают, а проявляется это «на выходе» из экспортируемой функции как SEHException в управляемой стороне. Глубина вложенности вызовов тут, почти наверняка, ни при чём — причина обычно в подписи (calling convention, размеры типов, упаковка структур, строки/буферы, исключения и т. п.).
Ниже — короткий чек-лист, который практически всегда находит такую ошибку.
extern "C" __declspec(dllexport) int __cdecl Foo(...);
csharp[DllImport("MyLib", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] static extern int Foo(...);
В C# по умолчанию
CallingConvention.Winapi
→ на Windows это чаще всего StdCall, поэтому обязательно указывайте Cdecl. Любое расхождение даёт «перекошенный» стек и SEH.
size_t
/ptrdiff_t
→ на x64 это 8 байт. В C# используйте UIntPtr
/IntPtr
или nuint
/nint
, а не int
.long
в MSVC — 4 байта и на x86, и на x64. В C# под это нужен int
, не long
.IntPtr
/UIntPtr
.bool
в C++ — 1 байт, а в P/Invoke по умолчанию — WinAPI BOOL
(4 байта).bool
(C++), пишите:
Если это WinAPI-стильcsharp[MarshalAs(UnmanagedType.I1)] bool
BOOL
, используйте int
.const char*
↔ C#: string
, CharSet.Ansi
или [MarshalAs(UnmanagedType.LPStr)]
.const wchar_t*
↔ C#: string
, CharSet.Unicode
или [MarshalAs(UnmanagedType.LPWStr)]
.[MarshalAs(UnmanagedType.LPUTF8Str)]
(доступно в .NET Core/5+), либо передавайте буфер byte[]/IntPtr
.new
/malloc
в C++; либо возвращайте в управляемый код копию, либо предоставьте функцию-освободитель из той же DLL.#pragma pack
/align
:
cundefined
// C++ #pragma pack(push, 1) struct MyArgs { bool f; int x; /* ... */ }; #pragma pack(pop)
text```csharp // C# [StructLayout(LayoutKind.Sequential, Pack = 1)] struct MyArgs { [MarshalAs(UnmanagedType.I1)] public bool f; public int x; }
[MarshalAs(UnmanagedType.ByValArray, SizeConst = N)]
.int Foo(in MyArgs args, out MyResult result)
.cextern "C" __declspec(dllexport) int __cdecl DoWork(const MyArgs* inArgs, MyResult* outRes) noexcept { try { return Impl(*inArgs, *outRes); // 0 = OK } catch (...) { return -1; // ошибка } }
И в C# проверяйте код возврата.
csharp[UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate void ProgressCb(int p);
GCHandle
или статическое поле.cpp#include <cstddef> #pragma pack(push, 1) struct MyArgs { bool flag; // 1 byte int count; // 4 bytes }; struct MyResult { int code; std::size_t bytes; // 8 bytes on x64 }; #pragma pack(pop) extern "C" __declspec(dllexport) int __cdecl DoWork(const MyArgs* inArgs, MyResult* outRes) noexcept { try { // ... реальная логика, вложенные вызовы и т.д. outRes->code = 0; outRes->bytes = 123; return 0; } catch (...) { return -1; } }
csharpusing System; using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential, Pack = 1)] struct MyArgs { [MarshalAs(UnmanagedType.I1)] public bool flag; // C++ bool (1 byte) public int count; } [StructLayout(LayoutKind.Sequential, Pack = 1)] struct MyResult { public int code; public UIntPtr bytes; // size_t } static class Native { [DllImport("MyLib", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true, CharSet = CharSet.Ansi)] // или Unicode, если wchar_t* public static extern int DoWork(in MyArgs args, out MyResult res); }
dumpbin /exports MyLib.dll
Имя должно быть ровно DoWork
(если extern "C"
и ExactSpelling=true
)./RTC1 /GS /sdl
#pragma pack
в разных инклудах.Если сигнатуры сложные (вложенные структуры, массивы структур, владение памятью), сделайте тонкий C++/CLI-шлюз:
Если хотите, можете скинуть точные подписи экспортируемой функции (C++) и DllImport
(C#) + определения структур — я сопоставлю поля/атрибуты и укажу, где именно возникает несоответствие. Но даже без этого этот чек-лист обычно за 1–2 правки убирает ваш SEH.