Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: байтовый массив на C#  (Прочитано 21083 раз)
0 Пользователей и 4 Гостей смотрят эту тему.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« : 12-09-2011 16:41 » 

имеется такая вот задача

в DLL имеется функция на C++ , эта функция формирует байтовый массив с размером, заранее неизвестным. Нужно из C# вызвать эту функцию и получить в шарпную программу этот массив

я с шарпом практически не знаком, как корректно сделать такой возврат массива ?
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 12-09-2011 18:31 » 

Алексей1153++, через указатель на массив и числовое значение с размером массива. System.Runtime.InteropServices.Marshal. Освобождение памяти настоятельно рекомендую делать там же в DLL, где она выделяется.
« Последнее редактирование: 12-09-2011 18:34 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #2 : 12-09-2011 18:43 » 

Dimka, с освобождением - это само собой )  То есть, всё равно через два вызова придётся делать - первый вызов это генерация, второй - вытаскивание готового ?

Добавлено через 10 часов, 50 минут и 16 секунд:
сделал вот так

Код:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

/*
//импортируем такие функции:
UINT32 WINAPI GenerateGIF_v1
(
UINT32 _letters_H//высота кегля
,UINT32 _delay_ms//задержка в мс (реально воспринимаются значения, кратные 10 мс)
,const WCHAR* pwch_text//текст
,const WCHAR* pwch_fontName//имя шрифта
)
 
UINT32 WINAPI GetGifArray(UINT8* pArray, UINT32 ArrayLen)
*/


namespace TestGifDLL
{

public partial class Form1:Form
{
[DllImport("GifGenerator.dll")]
static extern UInt32 GenerateGIF_v1
(
UInt32 _letters_H
,UInt32 _delay_ms
,IntPtr pwch_text
,IntPtr pwch_fontName
);

[DllImport("GifGenerator.dll")]
static extern UInt32 GetGifArray
(
IntPtr pArray
,UInt32 ArrayLen
);

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender,EventArgs e)
{
//генерируем гифку
UInt32 gif_len=0;
byte[] gif_array=null;

{
//zero-term в конце строк добавлять обязательно!
//иначе строки при маршалинге не будут ограничены нулём
string text="12345\0";
string font="Times New Roman\0";

char[] p_text = text.ToCharArray();
char[] p_font = font.ToCharArray();

int text_len=p_text.Length;
int font_len=p_font.Length;

IntPtr marsh_text = Marshal.AllocHGlobal(text_len);
IntPtr marsh_font = Marshal.AllocHGlobal(font_len);
{
//подготавливаем текст для передачи
Marshal.Copy(p_text,0,marsh_text,text_len);
Marshal.Copy(p_font,0,marsh_font,font_len);

gif_len=GenerateGIF_v1(40,60,marsh_text,marsh_font);
}
Marshal.FreeHGlobal(marsh_text);
Marshal.FreeHGlobal(marsh_font);
}

//добываем гифку в виде байтового массива
{
//память для заполнения в DLL
IntPtr marsh_gif_array=Marshal.AllocHGlobal((int)gif_len);
{
gif_len=GetGifArray(marsh_gif_array,gif_len);
gif_array=new byte[gif_len];
Marshal.Copy(marsh_gif_array,gif_array,0,(int)gif_len);
}
Marshal.FreeHGlobal(marsh_gif_array);
}

if(gif_array!=null)
{
//массив добыли
//gif_array;
//gif_len;
}

}
}
}

гифка создаётся в DLL , на диск сохраняется - всё нормально. Массив виден в шарпе, размер и содержимое - верные

но при закрытии шарповой программы вываливается сообщение



что я не так делаю ?

* gif_kos_1.PNG (39.17 Кб - загружено 2705 раз.)
« Последнее редактирование: 13-09-2011 05:33 от Алексей1153 » Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 13-09-2011 06:48 » 

Цитата: Алексей1153++
То есть, всё равно через два вызова придётся делать - первый вызов это генерация, второй - вытаскивание готового ?
Не знаю, какая религия запрещает сделать это в одном вызове при помощи out параметра (ссылки на указатель или указателя на указатель).

Цитата: Алексей1153++
что я не так делаю ?
Кто же знает, в твоём dispose ничего не написано. Могу предположить, что ты, получив массив из DLL, далее, вместо того, чтобы скопировать его в managed-память и избавиться от указателей, продолжаешь его злостно использовать в unmanaged-памяти из managed-кода.

Добавлено через 3 минуты:
Ещё один любопытный вопрос - зачем было городить DLL на C++ для работы с GIF, когда в .NET есть встроенные средства?
« Последнее редактирование: 13-09-2011 06:51 от dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #4 : 13-09-2011 06:53 » 

Не знаю, какая религия запрещает сделать это в одном вызове при помощи out параметра (ссылки на указатель или указателя на указатель).

так заранее размер неизвестен. Сколько выделять в шарпе памяти, если ещё не вызвал процедуру, я не знаю Улыбаюсь

