:: Visual Foxpro, Foxpro for DOS
Создать DLL на С++
Перминов Игорь
Автор

Сообщений: 1591
Откуда: Красная Орловка
Дата регистрации: 16.09.2001
Коллеги, всем доброго дня.
Писал DLL на Borland С++ Builder - все нормально было.
Теперь появилась нужда написать DLL, использую VS 2015 и C++.
Создал проект, подготовил CPP и H файлы, собрал решение в DLL.
Из VFP:
SET DEFAULT TO "D:\FOX\TestMailFuncs\"
IF VARTYPE(giCnt)="U"
PUBLIC giCnt
giCnt = 0
ENDIF
LOCAL loErr as Exception
TRY
giCnt = giCnt + 1
CLEAR
??REPLICATE("-",10)+" #"+TRANSFORM(giCnt)+" "+REPLICATE("-",10)
?"Run"
?"Declare DLLS"
DECLARE DOUBLE _MyAdd IN MailFuncsDLL.dll as "MyAdd" DOUBLE @a, DOUBLE @b
?MyAdd(1,2)
LIST DLLS
CATCH TO loErr
??
?"Message: ",loErr.Message
?"Line # ",loErr.LineNo
FINALLY
?"Clear dlls"
CLEAR DLLS "MyAdd"
ENDTRY
Выполняется код в CATCH
Message: Cannot find entry point _MyAdd in the DLL.
*********************
CPP файл:
// dllmain.cpp: определяет точку входа для приложения DLL.
#include "stdafx.h"
#define BUILD_DLL // Вот это надо вставлять для того чтобы было как нуно и работало в VFP
#include "MailFuncsDLL.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
extern "C" double DLL_TYP MyAdd(double x, double y)
{
double ldA, ldB;
ldA = x; ldB = y;
return ldA + ldB;
}
H файл:
// MailFuncsDLL.h
#ifndef _MAIN_H
#define _MAIN_H
#if defined(BUILD_DLL)
# define DLL_TYP __declspec(dllexport)
#else
# if defined(BUILD_APP)
# define DLL_TYP __declspec(dllimport)
# else
# define DLL_TYP
# endif
#endif
extern "C" DLL_TYP double MyAdd(double, double);
#endif

Вырезка из MAP файла:
Address Publics by Value Rva+Base Lib:Object
0000:00000000 ___guard_longjmp_count 00000000 <absolute>
0000:00000000 ___guard_iat_count 00000000 <absolute>
0000:00000000 ___guard_longjmp_table 00000000 <absolute>
0000:00000000 ___safe_se_handler_table 00000000 <absolute>
0000:00000000 ___dynamic_value_reloc_table 00000000 <absolute>
0000:00000000 ___guard_fids_count 00000000 <absolute>
0000:00000000 ___safe_se_handler_count 00000000 <absolute>
0000:00000000 ___guard_iat_table 00000000 <absolute>
0000:00000000 ___guard_fids_table 00000000 <absolute>
0000:00000100 ___guard_flags 00000100 <absolute>
0000:00000000 ___ImageBase 10000000 <linker-defined>
0001:00000000 __enc$textbss$begin 10001000 <linker-defined>
0001:00010000 __enc$textbss$end 10011000 <linker-defined>
0002:00000560 _DllMain@12 10011560 f dllmain.obj
0002:000005b0 _MyAdd 100115b0 f dllmain.obj
0002:00000620 __CRT_RTC_INIT 10011620 f MSVCRTD:_init_.obj
Как видим в MAP _MyAdd успешно существует.
Тогда где, что неправильно, может кто поможет разобраться.


------------------
Без коментариев..




Исправлено 2 раз(а). Последнее : Перминов Игорь, 23.03.17 10:39
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
Рома

Сообщений: 1079
Дата регистрации: 06.06.2001
_MyAdd в таблице экспорта dll будет без подчеркивания. "_" в объектном файле - соглашение о вызове stdcall cdecl
Для stdcall должно получиться _MyAdd@16

