pinvokestackimbalance - як я можу це виправити або вимкнути?


76

Я щойно перейшов на vs2010 з vs2008. Точне рішення, за винятком того, що тепер кожен окремий дзвінок до бібліотеки C ++ дає виняток "pinvokestackimbalance".

Цей виняток не звільняється у 2008 році. Я маю повний доступ до DLL C ++ та до програми, що викликає. Здається, проблем з обертанням немає, але ця проблема унеможливлює налагодження інших проблем; IDE постійно зупиняється, щоб розповісти мені про ці речі.

Наприклад, ось підпис C #:

    [DllImport("ImageOperations.dll")]
    static extern void FasterFunction(
        [MarshalAs(UnmanagedType.LPArray)]ushort[] inImage, //IntPtr inImage, 
        [MarshalAs(UnmanagedType.LPArray)]byte[] outImage, //IntPtr outImage, 
        int inTotalSize, int inWindow, int inLevel);

Ось як це виглядає на стороні С ++:

#ifdef OPERATIONS_EXPORTS
#define OPERATIONS_API __declspec(dllexport)
#else
#define OPERATIONS_API __declspec(dllimport)
#endif
extern "C" {


OPERATIONS_API void __cdecl FasterFunction(unsigned short* inArray, 
                                       unsigned char* outRemappedImage,
                                       int inTotalSize, 
                                       int inWindow, int inLevel);

}

Що різниться між vs2010 та vs2008, що призведе до виникнення цих винятків? Чи слід додавати інший набір параметрів до директиви DllImport?

Відповіді:


146

По-перше, зрозумійте, що код помилковий (і завжди був). "PInvokeStackImbalance" не є винятком як такий, а керованим помічником налагодження. У VS2008 він був вимкнений за замовчуванням, але багато людей не вмикали його, тому він за замовчуванням увімкнений у VS2010. MDA не працює в режимі випуску, тому він не спрацює, якщо ви збираєте для випуску.

У вашому випадку норма виклику є неправильною. DllImportза замовчуванням CallingConvention.WinApi, що ідентично CallingConvention.StdCallдля настільного коду x86. Це повинно бути CallingConvention.Cdecl.

Це можна зробити, відредагувавши рядок [DllImport("ImageOperations.dll")]таким чином:

[DllImport("ImageOperations.dll", CallingConvention = CallingConvention.Cdecl)]

Для отримання додаткової інформації див. Цей довідник MSDN


Дякую! Винятки, безумовно, перестали стріляти. Можливо, це вирішить деякі довгострокові проблеми стабільності, які ми також мали.
mmr

4
Цілком можливо. Цей конкретний тип дисбалансу стека насправді є дещо поширеним; це не спричиняє помилок відразу, але повільно споживає стек потоку. Згодом траплятимуться «погані речі». CLR є , як правило , в стані підняти StackOverflowException, але using, catchі finallyблоки дійсно ускладнювати , коли стек повний. З цієї причини, починаючи з .NET 2.0, a StackOverflowExceptionпросто припиняє процес.
Стівен Клірі,

Більше інформації про правила взаємодії викликів тут .
Дрю Ноукс,

Чудово, це вказує мені в правильному напрямку після спроби оновити деякі проекти до .Net 4.0. Змінивши умову викликів у джерелі C # на Cdecl, мені тоді довелося змінити наші файли * .h та * .c, щоб використовувати '__cdecl'.
IAbstract

Дякую! Ця відповідь врятувала день. У моєму випадку проблема виникла через посилання на DLL Visual C ++ 8.0 (Visual Studio 2008) у проекті Visual Studio 2010.
Шроквелл

45

Щоб вимкнути його:

  1. CTRL + ALT + E
  2. У розділі "Керовані налагоджувальні помічники" зніміть прапорець біля пункту PInvokeStackImbalance.

3
Ніколи, ніколи , ніколи не стріляй у месенджер. Дисбаланс стека рано чи пізно з’їсть вашу печінку з квасолею.
Ганс Пасант,

8

Краще вирішити цю проблему тут не надто складно. Я згадую деякі методи, це може бути так само, як і деякі мої друзі, згадані вище. Я працюю з програмою PCSC Smartcard, яку витрачаю близько тижня, озлобився, зробив багато змін, нарешті, отримав рішення.

Для мене його робота з розширенням PInvoke, яке я встановив для VS2010, ви можете завантажити його тут http://www.red-gate.com/products/dotnet-development/pinvoke/

Завантажте його та встановіть, Закрийте візуальну студію та знову відкрийте, ви можете знайти розширення в рядку меню. введіть тут опис зображення

Якщо помилка пов’язана з невідповідністю підпису, просто натисніть PInvoke.net> Вставити підписи PInvoke

З'явиться нове вікно, як показано нижче введіть тут опис зображення

Введіть ім'я DLL і натисніть на пошук, щоб побачити всі функції цієї DLL у вікні результатів пошуку. Клацніть на функцію, щоб отримати підпис для цієї функції.

Використовуйте цей підпис, і вам потрібно змінити свої програми відповідно до цього підпису, переважно типу даних.

Це вирішить мою проблему, у вас можуть виникнути різні проблеми, такі як callConvention або додаткові атрибути, які потрібно вказати під час імпорту dll.

Щасливого кодування Будь добре!


Більшість бібліотек DLL неможливо знайти у вікні пошуку. На жаль, це виглядає досить акуратно.
котиться

3

Я отримав цю проблему також під час використання VS2010. Що це таке: Visual Studio за замовчуванням має 64-розрядний код для будь-якого процесора. Вказівники на змінні (наприклад, рядки) тепер стають 64-бітними при виклику зовнішніх DLL, де як і всі ваші надійні та надійні DLL використовують 32-бітові покажчики.

Не думайте, що з вашими DLL щось не так, немає.

Змініть налаштування VS, щоб генерувати код X86 таким чином (експрес-версії C #)

  1. перейдіть до Інструменти -> Параметри.
  2. У нижньому лівому куті діалогового вікна "Параметри" встановіть прапорець "Показати всі налаштування".
  3. У вікні дерева зліва виберіть "Проекти та рішення".
  4. У параметрах праворуч поставте прапорець "Показати розширені конфігурації збірки".
  5. Клацніть OK.
  6. Перейдіть до Build -> Configuration Manager ...
  7. У стовпці Платформа поруч із вашим проектом клацніть поле зі списком та виберіть "".
  8. У налаштуваннях "Нова платформа" виберіть "x86".
  9. Клацніть OK.
  10. Клацніть Закрити.

Я також помічаю, що, хоча комп'ютери подвоювали потужність кожні 12 місяців, мій поточний комп'ютер з 1 гігабайт оперативної пам'яті здається не швидшим, ніж мій перший 486 з 4 Мегабайт. Не хвилюйтеся про використання 64-розрядного коду, він не буде швидшим чи кращим, оскільки він побудований на величезній громіздкій об'єктно-орієнтованій вежі здуття.


1
Дякую за поради, я їх перевірю. Але якщо у вас 64-розрядна ОС Windows із лише 1 ГБ оперативної пам'яті, у вас будуть проблеми зі швидкістю. Вам слід оновити принаймні 4 Гб, інакше у вас виникнуть проблеми із заміною. 64-розрядні ОС дозволяють мати більше інформації в пам'яті, а не на диску, і ці зміни можуть спричинити значне збільшення швидкості, залежно від операції. Якщо ваша машина не швидша за 486, можливо, пора перевірити віруси :)
mmr

Це спрацювало, велике спасибі! врятував години, якщо не дні!
Alex Z

0

Я намагався зателефонувати dll за допомогою CallingConventionis, ThisCallі це спрацювало для мене. Ось мій код роботи з BLOB MS Sql Server.

[DllImport("sqlncli11.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.ThisCall)]
            private static extern SafeFileHandle OpenSqlFilestream(
                        string FilestreamPath,
                        UInt32 DesiredAccess,
                        UInt32 OpenOptions,
                        byte[] FilestreamTransactionContext,
                        UInt32 FilestreamTransactionContextLength,
                        Int64 AllocationSize);

Більше на: https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention(v=vs.110).aspx

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.