Кто же знает, в твоём dispose ничего не написано. Могу предположить, что ты, получив массив из DLL, далее, вместо того, чтобы скопировать его в managed-память и избавиться от указателей, продолжаешь его злостно использовать в unmanaged-памяти из managed-кода.

нее, я не такой Улыбаюсь)

Код:
UINT32 WINAPI GetGifArray(UINT8* pArray, UINT32 ArrayLen)
{
UINT32 toCopy=min(CGF.gifFile.size(),ArrayLen);

if(toCopy)
{
::memmove(pArray,&CGF.gifFile[0],toCopy);
}

return toCopy;
}

а в шарпе я больше не использую нигде. Показанный код на C# - это весь код проекта (не включая визардовский)

Добавлено через 47 секунд:
Ещё один любопытный вопрос - зачем было городить DLL на C++ для работы с GIF, когда в .NET есть встроенные средства?

так надо заказчику. Вот именно так. Я даже спрашивал - может сразу на диск сохранять файл ? - нее, надо именно массив в шарп передать

Добавлено через 1 час, 10 минут и 17 секунд:
Дим, глянь, если не сложно, что там у меня не так...

Вот проект, DLL в папке bin/debug уже лежит, версия - релизная

Добавлено через 13 минут и 58 секунд:
если забить обе строчки с вызовам функций из длл, то не вылетает. Что-то в этом моменте я не так сделал. Если открыть даже только строчку с GenerateGIF_v1 - начинается

Добавлено через 1 час, 27 минут и 48 секунд:
Поменял содержимое функций следующим образом (чтобы было точно видно, что в самой DLL происходит):

DLL (собрана в релизе)

Код: (C++)
//массив с гифом - глобальный.
//вызов GenerateGIF_v1 заполняет его и возвращает размеры
//вызов GetGifArray заполняет внешний буфер
std::vector<BYTE> gifFile;

UINT32 WINAPI GenerateGIF_v1
(
         UINT32 _letters_H//высота кегля
        ,UINT32 _delay_ms//задержка в мс (реально воспринимаются значения, кратные 10 мс)
        ,const WCHAR* pwch_text//текст
        ,const WCHAR* pwch_fontName//имя шрифта
)
{
        gifFile.resize(100);
        return gifFile.size();
}

UINT32 WINAPI GetGifArray(UINT8* pArray, UINT32 ArrayLen)
{
        UINT32 toCopy=min(gifFile.size(),ArrayLen);

        if(toCopy)
        {
                ::memmove(pArray,&gifFile[0],toCopy);
        }

        return toCopy;
}

содержимое класса в C# программе:
Код: (C#)
        public partial class Form1:Form
        {
                [DllImport("GifGenerator.dll")]
                static extern UInt32 GenerateGIF_v1( UInt32 _letters_H,UInt32 _delay_ms,IntPtr pwch_text,IntPtr pwch_fontName);

                [DllImport("GifGenerator.dll")]
                static extern UInt32 GetGifArray( IntPtr pArray,UInt32 ArrayLen);

                private void button1_Click(object sender,EventArgs e)
                {
                        //генерируем гифку
                        UInt32 gif_len=0;
                        byte[] gif_array=null;

                        {
                                //zero-term в конце строк добавлять обязательно!
                                //иначе строки при маршалинге не будут ограничены нулём
                                string text="12345\0";
                                string font="Times New Roman\0";

                                char[] p_text = text.ToCharArray();
                                char[] p_font = font.ToCharArray();

                                int text_len=p_text.Length;
                                int font_len=p_font.Length;

                                IntPtr marsh_text = Marshal.AllocHGlobal(text_len);
                                IntPtr marsh_font = Marshal.AllocHGlobal(font_len);
                                {
                                    //подготавливаем текст для передачи
                                    Marshal.Copy(p_text,0,marsh_text,text_len);
                                    Marshal.Copy(p_font,0,marsh_font,font_len);


    gif_len=GenerateGIF_v1(40,60,marsh_text,marsh_font); //<<<<<<<<<<<<<<<<<<< это проблемная строчка!


                                }
                                Marshal.FreeHGlobal(marsh_text);
                                Marshal.FreeHGlobal(marsh_font);
                        }
                }
        }

Запускаем программу, жмём кнопку . Потом закрываем программу.


если закомментить проблемную строчку - всё нормально. Если открыть - вылетает ошибка после закрытия программы , запущенной в дебаге

(проект и DLL приложены)

* TestGifDLL.rar (91.54 Кб - загружено 909 раз.)
« Последнее редактирование: 13-09-2011 09:31 от Алексей1153 » Записан

Dmitry
Помогающий

ru
Offline Offline

« Ответ #5 : 13-09-2011 09:20 » 

А если в GenerateGIF_v1 не IntPtr передавать, а string?
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #6 : 13-09-2011 09:21 » 

Dmitry, свалится сразу, полагаю ) Автоматом к указателю на неуправляемую память нет приведения, я так понимаю

Добавлено через 1 минуту и 55 секунд:
ещё вот с этим неудобным моментом разобрался.

