PDA

View Full Version : QLibrary: how to download and check?



lucky_sever
12th April 2012, 16:09
There are some programs in C++


#include <cstdlib>
#include <iostream>
#include <windows.h>
#include "AtUsbHid.h"

using namespace std;

int main(int argc, char *argv[])
{
HINSTANCE hLib = NULL;
hLib =LoadLibrary(AT_USB_HID_DLL);
if(hLib == NULL){
cout << "boing\n";
}
loadFuncPointers(hLib);

DYNCALL(findHidDevice)(Vid, Pid);
DYNCALL(hidRegisterDeviceNotification)((m_hWnd));

system("PAUSE");
return EXIT_SUCCESS;
}

This realization is already on the QT. What am I doing wrong?


#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QLibrary>
#include <QMessageBox>
#include <cstdlib>
#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include <stddef.h>
#include <windowsx.h>

// Include Atmel Hid Usb
#include "AtUsbHid.h"

#define DEFAULT_VID 0x03EB
#define DEFAULT_PID 0x2013



MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);

}

MainWindow::~MainWindow()
{
delete ui;

Vid = DEFAULT_VID;
Pid = DEFAULT_PID;
}



void MainWindow::on_ConnectUsb_clicked()
{
QLibrary hLib(AT_USB_HID_DLL);
hLib.load();

if (!hLib.load())
{
QMessageBox::warning(this, tr("Error"),
tr("Not found AtUsbHid.dll"),
QMessageBox::Ok);
return;
}
//************************************************** *********
typedef void (*func)();
func PF_findHidDevice = (func) hLib.resolve("findHidDevice)(Vid, Pid)");
if (!PF_findHidDevice)
{
qDebug ("!!!!!!!!findHidDevice");
return;
}


typedef void (*func)();
func PF_hidRegisterDeviceNotification = (func) hLib.resolve("hidRegisterDeviceNotification");
if (!PF_hidRegisterDeviceNotification)
{
qDebug ("!!!!!!!!hidRegisterDeviceNotification");
return;
}
//************************************************** *********
}

d_stranz
12th April 2012, 18:01
Have you read the QLibrary documentation? From the docs:



The symbol must be exported as a C function from the library for resolve() to work. This means that the function must be wrapped in an extern "C" block if the library is compiled with a C++ compiler. On Windows, this also requires the use of a dllexport macro; see resolve() for the details of how this is done.


Do you know anything about your library? Is it a C or a C++ library?

What do you see when you look at it using the Visual Studio "dumpbin" tool? (Start a Visual Studio command window, cd to your dll folder, and type "dumpbin /EXPORTS AtUsbHid.dll"). This will show you the names of all of the exported classes and methods in the DLL. If you see things like this (from the Qwt 6.0 DLL):

?pinPoint@QwtSymbol@@QBE?AVQPointF@@XZ

then this means your library is a C++ library and has C++ mangled names. If you see something like this (from pgort90.dll, in the Visual Studio distribution):

IrtAutoSweepA

then this means it is a C library, or that the symbols in it were exported using the extern "C" declaration described in the docs.

lucky_sever
12th April 2012, 18:39
library C++

d_stranz
12th April 2012, 18:42
OK, then try using resolve() with one of the mangled names you see in that dump, and see if that succeeds.

lucky_sever
12th April 2012, 19:28
So I try to resolve (), the above code. But something does not work.

d_stranz
12th April 2012, 19:34
Is there some reason why you hve to load this DLL at run time? Don't you have an import library (.lib) that you can link to, so the DLL can be loaded automatically when the program runs? You have the header file (.h), so why not use the .lib at link time?

And did you look at the value return by the QLibrary::errorString() method to see what it says when the resolve() call fails?

lucky_sever
12th April 2012, 20:10
dumpbin.exe /exports


Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.


Dump of file AtUsbHid.dll

File Type: DLL

Section contains the following exports for AtUsbHid.dll

00000000 characteristics
479752C1 time date stamp Wed Jan 23 16:44:17 2008
0.00 version
1 ordinal base
21 number of functions
21 number of names