Т.е. нужно
#if defined(BUILD_DLL)
# define DLL_TYP __declspec(dllexport) stdcall
#else
# if defined(BUILD_APP)
# define DLL_TYP __declspec(dllimport) stdcall
# else
# define DLL_TYP
# endif
#endif

Вот правильная сигнатура для твоей функции (@ не нужно, так как нет в C функции указателя у параметров)
DECLARE DOUBLE MyAdd IN MailFuncsDLL.dll as "MyAdd" DOUBLE a, DOUBLE b



Исправлено 3 раз(а). Последнее : Рома, 23.03.17 17:41
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
Перминов Игорь
Автор

Сообщений: 1591
Откуда: Красная Орловка
Дата регистрации: 16.09.2001
Получилось:
H файл:
// MailFuncsDLL.h
#ifndef _MAIN_H
#define _MAIN_H
#if defined(BUILD_DLL)
# define DLL_TYP __declspec(dllexport) __cdecl
#else
# if defined(BUILD_APP)
# define DLL_TYP __declspec(dllimport __cdecl
# else
# define DLL_TYP
# endif
#endif
extern "C" double DLL_TYP MyAdd(double, double);
#endif

CPP файл:
// dllmain.cpp: определяет точку входа для приложения DLL.
#include "stdafx.h"
#define BUILD_DLL // Вот это надо вставлять для того чтобы было как нуно и работало в VFP
#include "MailFuncsDLL.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
extern "C" double DLL_TYP MyAdd(double x, double y)
{
double ldA, ldB;
ldA = x; ldB = y;
return ldA + ldB;
}

Декларирование в VFP:
DECLARE DOUBLE MyAdd IN MailFuncsDLL.dll as "MyAdd" DOUBLE a, DOUBLE b
?MyAdd(100.52,10)


------------------
Без коментариев..
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
lulgu

Сообщений: 1838
Дата регистрации: 30.11.2016
Непонятно назначение вашей функции, что делает и что возвращает.
Обычно функции API возвращают 0 при неудаче, поэтому и отпадает необходимость в TRY.
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
Перминов Игорь
Автор

Сообщений: 1591
Откуда: Красная Орловка
Дата регистрации: 16.09.2001
Не совсем понятен Ваш вопрос:
lulgu
Непонятно назначение вашей функции, что делает и что возвращает.
Обычно функции API возвращают 0 при неудаче, поэтому и отпадает необходимость в TRY.
Вы имеете в виду MyAdd()?
или вот это:
* Test
* TestMailFuncs.prg
*
SET DEFAULT TO "D:\FOX\TestMailFuncs\"
IF VARTYPE(giCnt)="U"
PUBLIC giCnt
giCnt = 0
ENDIF
LOCAL loErr as Exception
TRY
giCnt = giCnt + 1
CLEAR
??REPLICATE("-",10)+" #"+TRANSFORM(giCnt)+" "+REPLICATE("-",10)
?"Run"
?"Declare DLLS"
DECLARE DOUBLE MyAdd IN MailFuncsDLL.dll as "MyAdd" DOUBLE a, DOUBLE b
?MyAdd(100.52,10)
LIST DLLS
CATCH TO loErr
??
?"Message: ",loErr.Message
?"Line # ",loErr.LineNo
FINALLY
?"Clear dlls"
CLEAR DLLS "MyAdd"
ENDTRY
CLEAR DLLS
или почему все обернул в TRY...CATCH...FINALLY?


------------------
Без коментариев..
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
lulgu

Сообщений: 1838
Дата регистрации: 30.11.2016
Да, непонятно назначение вашей довольно громоздкой конструкции - что делает, что за параметры принимает, что возвращает, почему в TRY.



Исправлено 1 раз(а). Последнее : lulgu, 24.03.17 12:02
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
Перминов Игорь
Автор

