Так как из библиотеки импортируется не само тело функции, а ее адрес, то вызов такой функции превращается в непростое дело. Прежде всего, в программе объявляется не сама функция, а переменная, содержащая указатель на нее. Естественно, работа с таким указателем сильно отличается от работы с указателем на число или строку. Ведь функция в отличие от просто переменной возвращает значение и принимает некоторые параметры, поэтому указатель на нее должен быть объявлен специальным образом.
Указатель на функцию, ипортируемую из dll-библиотеки должен также быть скомпилирован со специальным объявлением типа — __declspec(dllimport). Эту строку также удобно представить в виде директивы #define.
#define XDSPINTER_API __declspec(dllimport).
Мы импортируем из библиотеки четыре функции, поэтому необходимо определить их типы: параметры, передаваемые в функцию, возвращаемое значение. Это можно сделать при помощи директивы typedef:
//Объявить тип - указатель на функцию, возвращающую значение типа int и принимающую два
//параметра – массив типа char и число int. В библиотеке ей будет соответствовать функция
// EXPORT int ReadMem(char *data, int len)
typedef XDSPINTER_API int (*MemReadFun)(char *data, int len);
// EXPORT int WriteMem(char *data, int len)
typedef XDSPINTER_API int (*MemWrtFun)(char *data, int len);
// EXPORT int GetMemSize(void)
typedef XDSPINTER_API int (*MemSizeFun)();
//EXPORT bool IsDriverPresent(void)
typedef XDSPINTER_API bool (*IsDrivFun)();
Теперь пришло время создать сами указатели на функции:
MemReadFun ReadMem;
MemWrtFun WriteMem;
MemSizeFun GetMemSize;
IsDrivFun IsDriverPresent;
Теперь рассмотрим функцию, подключающую dll-библиотеку к приложению. Она будет подключать dll-библиотеку к приложению и пытаться установить связь с драйвером. Функция вернет true в случае успеха и false при неудаче. Т.к. VC++ — объектно-ориентированная среда, то эта функция будет методом одного из классов приложения (в нашем случае — класса представления).
bool CXDSPView::ConnectToDriver() {
//Переменная, в которой будет храниться возвращаемое значение.
success=true;
//HMODULE InterDll – переменная экземпляра, где хранится хэндл библиотеки.
InterDll=::LoadLibrary("XDSPInter");
if (InterDll==NULL) {
//Не удалось подключиться к библиотеке
AfxMessageBox("Couldn't load a library XDSPInter.dll",MB_ICONERROR | MB_OK);
//Вернем неудачу.
success=false;
} else {
//Библиотека подключена успешно. Импортируем функции.
ReadMem=(MemReadFun)::GetProcAddress(InterDll,"ReadMem");
if (ReadMem==NULL) {
//Не удалось импортировать функцию
AfxMessageBox("Couldn't get adress for ReadMem function from library XDSPInter.dll", MB_ICONERROR | MB_OK);
success=false;
}
WriteMem=(MemReadFun)::GetProcAddress(InterDll,"WriteMem");
if (WriteMem==NULL) {
//Не удалось импортировать функцию
AfxMessageBox("Couldn't get an adress for WriteMem function from library XDSPInter.dll", MB_ICONERROR | MB_OK);
success=false;
}
GetMemSize=(MemSizeFun)::GetProcAddress(InterDll,"GetMemSize");
if (GetMemSize==NULL) {
//Не удалось импортировать функцию AfxMessageBox("Couldn't get an adress for GetMemSize function from library XDSPInter.dll", MB_ICONERROR | MB_OK);
success=false;
}
IsDriverPresent=(IsDrivFun)::GetProcAddress(InterDll,"IsDriverPresent");
if (IsDriverPresent==NULL) {
//Не удалось импортировать функцию
AfxMessageBox("Couldn't get an adress for IsDriverPresent function from library XDSPInter.dll", MB_ICONERROR | MB_OK);
success=false;
}
}
return(success);
}
Вызов метода ConnectToDriver() целесообразно сделать в конструкторе класса. Там же надо реализовать и проверку, присутствует ли в системе драйвер. Тогда вся необходимая инициализация будет проведена еще при запуске приложения.
CXDSPView::CXDSPView() : CFormView(CXDSPView::IDD) {
//{{AFX_DATA_INIT(CXDSPView)
//}}AFX_DATA_INIT
//Здесь мы добавляем свой код. Success – переменная экземпляра. Если она
//равна true – то ошибок нет, иначе произошла какая-то ошибка.
success=true;
//Пробуем подключить dlclass="underline"
if (ConnectToDriver()) {
//Удалось подключить библиотеку. Теперь пытаемся установить связь с
//драйвером – вызываем функцию в dlclass="underline"
if (!IsDrvPresent()) {
//Неудача
success=false;
AfxMessageBox("Necessary driver isn't present in the system",MB_ICONERROR | MB_OK);
}
} else
//Не удалось подключиться к dll.
success=false;
}
Метод, производящий чтение памяти устройства может выглядеть следующим образом:
void CXDSPView::OnRead() {
int res; //Количество слов, прочитанных из памяти
res=(*ReadMem)(dt,256); //Пытаемся читать 256 слов.
m_buff.SetWindowText(dt); //Выводим данные на экран
//Код, характерный для VC++.
CXDSPDoc *m_doc; //Подключаем документ, связанный с представлением
m_doc=GetDocument();
//копируем туда данные.
strcpy((char*)m_doc->m_buffer,dt);
//Примечание: оба буфера должны иметь достаточный объем – минимум
//256*4+1 байт.
}
Аналогично может выглядеть метод записи в память устройство:
void CXDSPView::OnWrite() {
//Получили данные, введенный пользователем
m_buff.GetWindowText(dt,32767);
int res;
//Записываем его в память устройства. Заметим, что в качестве длины данных
//мы передаем не длину в байтах, а в 4-байтых словах.
res=(*WriteMem)(dt,strlen(dt)%4+1);
}
Метод, возвращающий длину памяти устройства, совсем прост и, думаю, в комментариях не нуждается.
int CXDSPView::GetTotalLen() {
int res=(*GetMemSize)();
return(res);
}
Также введем еще один метод, который может быть полезным. Он будет очищать память устройства.
void CXDSPView::OnClear() {
//Получили документ
CXDSPDoc *m_doc;
m_doc=GetDocument();
//Забиваем буфер нулями
for (int i=0;i<1025;i++) dt[i]=0;
//Обнуляем буфер в классе документа