Код:
				//zero-term в конце строк добавлять обязательно!
//иначе строки при маршалинге не будут ограничены нулём
string text="12345\0";
string font="Times New Roman\0";

тут всё просто:

Код:
				string text="12345";
string font="Times New Roman";

text+='\0';
font+='\0';

« Последнее редактирование: 13-09-2011 09:23 от Алексей1153 » Записан

Dmitry
Помогающий

ru
Offline Offline

« Ответ #7 : 13-09-2011 09:30 » 

Алексей1153++,
Так в прототипе, соответственно, тоже указать параметры типа string
Код:
static extern UInt32 GenerateGIF_v1( UInt32 _letters_H,UInt32 _delay_ms, string text, string fontName);
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #8 : 13-09-2011 09:33 » 

Dmitry, шутник Улыбаюсь А в WINAPI немножечко нет такого типа ))

Добавлено через 2 минуты и 35 секунд:
вот хорошо не в шарпе - можно стек вызовов глянуть , и понятно, откуда ноги ошибки растут (ну, почти всегда). А тут...
« Последнее редактирование: 13-09-2011 09:35 от Алексей1153 » Записан

Dmitry
Помогающий

ru
Offline Offline

« Ответ #9 : 13-09-2011 09:56 » 

Через механизм маршалинга string при передачи в неуправляемый код преобразуется в LPSTR. Попробуй. Хотя дело твоё Улыбаюсь
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #10 : 13-09-2011 09:58 » 

Dmitry, дык, какая разница то, всё равно void* же на выходе

или покажи на примере, о чём ты ?
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #11 : 13-09-2011 10:05 » new

Цитата: Алексей1153++
так заранее размер неизвестен. Сколько выделять в шарпе памяти, если ещё не вызвал процедуру, я не знаю
А зачем заранее выделять в C# память? Marshal обеспечивает доступ к unmanaged-памяти через указатели.

Цитата: Алексей1153++
если закомментить проблемную строчку - всё нормально. Если открыть - вылетает ошибка после закрытия программы , запущенной в дебаге
Дикий ты. У Marshal есть специальные методы преобразования string в Unicode или ANSI строки. Посмотри и не городи с копированием буферов.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #12 : 13-09-2011 10:06 » 

Код:
[DllImport("GifGenerator.dll")]
static extern UInt32 GenerateGIF_v1
(
UInt32 _letters_H
,UInt32 _delay_ms
,string pwch_text
,string pwch_fontName
);

и вызов:

Код:
int gif_len;
string text="12345\0";
string font="Times New Roman\0";
gif_len = GenerateGIF_v1(80, 60, text, font);

http://msdn.microsoft.com лежит...
« Последнее редактирование: 13-09-2011 10:11 от Dmitry » Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #13 : 13-09-2011 10:35 » 

Дикий ты.

дык, базару нету! )) Я ж дикое животное ) Оттого и спрашиваю. Но как это всё связано с ошибкой - не понимаю

Dmitry, ну да, так вроде даже и не отваливается теперь, спасибо Улыбаюсь  Только тут одна проблема вылезла - строки передаются не детерминированные. Длину, наверное, тоже придётся передать
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 13-09-2011 10:45 » 

Алексей1153++, вот тебе пример ровно из двух файлов. Первый собираешь как DLL на C++, второй как EXE-сборку C#.NET.

Код: (C++)
#include <iostream>
#include <windows.h>

using namespace std;

extern "C" __declspec(dllexport) void test_in(int x, wchar_t *y)
{
        wcout << x << L" " << y << endl;
}

extern "C" __declspec(dllexport) void test_out(int &x, wchar_t *&y)
{
        x = 3;
        y = L"Hello world";
}


BOOL WINAPI DllMain(DWORD dwReason, LPVOID lpReserved)
{
        return TRUE;
}

Код: (C#)
using System;
using System.Runtime.InteropServices;

namespace exe
{
    class Program
    {
        [DllImport("dll.dll", CharSet = CharSet.Unicode)]
        static extern void test_in(int x, string y);

        [DllImport("dll.dll", CharSet = CharSet.Unicode)]
        static extern void test_out(out int x, out string y);

        static void Main(string[] args)
        {
            test_in(3, "Hello world");
            int x;
            string y;
            test_out(out x, out y);
            Console.WriteLine("{0} {1}", x, y);
            Console.ReadKey();
        }
    }
}

Всё работает в обе стороны без всяких танцев с бубном, и даже размеры строк передавать не надо.
« Последнее редактирование: 13-09-2011 10:47 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #15 : 13-09-2011 10:58 » 

Dimka,  с параметрами - это да, мне тут уже подсказали ) С возвратом - спасибо за подсказку, так проще


Добавлено через 22 минуты и 31 секунду:
о, я ещё вот это не сразу заметил - ", CharSet = CharSet.Unicode" . Долбился некоторое время ) С указанием кодировки заработало
« Последнее редактирование: 13-09-2011 11:21 от Алексей1153 » Записан

Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines