Форум программистов, компьютерный форум, киберфорум
Наши страницы
Assembler, MASM, TASM
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.50/4: Рейтинг темы: голосов - 4, средняя оценка - 4.50
MrDreher
1 / 0 / 1
Регистрация: 02.10.2012
Сообщений: 71
1

Динамическая библиотека на MASM разбалансировала стек

14.11.2014, 00:05. Просмотров 756. Ответов 15
Метки нет (Все метки)

Динамическая библиотека на MASM при возврате значения разбалансировала стек. Без возвращаемого значения все работает нормально.
Необходимо написать динамическую библиотеку на MASM + обертку на C#. Нашел простой и понятный пример в гугле. Все работает, кроме возврата значения (когда библиотека должна вернуть строку вызвавшей ее программе).

Вот код библиотеки:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
      .386
      .model flat, stdcall
      option casemap :none   ; case sensitive
;#########################################################################
      include c:\masm32\include\windows.inc
      include c:\masm32\include\user32.inc
      include c:\masm32\include\kernel32.inc
 
      includelib c:\masm32\lib\user32.lib
      includelib c:\masm32\lib\kernel32.lib
;#########################################################################
    .data
    HelloMsg db "Эта наш первый DLL Модуль",0
    AppName db " DLL_test ",0
    
    text db 256 dup (?);
    
    text1 db "%d",0 
    BUF DB 256 DUP(?) 
;#########################################################################
    .code
    
;-------------------------------------------------------------------------
Dll_test proc hInstance:HINSTANCE, reason:DWORD, reserved1:DWORD
; обработчик ошибок загрузки dll модуля здесь мы его упустим
 
    ret 12
Dll_test Endp
;-------------------------------------------------------------------------
Hello proc string:DWORD, a:DWORD
; наша процедура которую мы и будем вызывать
 
    invoke wsprintf, addr BUF, addr text1, a ;API функция преобразования числа в строку
    invoke MessageBox,NULL,addr BUF,addr AppName,MB_OK 
    
    invoke lstrcpy, addr text, string ;API функция копирования строк
    invoke MessageBox,NULL,addr text,addr AppName,MB_OK
    
    mov EAX,offset [HelloMsg]
    ret 4
Hello endp
;-------------------------------------------------------------------------
end Dll_test
Вот код обертки:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
 
namespace DllFrm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        //==== подключаем внешний dll модуль(test.dll) и импортируем функцию Hello ======
        [DllImport("Dll_test.dll")]
        public static extern string Hello(string text, int a);
 
        private void button1_Click(object sender, EventArgs e)
        {
            string text = "Это уже наш текст";
            int a = 2012;
            label1.Text = Hello(text, a);
        } 
    }
}
Полное описание ошибки:
Кликните здесь для просмотра всего текста

Название: Обнаружено событие pInvokeStackImbalance
Описание: Вызов функции PInvoke "DllFrm!DllFrm.Form1::Hello" разбалансировал стек. Вероятно, это вызвано тем, что управляемая сигнатура PInvoke не совпадает с неуправляемой целевой сигнатурой. Убедитесь, что соглашение о вызовах и параметры сигнатуры PInvoke совпадают с неуправляемой целевой сигнатурой.


Варианты которые уже были проверены и не помогли:
Кликните здесь для просмотра всего текста

1. Заменить mov EAX,offset [HelloMsg] => mov EAX,offset HelloMsg => lea EAX,HelloMsg
2. Заменить ret 4 => ret => ret 12 => ret 8 => ret N (от 0 до 12)
3. Опять же в гугле нашел информацию что параметры передаются через стек, поэтому попробовал добавить push EAX перед ret. Опять ошибка.. Потом опять сменил "ret 4" => "ret". Опять ошибка.. Потом сменил тип возвращаемого значения на int и сменил mov EAX, offset HelloMsg => mov EAX, 123h. Заработало! Но так передается лишь int... При попытке передать таким образом string программа (обертка) просто тихо вылетает...
0
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
14.11.2014, 00:05
Ответы с готовыми решениями:

Динамическая библиотека
Как создать динамическую библиотеку, Объясните пожалуйста все пошагово. На Visual С++

Динамическая библиотека
Я создал динамическую библиотеку в Code Blocks'e и хочу подключить ёё статически. Что мне для...

Динамическая Библиотека
Имеется динамическая библиотека Windows (Dynamic Link Library ".dll"). В ней написаны функции,...