Сообщений: 1591
Откуда: Красная Орловка
Дата регистрации: 16.09.2001
lulgu
Да, непонятно назначение вашей довольно громоздкой конструкции - что делает, что возвращает, почему в TRY.
Делает только то, что тестирует функцию из DLL скомпонованный в VS 2015.
А то что обернул так громоздко - пока голову ломал (заколебался на кнопку Продолжить давить), почему генерировалась ошибка Cannot find entry point _MyAdd in the DLL".
Теперь буду дальше писать в VS то, что задумал для использования в VFP.


------------------
Без коментариев..
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
lulgu

Сообщений: 1838
Дата регистрации: 30.11.2016
Вам надо предусмотреть стандартный возврат 0 при ошибке.
LPARAMETERS a,b
DECLARE DOUBLE MyAdd IN MailFuncsDLL.dll as "MyAdd" DOUBLE a, DOUBLE b
lnReturn = MyAdd(a,b)
IF lnReturn = 0
* Ошибка
ENDIF
CLEAR DLLS "MyAdd"
RETURN lnReturn
Ratings: 1 negative/0 positive
Re: Создать DLL на С++
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
lulgu
Вам надо предусмотреть стандартный возврат 0 при ошибке.
LPARAMETERS a,b
DECLARE DOUBLE MyAdd IN MailFuncsDLL.dll as "MyAdd" DOUBLE a, DOUBLE b
lnReturn = MyAdd(a,b)
IF lnReturn = 0
* Ошибка
ENDIF
CLEAR DLLS "MyAdd"
RETURN lnReturn

Боже мой, какой идиотизм...
Да, мой друг, это клинический случай. Когда функция сложения возвращает 0 "в случае ошибки" - тут медицина бессильна.

Для более адекватных читателей - try в коде тёзки ловит прежде всего ошибки при декларировании функции и при попытке её вызова. И лишь в самую последнюю очередь ошибки которые могли произойти ВНУТРИ этой функции.


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
lulgu

Сообщений: 1838
Дата регистрации: 30.11.2016
Ну да.
Все Winapi, в объеме MSDN это сплошной идиотизм, и каждый раз все DECLARE обвешиваются траями.
Вам дай волю, и вы со своей религией каждую запятую обернете в TRY.
Ratings: 1 negative/0 positive
Re: Создать DLL на С++
pasha_usue

Сообщений: 3650
Откуда: Е-бург
Дата регистрации: 06.10.2006
lulgu
Все Winapi, в объеме MSDN это сплошной идиотизм,
Вы себе придумали догму, которой в WINAPI нет. Во-первых, ошибка это не всегда ноль. Очень часто для обозначения ошибки WINAPI возвращает значения меньше нуля для знаковых возвращаемых данных (DialogBox, GetMessage).

И есть ряд функций, которые вообще не возвращают ошибку, например:
AnsiLower
AnsiUpper
CountClipoardFormats
CountVoiceNotes
EqualRect
EqualRgn
MakeLong



Исправлено 1 раз(а). Последнее : pasha_usue, 24.03.17 14:46
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
lulgu

Сообщений: 1838
Дата регистрации: 30.11.2016
pasha_usue
lulgu
Все Winapi, в объеме MSDN это сплошной идиотизм,
Вы себе придумали догму, которой в WINAPI нет. Во-первых, ошибка это не всегда ноль. Очень часто для обозначения ошибки WINAPI возвращает значения меньше нуля для знаковых возвращаемых данных (DialogBox, GetMessage).

И есть ряд функций, которые вообще не возвращают ошибку, например:
AnsiLower
AnsiUpper
CountClipoardFormats
CountVoiceNotes
EqualRect
EqualRgn
MakeLong

Ваши примеры только подтверждают общее правило. Если 0 по каким-либо причинам не подходит, то ничто не запрещает использовать другую цифру.
И эти функции не оборачивают в TRY, даже если они ничего не возвращают.
Если бы ТС не обернул свою конструкцию в трей, то он шел бы по шагам и отлавливал бы свои ошибки. Но TRY не позволил ему локализовать их.
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
pasha_usue

Сообщений: 3650
Откуда: Е-бург
Дата регистрации: 06.10.2006
lulgu
Ваши примеры только подтверждают общее правило. Если 0 по каким-либо причинам не подходит, то ничто не запрещает использовать другую цифру.
Какую цифру для возврата ошибки использует функция MakeLong?

lulgu
И эти функции не оборачивают в TRY, даже если они ничего не возвращают.
Если бы ТС не обернул свою конструкцию в трей, то он шел бы по шагам и отлавливал бы свои ошибки. Но TRY не позволил ему локализовать их.
А головой подумать? ТС отлаживал именно объявление функции в C++. После каждой попытки запуска ему надо было перекомпилировать DLL. И для того чтобы файл DLL был не занят на момент перекомпиляции, он в блоке FINALLY гарантированно этот файл высвобождал. Соответственно, он экономил кучу времени, что б вручную не освобождать DLL-ку.
Ну вы если сами не догоняете, вы спросите лучше вежливо.



Исправлено 1 раз(а). Последнее : pasha_usue, 24.03.17 15:43
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Человеку не понимающему смысл структурной обработки ошибок бессмысленно что-либо пояснять
Да, в нормальной программе использующей структурную обработку ошибок "любая запятая" находится внутри блока try. Более того, некоторые "запятые" находятся внутри 2, 3 а то и 4-х вложенных блоков try. И это правильно.

Да, декларации функций из стандартных виндовых библиотек так тщательно "проверять" не требуется - крайне маловероятно что в работающей Windows системе не будет найдена библиотека user32.dll или kernel32.dll - при их использовании главное не ошибиться с параметрами (не передать, к примеру, адрес указывающий на невыделенную память - хотя и такую ошибку фокс попытается "сгладить" - он сам "обвешивает" любой вызов внешней библиотеки своим внутренним try...catch обработчиком) - тогда шанс получить ошибку во время исполнения минимален, и её вполне может ловить обработчик ошибок самого верхнего уровня - тот что ловит все "непредусмотренные" ошибки (например самый внешний try в случае использования этого подхода к обработке ошибок). Однако когда речь заходит про какие-то "левые" (не системные) библиотеки, то такого рода проверка не будет лишней. И сама библиотека может отсутствовать, и экспортируемая функция может отсутствовать или отличаться параметрами от того как мы её описали - собственно говоря вся эта тема из того и выросла - что в dll НЕ БЫЛО экспортируемой функции с нужным именем.

А по поводу "возвращаемых значений" - 0 как "признак ошибки" не так уж и "повсеместно" используется. Он появляется в тех функциях которые возвращают BOOL значение и это именно "статус выполнения", и, иногда, указатели. При том, формально, это РАЗНЫЕ нули. FALSE в первом случае и NULL во втором.
А те функции что возвращают хендлы - там как раз -1 является признаком ошибки, т.к. именно таково значение константы INVALID_HANDLE_VALUE. А есть ещё куча функций возвращающих HRESULT - и там, о боже, какой ужас именно значение 0 является признаком УСПЕШНОГО выполнения. А разнообразные "ошибки" представлены отрицательными значениями...
Вообще трудно найти более убогую и непоследовательную "систему соглашений" о том как именно функция должна "сигнализировать" вызывающему коду о проблемах, нежели то что имеется в WindowsAPI - исторические причины, разнобой в разных командах писавших это хозяйство и мы имеем то что имеем - трэш, угар и содомию - никакого "единого подхода".

Вот в .NET есть единый подход - там любая "проблема" в вызываемом коде ДОЛЖНА сопровождаться генерацией исключения, и, соответственно, в вызывающем коде "ловиться" при помощи блока try ... catch. И это, кстати, строго, последовательно и правильно

В общем можно констатировать что очередной раз воинствующее невежество село в лужу.


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
lulgu

Сообщений: 1838
Дата регистрации: 30.11.2016
pasha_usue
Чуть-чуть выше я выложил простой типовой код для функций Winapi, который делает то же самое.
Что качается вашего примера, то функций в Winapi тыщи и я не занимаюсь пискомеряньем.

Igor Korolyov
Ваша способность заболтать любой вопрос порой просто удивляет.
Ладно еще, что не добавили: - "Волга впадает в Каспийское море".
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
pasha_usue

Сообщений: 3650
Откуда: Е-бург
Дата регистрации: 06.10.2006
lulgu
Чуть-чуть выше я выложил простой типовой код для функций Winapi, который делает то же самое.
Что то же самое? При CANCEL высвобождает файл? Вы лукавите и виляете. Просто потому что в каждом пункте "сели в лужу".

lulgu
Что качается вашего примера, то функций в Winapi тыщи и я не занимаюсь пискомеряньем.
:xixi: Виляете.

lulgu
Igor Korolyov
Ваша способность заболтать любой вопрос порой просто удивляет.
И виляете и лукавите одновременно.
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
lulgu

Сообщений: 1838
Дата регистрации: 30.11.2016
pasha_usue
Похоже, у вас опять очень быстро истощились аргументы, ненадолго вас хватает.

Этот типовой код подходит как обертка для большинства функций Winapi.
Мало того, даже в такой компактной обертке в Фоксе нет необходимости.
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
lulgu
Этот типовой код подходит как обертка для большинства функций Winapi.
Подходит. Для многих винАПИ функций вполне.

lulgu
Мало того, даже в такой компактной обертке в Фоксе нет необходимости.
Именно так - для большинства винАПИ функций (да и не только WinAPI) "обёртки" не нужны. Сама по себе команда DECLARE-DLL и создаёт "обёртку" позволяющую из фокса вызвать библиотечную функцию. Всё прочее - это уже комбинация нескольких функций для получения требуемого результата. Ну или скрытие нюансов трансформации параметров (структуры, или буфер памяти для возврата строки).

И что далее?
В смысл начального вопроса вникнуть никак?
И в банальный факт того что именно кодировалась в примере - уж проще сишного кода и придумать сложно...

Зато подмечать то чего нет, и влазить в спор о том чего не понимаете - это завсегда пожалуйста.


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
lulgu

Сообщений: 1838
Дата регистрации: 30.11.2016
Igor Korolyov
Если код подходит, то что вас взвинтило, что даже о достоинстве забыли?
Так нужна для DECLARE обертка или нет?
И зачем, по вашему, DECLARE нужно заворачивать в TRY, если она и так "создает обертку"?
Ratings: 0 negative/0 positive
Re: Создать DLL на С++
Перминов Игорь
Автор

Сообщений: 1591
Откуда: Красная Орловка
Дата регистрации: 16.09.2001
lulgu
pasha_usue
Чуть-чуть выше я выложил простой типовой код для функций Winapi, который делает то же самое.
Что качается вашего примера, то функций в Winapi тыщи и я не занимаюсь пискомеряньем.

Igor Korolyov
Ваша способность заболтать любой вопрос порой просто удивляет.
Ладно еще, что не добавили: - "Волга впадает в Каспийское море".
Не думал, что из такого примера можно раздуть слона.
Мой пример просто тестирует вызов MyAdd() из мной же написанной DLL на VC (C++) - и все.
Так что должна вернуть функция которая складывает 2 числа? 0 (ноль) или 1 (один).
Или может добавить третий параметр в котором будет возвращен результат сложения - блиииин.
Результатом возврата 0 или 1 может быть только логический результат выполнения некоторого действия а не принадлежность к API, т.е. например if (IsConnect) или if (a>b), and etc.
API это набор функций, классов, объектов, форм.
Вот когда мной задуманное будет реализовано в некий функционал, тогда всю эту сущность можно будет, с большой натяжкой, назвать API (application programming interface).


------------------
Без коментариев..




Исправлено 2 раз(а). Последнее : Перминов Игорь, 24.03.17 18:01
Ratings: 0 negative/0 positive


Извините, только зарегистрированные пользователи могут оставлять сообщения в этом форуме.

On-line: 31 hvh2007  (Гостей: 30)

© 2000-2024 Fox Club 
Яндекс.Метрика