ordinal hint RVA name

2 0 0000198D closeDevice
3 1 000011CF findHidDevice
4 2 00002103 getFeatureReportLength
5 3 000020F3 getInputReportLength
6 4 000020E3 getOutputReportLength
7 5 00001EB0 getch
8 6 00001FA0 hidRegisterDeviceNotification
9 7 00001F8F hidUnregisterDeviceNotification
10 8 00001FFD isMyDeviceNotification
11 9 00001000 openDevice
12 A 00001E62 putch
13 B 00001CE5 readContinuous
14 C 00001D1F readData
15 D 00001C13 readStandard
16 E 0000204D setFeature
17 F 00001AE7 setReportContinuous
18 10 00001F63 setTxPeriod
1 11 00001DED updateFeatureBytes
19 12 00001D2F writeContinuous
20 13 00001DDD writeData
21 14 00001B1D writeStandard

Summary

5000 .data
2000 .rdata
1000 .reloc
1000 .rsrc
8000 .text

d_stranz
12th April 2012, 20:28
This is a C library, not a C++ library.

Create a Qt Console Application, and insert this code in main(). What do you see when this code runs?



#include <QtCore/QCoreApplication>
#include <QLibrary>
#include <iostream>

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);


QLibrary qLib( "AtUsbHid.dll" );
if ( qLib.load() )
{
void * func = qLib.resolve( "openDevice" );
if ( 0 == func )
std::cout << qLib.errorString().toStdString() << std::endl;
else
std::cout << "No errors" << std::endl;
}
else
std::cout << qLib.errorString().toStdString() << std::endl;
return a.exec();
}

lucky_sever
12th April 2012, 21:14
C:\Qt\errorString-build-desktop-Qt_4_8_0_for_Desktop_-_MSVC2010__Qt_SDK_________\..\errorString\main.cpp :-1: error: C1902: ЌҐб®®вўҐвбвўРÒ ¤ЁбЇҐвзҐа* ÐŽ*§л ¤***ле Їа®Ја*¬¬л; Їа®ўҐам⥠гбв**®ў«Ґ**го Є®ЇЁо

I have a library AtUsbHid.dll header atusbhid.h



#ifndef _ATUSBHID_H_
#define _ATUSBHID_H_

// Error codes.
#define ERROR_USB_DEVICE_NOT_FOUND 0xE0000001
#define ERROR_USB_DEVICE_NO_CAPABILITIES 0xE0000002

// name of the DLL to be loaded
#define AT_USB_HID_DLL "AtUsbHid"

// Implement the DLL export/import mechanism and allow a C-written program
// to use our DLL.
#ifdef ATUSBHID_EXPORTS
#define ATUSBHID_API extern "C" __declspec(dllexport)
#else
#define ATUSBHID_API extern "C" __declspec(dllimport)
#endif


// This macro function calls the C runtime's _beginthreadex function.
// The C runtime library doesn't want to have any reliance on Windows' data
// types such as HANDLE. This means that a Windows programmer needs to cast
// values when using _beginthreadex. Since this is terribly inconvenient,
// this macro has been created to perform the casting.
typedef unsigned(__stdcall *PTHREAD_START)(void *);

#define chBEGINTHREADEX(psa, cbStack, pfnStartAddr, \
pvParam, fdwCreate, pdwThreadId) \
((HANDLE)_beginthreadex( \
(void *) (psa), \
(unsigned) (cbStack), \
(PTHREAD_START) (pfnStartAddr), \
(void *) (pvParam), \
(unsigned) (fdwCreate), \
(unsigned *)(pdwThreadId)))

// Allow applications not built with Microsoft Visual C++ to link with our DLL.
#define STDCALL __stdcall

// These macros make calling our DLL functions through pointers easier.
#define DECLARE_FUNCTION_POINTER(FUNC) PF_##FUNC lp##FUNC=NULL;
#define LOAD_FUNCTION_POINTER(DLL,FUNC) lp##FUNC = (PF_##FUNC)GetProcAddress(DLL, #FUNC);
#define ADDR_CHECK(FUNC) if (lp##FUNC == NULL) {fprintf(stderr,"%s\n", "Error: Cannot get address of function."); return FALSE;}
#define DYNCALL(FUNC) lp##FUNC


///////////////////////////////////////////////////////////////////////////////
typedef BOOLEAN (STDCALL *PF_findHidDevice)(const UINT VendorID, const UINT ProductID);
typedef void (STDCALL *PF_closeDevice)(void);
typedef BOOLEAN (STDCALL *PF_writeData)(UCHAR* buffer);
typedef BOOLEAN (STDCALL *PF_readData)(UCHAR* buffer);
typedef int (STDCALL *PF_hidRegisterDeviceNotification)(HWND hWnd);
typedef void (STDCALL *PF_hidUnregisterDeviceNotification)(HWND hWnd);
typedef int (STDCALL *PF_isMyDeviceNotification)(DWORD dwData);
typedef BOOLEAN (STDCALL *PF_setFeature)(UCHAR* buffer);
typedef int (STDCALL *PF_getFeatureReportLength)();
typedef int (STDCALL *PF_getInputReportLength)();
typedef int (STDCALL *PF_getOutputReportLength)();

///////////////////////////////////////////////////////////////////////////////

// Exported functions prototypes.

///////////////////////////////////////////////////////////////////////////////
ATUSBHID_API BOOLEAN STDCALL findHidDevice(const UINT VendorID, const UINT ProductID);

// Closes the USB device and all handles before exiting the application.
ATUSBHID_API void STDCALL closeDevice(void);

ATUSBHID_API BOOLEAN STDCALL writeData(UCHAR* buf);

ATUSBHID_API BOOLEAN STDCALL readData(UCHAR* buffer);

ATUSBHID_API int STDCALL hidRegisterDeviceNotification(HWND hWnd);

ATUSBHID_API void STDCALL hidUnregisterDeviceNotification(HWND hWnd);

ATUSBHID_API int STDCALL isMyDeviceNotification(DWORD dwData);

ATUSBHID_API BOOLEAN STDCALL setFeature(UCHAR *buffer);

ATUSBHID_API int STDCALL getFeatureReportLength(void);

ATUSBHID_API int STDCALL getOutputReportLength(void);

ATUSBHID_API int STDCALL getInputReportLength(void);

///////////////////////////////////////////////////////////////////////////////

#ifndef ATUSBHID_EXPORTS


DECLARE_FUNCTION_POINTER(findHidDevice);
DECLARE_FUNCTION_POINTER(closeDevice);
DECLARE_FUNCTION_POINTER(writeData);
DECLARE_FUNCTION_POINTER(readData);
DECLARE_FUNCTION_POINTER(hidRegisterDeviceNotifica tion);
DECLARE_FUNCTION_POINTER(hidUnregisterDeviceNotifi cation);
DECLARE_FUNCTION_POINTER(isMyDeviceNotification);
DECLARE_FUNCTION_POINTER(setFeature);
DECLARE_FUNCTION_POINTER(getFeatureReportLength)
DECLARE_FUNCTION_POINTER(getOutputReportLength)
DECLARE_FUNCTION_POINTER(getInputReportLength)

// this function call all function available in the DLL *
static bool loadFuncPointers(HINSTANCE hLib)
{
LOAD_FUNCTION_POINTER(hLib, findHidDevice);
ADDR_CHECK(findHidDevice);

LOAD_FUNCTION_POINTER(hLib, closeDevice);
ADDR_CHECK(closeDevice);

LOAD_FUNCTION_POINTER(hLib, writeData);
ADDR_CHECK(writeData);

LOAD_FUNCTION_POINTER(hLib, readData);
ADDR_CHECK(readData);

LOAD_FUNCTION_POINTER(hLib, hidRegisterDeviceNotification);
ADDR_CHECK(hidRegisterDeviceNotification);

LOAD_FUNCTION_POINTER(hLib, hidUnregisterDeviceNotification);
ADDR_CHECK(hidUnregisterDeviceNotification);

LOAD_FUNCTION_POINTER(hLib, isMyDeviceNotification);
ADDR_CHECK(isMyDeviceNotification);

LOAD_FUNCTION_POINTER(hLib, setFeature);
ADDR_CHECK(setFeature);

LOAD_FUNCTION_POINTER(hLib, getOutputReportLength);
ADDR_CHECK(getOutputReportLength);

LOAD_FUNCTION_POINTER(hLib, getInputReportLength);
ADDR_CHECK(getInputReportLength);

LOAD_FUNCTION_POINTER(hLib, getFeatureReportLength);
ADDR_CHECK(getFeatureReportLength);

return true;
}

#endif

#endif // _ATUSBHID_H_


Added after 13 minutes:

The console displays "No errors"

d_stranz
12th April 2012, 22:01
The console displays "No errors"

OK, this means the library has been loaded, and you have a function pointer to the "openDevice" method. So, it is working. From you r header file, it looks like all of the exports except for openDevice have function prototypes in the .h file. I do not have any idea how you use this library, so I cannot help you with that.

These lines in the .h file:



DECLARE_FUNCTION_POINTER(findHidDevice);
DECLARE_FUNCTION_POINTER(closeDevice);
//...


are declaring variables like this:


PF_findHidDevice lpfindHidDevice = NULL;
PF_closeDevice lpcloseDevice = NULL;
// ...

So your Qt code, I think you need to do something like this after you load the library:



lpfindHidDevice = qLib.resolve( "findHidDevice" );
lpcloseDevice = qLib.resolve( "closeDevice" );

// etc.


To use one of these functions, you need to look at its signature in the .h file, and pass the correct arguments to it. For example, "findHidDevice" expects two arguments: an unsigned integer VendorID, and an unsigned integer ProductID. I assume you know what these are, so you can assign correct values to them. The DYNCALL() macro in the .h file allows you to call that method in your code like this:



UINT vendorID = 123; // I made this up...
UINT productID = 456; // and this too. You have to know the correct IDs when you call this method
BOOLEAN bFound = DYNCALL(findHidDevice)( vendorID, productID );

if ( bFound == FALSE )
// then it didn't find the device you were looking for
else
{
// found it, now do something with it
}


Added after 12 minutes:

Google shows me this link for the Atmel USB Microcontrollers Application Note. (www.atmel.com/Images/doc7645.pdf) This looks like the library you are using, so if you do not have this document, you should probably get it so you know how to use the library.

lucky_sever
12th April 2012, 22:13
This library is used for communication AtUsbHid.DLL Atmel microcontroller at90usb PC. The program connects to USB-device and communicates with the library.

Added after 6 minutes:

Thank you very much! I have this article, and even have a program written in Visual C + +. But I can not recompile it on the QT.

lucky_sever
13th April 2012, 09:44
I'm doing right?



#include "AtUsbHid.h"

#define DEFAULT_VID 0x03EB
#define DEFAULT_PID 0x2013

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}

MainWindow::~MainWindow()
{
delete ui;
vendorID = DEFAULT_VID;
productID = DEFAULT_PID;
}

void MainWindow::on_ConnectUsb_clicked()
{
//************************************************** *********
QLibrary hLib(AT_USB_HID_DLL);
hLib.load();

if (!hLib.load())
{
QMessageBox::warning(this, tr("Error"),
tr("Not found AtUsbHid.dll"),
QMessageBox::Ok);
return;
}

//************************************************** *********

typedef void (*lpfindHidDevice)();
typedef void (*lphidRegisterDeviceNotification)();

PF_findHidDevice lpfindHidDevice = NULL;
PF_hidRegisterDeviceNotification lphidRegisterDeviceNotification = NULL;

lpfindHidDevice = hLib.resolve( "findHidDevice" );
lphidRegisterDeviceNotification = hLib.resolve( "closeDevice" );

BOOLEAN bFound = DYNCALL(findHidDevice)( vendorID, productID );
if ( bFound == FALSE )
{
qDebug ("Not DYNCALL findHidDevice");
}
else
{
qDebug ("DYNCALL findHidDevice");
}

wysota
13th April 2012, 10:43
I would like to return to one of the earlier questions asked. Why are you loading the library runtime instead of linking to it at compile time? Do you have a .lib file corresponding to the DLL file you are using?

lucky_sever
13th April 2012, 11:10
I have only AtUsbHid.dll and AtUsbHid.h.

And how to specify a reference to it at compile time?

wysota
13th April 2012, 15:29
I have only AtUsbHid.dll and AtUsbHid.h.
You can probably recreate the import library using one of tools available for that task.


And how to specify a reference to it at compile time?
Add a LIBS += -Ldirectorycontainingthelibfile -lAtUsbHid entry in your project file. Then you can use all the functions directly in your code without QLibrary or similar solutions.

d_stranz
13th April 2012, 16:02
You can probably recreate the import library using one of tools available for that task.

Apparently Microsoft doesn't supply any.

Here is a link to a CodeProject (http://www.codeproject.com/Articles/146652/Creating-Import-Library-from-a-DLL-with-Header-Fil) article that describes the process. It looks very painful, even for a simple library such as this one.

I downloaded the distribution package from Atmel that the OP refers to, and indeed, there is no import library, just a DLL and header. The example code provided in the package uses LoadLibrary and runtime resolution of the linkages. Worse, the header file declares global variables in such a way that it can't be included in more than one source file without getting multiply-defined reference errors at link time. So you are faced with either putting all of your code that uses the library into a single source file, rewriting the header to remove the variable declarations, or ignoring it altogether and doing your own header. I would opt for this last option.

Further, the use of the library is intricately linked to the Windows messaging system. It uses WM_DEVICECHANGE messages to manage communication between the program and the Atmel controller. Fortunately, I think QWidget::winEvent() can be used to intercept and handle these.

And finally, it also looks like the library can be used to communicate with only one device at a time; none of the methods in the DLL have any reference to a device ID, so commands to send and receive data appear to interact with whatever device was last connected.

Atmel is a major supplier of embedded controllers. It seems sort of embarrassing that their software offering is so primitive.

Added after 7 minutes:


I'm doing right?

I know you are having a hard time getting started. You picked a difficult project to learn Qt, because this DLL uses techniques that are unusual in the Qt world.

I have the example project from Atmel that uses this library (UsbHidDemoCode). I will convert this to a Qt project for you and post the code here. It is a pretty simple GUI. However, I do not have any way to test the code since I do not have the Atmel board. You will have to test that yourself.

wysota
13th April 2012, 18:11
Apparently Microsoft doesn't supply any.
It doesn't have to be of Microsoft origin. This is a C library, so no name mangling is involved. I think some simple scanner that discovers symbol addresses should be enough.

http://www.mingw.org/wiki/CreateImportLibraries

ChrisW67
13th April 2012, 21:57
If the OP is using the MingW compiler then chances are it will Just Work(tm) without an import library as long as it sees the DLL. Exactly the same LIBS incantations.

d_stranz
14th April 2012, 05:51
No, he's using Visual Studio. Anyway, I have duplicated the Atmel demo project in Qt using the run-time QLibrary loading, and it will probably work fine.

@lucky_sever: In the attached ZIP file is a Visual Studio 2008 project that implements the Atmel UsbHidDemoCode project as a Qt version.

Because I do not have an Atmel board, I cannot test the code completely. However, I have added an "emulation mode" that responds to commands in the same way that the Atmel project does. There is one line in the UsbHidDlg.cpp file that must be changed in order to turn off emulation mode:



void CUsbHidDlg::showEvent( QShowEvent * pEvent )
{
// TESTING ONLY - remove this when a live board is present (or change "true" to "false")
mpController->setEmulating( true );

if ( mpController->initialize( "AtUSBHid.dll" ) )
mpConnectionStatusCtrl->setText( "Library loaded" );
else
mpConnectionStatusCtrl->setText( "Library load failed" );
}


The Atmel project mixed up communication with the board and the GUI dialog. I have split this into two classes: CUSBHidDlg and CUSBHidController.

CUSBHidDlg is just the user interface. It does not know anything about the Atmel library. It creates an instance of CUSBHidController, which is the class that communicates with the board through the Atmel DLL. It is a QWidget-based class, and emits status and device connection signals.

Because the Atmel library uses the Windows WM_DEVICECHANGE message to manage some communication with the board and operating system, it was necessary to override the QWidget::winEvent() method in CUSBHidController. This new code watches for the Atmel board being connected or disconnect from its USB port; but otherwise simply passes all other Windows messages along to Qt for handling.

The ZIP file contains a complete project. Unzip it into a new directory to build it. You might have to change some of the VC++ project settings to make sure it finds the Qt header and library files correctly. Make sure that the Qt DLLs are in your PATH environment so the exe can find them at run time.

I have not put too many comments into the code. Most of the methods are small and very similar to the Atmel demo code. If you have any questions, please ask.

7587

lucky_sever
14th April 2012, 12:01
Excellent! Excellent! Excellent! Everything works: connects, receives and sends data.

I could not write this! Tell me, please, and whether such a program to move to the Tablet PC on the android with slight modifications?

Added after 37 minutes:

Here are just at the close of the application in debug mode segfault.
And wrote an unused variable closeDevice.


DYNCALL (closeDevice) ();


Added after 23 minutes:

warning: unused variable 'result'
Swears on this line!



int result = DYNCALL (hidRegisterDeviceNotification) (winId ());


You do not think I'm not complaining about the application, but only a share.

d_stranz
14th April 2012, 18:12
You do not think I'm not complaining about the application, but only a share.

I understand. Remember, I cannot test the code, because I do not have an Atmel board. So maybe there are a few things that do not work correctly; if the DYNCALL(closeDevice)() call causes a segfault, then it should be removed or moved to a different place. I am not sure why the segfault occurs - possibly the library is already destroyed before this call takes place.

You could try to call mpController->shutdown() from inside CUSBHidDlg. Add a new method, a protected slot:



// CUSBHidDlg.h

protected slots: // This section already exists, just add a new method declaration
void accept();

// CUSBHidDlg.cpp:

void CUSBHidDlg::accept()
{
mpController->shutdown();
CDialog::accept();
}



Tell me, please, and whether such a program to move to the Tablet PC on the android with slight modifications?

No, I do not think you can use this program on the Tablet PC, because the DLL is for Windows only and I do not think it can work on the Android OS. If Atmel has a similar library for Android, then maybe it can be modified to work there. Otherwise, you will have to find some code that talks to USB HID devices on Android OS and try to use that.

lucky_sever
14th April 2012, 19:33
Tell me, please. Such a question. Is it possible to add a button that will ignite at the same time, all four LEDs?

d_stranz
16th April 2012, 23:31
Tell me, please. Such a question. Is it possible to add a button that will ignite at the same time, all four LEDs?

Yes, of course. Open the ui file in Qt Designer, add the new button, then add the slot in the .h file, implement the code for the slot in the .cpp file. In the slot, write the commands to turn on all of the LEDs. You should be able understand enough of the code that I wrote to be able to do this without help.

lucky_sever
17th April 2012, 10:44
Thank you! I almost figured out the code. Now modify the program. I am interested in this question


int result = DYNCALL (hidRegisterDeviceNotification) (winId ());

hidRegisterDeviceNotification where you use the program? I did not find anywhere else.

Added after 52 minutes:



bool CUSBHidController::shutdown()
{
if ( isConnected() )
disconnectDevice();

DYNCALL(hidUnregisterDeviceNotification( winId() ));

if ( mUSBHidLib.isLoaded() )
mUSBHidLib.unload();


When you close the program in debug mode - a segfault on the call DYNCALL (hidUnregisterDeviceNotification (winId ()));. It turns out that DYNCALL (hidRegisterDeviceNotification) (winId ()) not yet due, but just checking!

lucky_sever
18th April 2012, 07:03
That's it! The question is exhausted! I fully understand the code. Thank you very much! That's only one did not understand where it is used "result?

int result = DYNCALL (hidRegisterDeviceNotification) (winId ());