Связь C-шной dll с VB-шным приложением

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
Odrick
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 503
Зарегистрирован: 28.09.2003 (Вс) 2:04

Связь C-шной dll с VB-шным приложением

Сообщение Odrick » 15.09.2005 (Чт) 18:32

Ситуация такова: есть dll, написанная на C, которая имеет одну внешнюю функцию, параметром которой является указатель на функцию внутри VB-шной программы. После ее вызова она вызывает VB-шную функцию, параметром которой является строка и возвращаемым значением тоже строка. На C прототип такой функции выглядит например так:
Код: Выделить всё
const char *Test(char *Params)


Вопрос в том, как правильно объявить такую функции в VB?
Код: Выделить всё
Public Function Test(Params as String) as String

Успешно валит приложение.

Код: Выделить всё
Public Function Test(Params as Long) as Long

Уже лучше. Указатель на строку принимается. Однако как потом получить строку по указателю непонятно. Попытка вернуть указатель на VB-шную строку, типа:
Код: Выделить всё
Test = VarPtr(sRes)

приводит к успешному краху...
То, что для одних константа, для других только переменная...

hCORe
VB - Экстремал
VB - Экстремал
Аватара пользователя
 
Сообщения: 2332
Зарегистрирован: 22.02.2003 (Сб) 15:21
Откуда: parent directory

Сообщение hCORe » 15.09.2005 (Чт) 18:37

Кажется, нарушается основное правило работы с указателями: создавать новый и отдавать его "клиенту". Конвенция вызова stdcall, надеюсь?
Моду создают модоки, а распространяют модозвоны.

hCORe
VB - Экстремал
VB - Экстремал
Аватара пользователя
 
Сообщения: 2332
Зарегистрирован: 22.02.2003 (Сб) 15:21
Откуда: parent directory

Сообщение hCORe » 15.09.2005 (Чт) 18:40

А строку по указателю можно получить через lstrcpy.
Моду создают модоки, а распространяют модозвоны.

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 15.09.2005 (Чт) 18:44

Public Function Test(Params As Long) As Long - правильно.
Создать строку по указателю проще всего вызовом SysAllocString.
Возвращать надо StrPtr(sRes).
Изображение

Odrick
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 503
Зарегистрирован: 28.09.2003 (Вс) 2:04

Сообщение Odrick » 15.09.2005 (Чт) 18:46

hCORe писал(а):Конвенция вызова stdcall, надеюсь?

Да, конечно ;)
То, что для одних константа, для других только переменная...

Odrick
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 503
Зарегистрирован: 28.09.2003 (Вс) 2:04

Сообщение Odrick » 15.09.2005 (Чт) 18:52

Код: Выделить всё
Test = StrPtr(sRes)

приводит к тому-же результату. Уже пробовал. Ладно, прийду домой - выложу тестовый пример на C и на VB вместе с dll-кой. Так будет проще...
То, что для одних константа, для других только переменная...

Odrick
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 503
Зарегистрирован: 28.09.2003 (Вс) 2:04

Сообщение Odrick » 16.09.2005 (Пт) 1:25

В общем на C все это дело работает так:

Код: Выделить всё
#include "BOTS.h"
const char *TestAI(char *_Str)
{
return "test";
}
ENGINE_API l_engine = {TestAI};
void __fastcall TForm1::Button1Click(TObject *Sender)
{
//
HINSTANCE l_lib = LoadLibrary("alpha_bot.dll");
if ( !l_lib )
    return ;
botDriverEntryProc l_entry;
l_entry = (botDriverEntryProc)GetProcAddress(l_lib, BOT_DRV_ENTRY_PROCNAME);
if ( l_entry )
    {
    l_entry(&l_engine);
    }
FreeLibrary(l_lib);
}


VB-шный проект прикреплен. Как все это дело запустить затрудняюсь... :(
Вложения
Dll.zip
(12.82 Кб) Скачиваний: 25
То, что для одних константа, для других только переменная...

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 16.09.2005 (Пт) 2:18

Odrick писал(а):Ладно, прийду домой - выложу тестовый пример на C и на VB вместе с dll-кой. Так будет проще...

Сутки домой шёл? :lol:



На самом деле, как и предполагал hCORe, там не stdcall. В сообщении об ошибке, между прочим, так и написано.
Изображение

codemaster
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 604
Зарегистрирован: 13.02.2004 (Пт) 13:35

Re: Связь C-шной dll с VB-шным приложением

Сообщение codemaster » 16.09.2005 (Пт) 10:21

Odrick писал(а):Ситуация такова: есть dll, написанная на C, которая имеет одну внешнюю функцию, параметром которой является указатель на функцию внутри VB-шной программы.

5 баллов!

зодно к прочтению
How To Pass a String Between Visual Basic and Your C DLL
http://support.microsoft.com/default.as ... US;Q187912

P.S. Borland ?
//<-
Mit freundlichen Grüßen
//->

Odrick
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 503
Зарегистрирован: 28.09.2003 (Вс) 2:04

Сообщение Odrick » 16.09.2005 (Пт) 11:46

2tyomitch - ну-у-у... Так вышло. Действительно почти сутки шел :)) Не stdcall??? Блин... Меня надули. Ладно, буду пинать того, кто dll-ку писал...

2codemaster - спасибо за ссылку! Щас почитаю. Нет, не Borland - VS.
То, что для одних константа, для других только переменная...

Odrick
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 503
Зарегистрирован: 28.09.2003 (Вс) 2:04

Сообщение Odrick » 16.09.2005 (Пт) 14:41

Так... В общем замена __cdecl на __stdcall и замена прототипа функции основного модуля на:

Код: Выделить всё
const LPSTR Test(LPSTR Params)


дала положительный результат :)
Но как говорится не без ложки дегтя... Во-первых - так и не удалось получить строку по указателю.

Код: Выделить всё
Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyA" (ByVal lpString1 As String, ByVal lpString2 As Long)  As Long

или

Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyA" (ByVal lpString1 As Any, ByVal lpString2 As Any)  As Long

Эффект тот-же - результат - пустая строка...
Кроме того, dll-ка получает строку, но если это символы меньше 128, то результат - 1 символ, если больше 128 - "иероглифы" :) Подозреваю, что это связано с форматом хранения строки в VB (2 байта). Тогда каждый символ анлийского текста вторым байтом имеет 0, что расценивается dll как конец строки - отсюда и 1 символ. Вот и вопрос: как сконвертить строку в нужный формат и как-же все-таки получить строку, зная указатель? :?
То, что для одних константа, для других только переменная...

Odrick
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 503
Зарегистрирован: 28.09.2003 (Вс) 2:04

Сообщение Odrick » 16.09.2005 (Пт) 15:20

Гм... А может передавать массив байт? Так прокатит? И как тогда на массив байт получить указатель?
То, что для одних константа, для других только переменная...

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 16.09.2005 (Пт) 15:44

"lstrcpyA" попробуй заменить на "lstrcpyW"
Lasciate ogni speranza, voi ch'entrate.

Odrick
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 503
Зарегистрирован: 28.09.2003 (Вс) 2:04

Сообщение Odrick » 16.09.2005 (Пт) 16:00

2alibek - спасибо, я уже разобрался. Проблема в моих кривых руках - достаточно было объявить функцию так:

Код: Выделить всё
Public Function Test(ByVal Params as Long) as Long

и lstrcpy заработала. Но КАК корректно передать строку назад в dll? То есть сконвертить в нужный формат и отдать? Може есть какие-нить соображения? Срочно нужно... Но в крайнем случае вечером выложу проект dll-ки и тестовий VB-шный в нынешнем состоянии...
То, что для одних константа, для других только переменная...

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 16.09.2005 (Пт) 20:59

Ну я же всё это уже писал?

tyomitch писал(а):Public Function Test(Params As Long) As Long - правильно.
Создать строку по указателю проще всего вызовом SysAllocString.
Возвращать надо StrPtr(sRes).


Зачем там lstrcpy? Ей же нужно самому выделять буфер нужной длины, т.е. сначала и длину померять?
А вот SysAllocString делает всё это сама.

Нет чтобы одним вызовом API всю работу сделать - городите огород из десятка таких вызовов :-|
Изображение

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 16.09.2005 (Пт) 21:03

Odrick писал(а):Но КАК корректно передать строку назад в dll? То есть сконвертить в нужный формат и отдать? Може есть какие-нить соображения? Срочно нужно... Но в крайнем случае вечером выложу проект dll-ки и тестовий VB-шный в нынешнем состоянии...

Я правильно понял, что единственная проблема - перевод строки из Юникода в ANSI?
Тогда спасёт StrConv(sRes, vbFromUnicode)
Изображение

Odrick
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 503
Зарегистрирован: 28.09.2003 (Вс) 2:04

Сообщение Odrick » 17.09.2005 (Сб) 0:21

Да, ты прав. SysAllocString действительно проще. StrConv - именно то, что нужно! В общем выглядит теперь это так:

Код: Выделить всё
Public Function Test(ByVal Params As Long) As Long
    MsgBox SysAllocString(Params)
    Test = StrPtr(StrConv("Возврат результата функции в dll", vbFromUnicode))
End Function

и все работает замечательно ;) Всем спасибо.
То, что для одних константа, для других только переменная...


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: AhrefsBot, PetalBot и гости: 5

    TopList