Динамическая библиотека
Здравствуйте. Такой вопрос. Если я, например, напишу dll на Си, которая будет возвращать структуру...

Динамическая библиотека
В C++ я полный ноль, хочу знать как создаются библиотеки, которые подключаются в .NET как...

15
Charles Kludge
Клюг
7648 / 3163 / 383
Регистрация: 03.05.2011
Сообщений: 8,381
14.11.2014, 00:57 2
А где в коде DLL LibMain?
Посмотрите уж в c:\masm32\examples\exampl01\dll\tstdll.asm потроха dll и как её построить в том же каталоге.
0
MrDreher
1 / 0 / 1
Регистрация: 02.10.2012
Сообщений: 71
14.11.2014, 09:33  [ТС] 3
А где в коде DLL LibMain?
В ассемблере нет надобности называть главную процедуру каким-то магическим словом вроде main, LibMain, WinMain или init. В ассемблере главная процедура может называться почти как угодно. У меня она называется Dll_test.

Посмотрите уж в c:\masm32\examples\exampl01\dll\tstdll.asm потроха dll и как её построить в том же каталоге.
Вопрос на засыпку: а вы сами смотрели этот исходник? Ни одна из процедур (а их там 2) не возвращает string. Такую dll как в примере, я могу написать и без этого примера. Мне нужен корректный возврат строки.
0
Charles Kludge
Клюг
7648 / 3163 / 383
Регистрация: 03.05.2011
Сообщений: 8,381
14.11.2014, 09:48 4
Цитата Сообщение от MrDreher Посмотреть сообщение
Мне нужен корректный возврат строки.
Только возврат указателя на строку ака адрес строки в виде unsigned long. Если в C# считают, что строку можно вернуть по значению - это их граблемы. Можно, конечно, но это бастурмация.
0
14.11.2014, 09:48
MrDreher
1 / 0 / 1
Регистрация: 02.10.2012
Сообщений: 71
14.11.2014, 10:03  [ТС] 5
Цитата Сообщение от Charles Kludge Посмотреть сообщение
Только возврат указателя на строку ака адрес строки в виде unsigned long. Если в C# считают, что строку можно вернуть по значению - это их граблемы. Можно, конечно, но это бастурмация.
Похоже опять придется использовать небезопасный код (только с его помощью в c# можно получить строку по ее адресу).
Еще пара вопросов по теме:
1. Зачем нужен "ret N" в конце возвращаемой функции, если передаваемый параметр все равно берется из стека? В найденных примерах то и там есть "ret 4" и "ret 12" - другие варианты почти не встречаются.
2. Необходимо обеспечить программе авто обновление каждые N секунд. Соответственно, процедура (из библиотеки) будет вызываться много раз. Соответственно в стеке будет все больше и больше всякой ерунды. Нужно ли делать в библиотеке отдельную функцию для чистки стека или когда обертка на C# забирает значение из стека - регистр ESP автоматически уменьшается (на величину забранного значения).. ?
0
Charles Kludge
Клюг
7648 / 3163 / 383
Регистрация: 03.05.2011
Сообщений: 8,381
14.11.2014, 10:21 6
Цитата Сообщение от MrDreher Посмотреть сообщение
нет надобности называть главную процедуру каким-то магическим словом
Я в курсе, но не уверен, что это справедливо, когда dll вызывается из C# и прочих... Васиков ЯВУ.
И да, AFAIR, Dll_test должна возвращать 1 в EAX, а не абы что.
Цитата Сообщение от MrDreher Посмотреть сообщение
Похоже опять придется использовать небезопасный код (только с его помощью в c# можно получить строку по ее адресу).
Попробуйте передавать адрес строки-приёмника как ещё один параметр в Hello и копировать туда HelloMsg прямо в dll.
Цитата Сообщение от MrDreher Посмотреть сообщение
1. Зачем нужен "ret N" в конце возвращаемой функции
Вызываемая ф-ция сама должна очищать стек от переданных ей параметров, N=кол-во парам.*4, т.е. sizeof(dword long). Кмк, выход из Hello должен выглядеть так:
Assembler
1
2
    lea EAX,HelloMsg
    ret 8
1
MrDreher
1 / 0 / 1
Регистрация: 02.10.2012
Сообщений: 71
14.11.2014, 11:34  [ТС] 7
Цитата Сообщение от Charles Kludge Посмотреть сообщение
Вызываемая ф-ция сама должна очищать стек от переданных ей параметров, N=кол-во парам.*4, т.е. sizeof(dword long).
Понял, спасибо.
Цитата Сообщение от Charles Kludge Посмотреть сообщение
И да, AFAIR, Dll_test должна возвращать 1 в EAX, а не абы что.
Не понял. Почему Dll_test должна возвращать 1 в EAX, а не абы что?
Цитата Сообщение от Charles Kludge Посмотреть сообщение
Попробуйте передавать адрес строки-приёмника как ещё один параметр в Hello и копировать туда HelloMsg прямо в dll.
Попробовал. Появляется следующая ошибка:
Кликните здесь для просмотра всего текста

Название ошибки: Обнаружено событие FatalExecutionEngineError.
Описание ошибки: В среде выполнения обнаружена критическая ошибка. Ошибка произошла по адресу 0x5aeb2a01 в потоке 0x1548. Код ошибки 0xc0000005. Она может быть вызвана ошибкой в CLR или в небезопасных либо не поддающихся проверке фрагментах пользовательского кода. Обычно источниками таких ошибок бывают ошибки упаковки, допускаемые пользователями при COM-взаимодействии, либо PInvoke, повредивший стек.
0
Mikl___
Автор FAQ
13515 / 6444 / 638
Регистрация: 11.11.2010
Сообщений: 11,637
14.11.2014, 11:40 8
Цитата Сообщение от MrDreher Посмотреть сообщение
Не понял. Почему Dll_test должна возвращать 1 в EAX, а не абы что?
попробуй прочитать http://www.cyberforum.ru/assembler-articles/thread751124.html http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow "Win32 API. Урок 17. Динамические библиотеки"
2
MrDreher
1 / 0 / 1
Регистрация: 02.10.2012
Сообщений: 71
14.11.2014, 13:48  [ТС] 9
Цитата Сообщение от Mikl___ Посмотреть сообщение
попробуй прочитать Сам себе Iczelion http://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow "Win32 API. Урок 17. Динамические библиотеки"
Спасибо.
Если тему будет читать еще более ленивый программист чем я, скопирую оттуда ответ на мой вопрос
Вы возвращаете TRUE в eax, если вы хотите, чтобы DLL продолжала выполняться. Если вы возвратите FALSE, DLL не будет загружена. Например, если ваш инициализационный код должен зарезервировать память и он не может это сделать, стартовой функции следует возвратить FALSE, чтобы показать, что DLL не может запуститься.
--------------------------------
Решил воспользоваться небезопасным кодом (в обертке на C#) чтобы получить строку по ее адресу (полученному из библиотеки).
Вот код обертки:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
 
namespace DllFrm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        //==== подключаем внешний dll модуль(test.dll) и импортируем функцию Hello ======
        [DllImport("Dll_test.dll")]
        public static extern long Hello(string text, int a);
 
        private void button1_Click(object sender, EventArgs e)
        {
            string text = "Это уже наш текст";
            int a = 2012;
            label1.Text = Lib.ImportStr(Hello(text, a));
        } 
    }
}
Вот код класса с небезопасной статической функцией загрузки содержимого строки по ее адресу:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace DllFrm
{
    class Lib
    {
        static public unsafe string ImportStr(long addr)
        {
            char* str = (char*)addr;
            return new string(str);
        }
    }
}
Вот код библиотеки:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
      .386
      .model flat, stdcall
      option casemap :none   ; case sensitive
;#########################################################################
      include c:\masm32\include\windows.inc
      include c:\masm32\include\user32.inc
      include c:\masm32\include\kernel32.inc
 
      includelib c:\masm32\lib\user32.lib
      includelib c:\masm32\lib\kernel32.lib
;#########################################################################
    .data
    HelloMsg db "Эта наш первый DLL Модуль",0
    AppName db " DLL_test ",0
    
    text db 256 dup (?);
    
    text1 db "%d",0 
    BUF DB 256 DUP(?) 
;#########################################################################
    .code
    
;-------------------------------------------------------------------------
Dll_test proc hInstance:HINSTANCE, reason:DWORD, reserved1:DWORD
; обработчик ошибок загрузки dll модуля здесь мы его упустим
    
    mov EAX, TRUE
    ret 12
Dll_test Endp
;-------------------------------------------------------------------------
Hello proc string:DWORD, a:DWORD
; наша процедура которую мы и будем вызывать
 
    invoke wsprintf, addr BUF, addr text1, a ;API функция преобразования числа в строку
    invoke MessageBox,NULL,addr BUF,addr AppName,MB_OK 
    
    invoke lstrcpy, addr text, string ;API функция копирования строк
    invoke MessageBox,NULL,addr text,addr AppName,MB_OK
    
    mov EAX, offset [HelloMsg]
    ret 8
Hello endp
;-------------------------------------------------------------------------
end Dll_test
В результате вместо строки "Эта наш первый DLL Модуль" получаем какие-то иероглифы...
И это не из-за разных кодировок. Вот тот бред который получает обертка: "⃠⃸䐠䱌찠ü䐠䱌瑟獥⁴�".
0
Charles Kludge
Клюг
7648 / 3163 / 383
Регистрация: 03.05.2011
Сообщений: 8,381
14.11.2014, 13:53 10
Цитата Сообщение от MrDreher Посмотреть сообщение
получаем какие-то иероглифы...
А это пробовали?
Цитата Сообщение от Charles Kludge Посмотреть сообщение
lea EAX,HelloMsg
Вместо
Цитата Сообщение от MrDreher Посмотреть сообщение
mov EAX, offset [HelloMsg]
?
0
MrDreher
1 / 0 / 1
Регистрация: 02.10.2012
Сообщений: 71
14.11.2014, 14:07  [ТС] 11
Цитата Сообщение от Charles Kludge Посмотреть сообщение
А это пробовали?
Да. Не помогает. Результат тот-же. Иероглифы те же самые. Один в один.
Если сильно напрячь память, начинаю вспоминать что-то про страницы памяти и сегменты. Мне кажется, что этим процессам(dll и обертке) выделены разные области памяти (никак не пересекающиеся)..
0
Charles Kludge
Клюг
7648 / 3163 / 383
Регистрация: 03.05.2011
Сообщений: 8,381
14.11.2014, 14:19 12
Цитата Сообщение от MrDreher Посмотреть сообщение
Иероглифы те же самые. Один в один.
Скриншот покажите.
0
MrDreher
1 / 0 / 1
Регистрация: 02.10.2012
Сообщений: 71
14.11.2014, 14:26  [ТС] 13
Цитата Сообщение от Charles Kludge Посмотреть сообщение
Скриншот покажите.
Во вложении.
0
Миниатюры
Динамическая библиотека на MASM разбалансировала стек  
Charles Kludge
Клюг
7648 / 3163 / 383
Регистрация: 03.05.2011
Сообщений: 8,381
14.11.2014, 14:30 14
Хм, а прога-то компилится не с поддержкой юникода, случаем?
1
Mikl___
Автор FAQ
13515 / 6444 / 638
Регистрация: 11.11.2010
Сообщений: 11,637
14.11.2014, 15:07 15
Charles Kludge,
это юникод и есть, пусть пробует вместо HelloMsg db "Эта наш первый DLL Модуль" написать
Assembler
1
HelloMsg dw 'H','e','l','l','o','!',0
1
MrDreher
1 / 0 / 1
Регистрация: 02.10.2012
Сообщений: 71
14.11.2014, 15:45  [ТС] 16
Всем спасибо, все работает. Изменил только код функции загрузки содержимого строки по ее адресу:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace DllFrm
{
    class Lib
    {
        static public unsafe string ImportStr(long addr)
        {
            sbyte* str = (sbyte*)addr;
            string tmp = "";
            for (int i = 0; (new string(str, i, 1))[0] != '\0'; i++) 
                tmp += new string(str, i, 1); 
 
            return tmp;
        }
    }
}
Так все работает.
Чувствую что можно написать красивее и правильнее, но пока так. Позже думаю исправлю. Когда нибудь.

Добавлено через 23 минуты
Заменил тело функции ImportStr на
C#
1
2
3
4
        static public unsafe string ImportStr(long addr)
        {
            return new string((sbyte*)addr);
        }
Все. Теперь моя совесть чиста.
0
14.11.2014, 15:45
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
14.11.2014, 15:45

динамическая библиотека
Кто знает, как правильно создать и подключить динамическую библиотеку на WinAPI C++? файл *.pp, к...

Стек и динамическая память
Читаю книжку по с++. Для хранения переменных может использоваться стек или динамическая память. ...

Нужна динамическая библиотека!
Библиотека из VIsual Basic 7.0 msvbvm70.dll


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru