PDA

View Full Version : ActiveX | from MFC to Qt5



magrif
14th October 2015, 12:22
I bought Fingerprint reader ZK7500, it SDK is written in MFC. I want to remake it to Qt.

1):confused: to code


QAxWidget *ax = new QAxWidget();
ax->setControl(QString::fromUtf8("{CA69969C-2F27-41D3-954D-A48B941C3BA7}"));

I received a warning


QAxBase: Unhandled type LPSTR


This is critical?

2):confused: at MFC inbound events handled as follows:


BEGIN_EVENTSINK_MAP(CDemoDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CDemoDlg)
ON_EVENT(CDemoDlg, IDC_ZKFPENGX2, 8 /* OnImageReceived */, OnOnImageReceivedZkfpengx2, VTS_PBOOL)
ON_EVENT(CDemoDlg, IDC_ZKFPENGX2, 10 /* OnCapture */, OnOnCaptureZkfpengx2, VTS_BOOL VTS_VARIANT)
ON_EVENT(CDemoDlg, IDC_ZKFPENGX2, 9 /* OneEnroll */, OnOnEnrollZkfpengx2, VTS_BOOL VTS_VARIANT)
ON_EVENT(CDemoDlg, IDC_ZKFPENGX2, 5 /* OnFeatureInfo */, OnOnFeatureInfoZkfpengx2, VTS_I4)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()


How to do it on Qt? I have not found *.TLB file with definitions of functions.

3):confused: I have many functions generated by MFC Wizard such as:


void SaveJPG(LPCTSTR FileName)
{
static BYTE parms[] = VTS_BSTR ;
InvokeHelper(0x18, DISPATCH_METHOD, VT_EMPTY, NULL, parms, FileName);
}
long InitEngine()
{
long result;
InvokeHelper(0x1a, DISPATCH_METHOD, VT_I4, (void*)&result, NULL);
return result;
}
...


I found this topic (http://www.qtcentre.org/threads/231-Qt-application-with-live-Active-X-camera-feed?), but did not work out. How it correctly to remake to Qt.

Sorry for my Google Translate.

anda_skoa
14th October 2015, 12:52
Maybe check if the vendor has a real SDK, one that does not require any specific UI, at least not one abandoned years ago.

Cheers,
_

d_stranz
14th October 2015, 15:26
I have not found *.TLB file with definitions of functions.

You can #import (using MSVC) the DLL file, and it will create the .tli and .tlh files with the interface information. See this (https://msdn.microsoft.com/en-us/library/8etzzkb6.aspx).

That might be enough to get you started wrapping the interface in Qt ActiveX methods calls.

magrif
19th November 2015, 16:02
Thanks for you answer.

I'm found interface information with help Windows tool OLEVIEW.EXE (open only with administrator rights). In the branch Type Libraries found my lib.

My solutions:

1) :)



class CZKFPEngX : public QAxObject
{
Q_OBJECT
public:
CZKFPEngX()
{
setControl(QString::fromUtf8("{CA69969C-2F27-41D3-954D-A48B941C3BA7}"));
connect(this,SIGNAL(exception(int,QString,QString, QString)),this,SLOT(slotException(int,QString,QStr ing,QString)));
}
...
};



2) :)

CZKFPEngX zkfpEng;
connect(&zkfpEng,SIGNAL(OnImageReceived(bool&)), SLOT(imageReceived(bool&)));
connect(&zkfpEng,SIGNAL(OnEnroll(bool,QVariant)), SLOT(enroll(bool,QVariant)));
connect(&zkfpEng,SIGNAL(OnCapture(bool,QVariant)), SLOT(capture(bool,QVariant)));
connect(&zkfpEng,SIGNAL(OnFeatureInfo(int)), SLOT(featureInfo(int)));

3) :)


void SaveJPG(QString FileName)
{
dynamicCall("SaveJPG(const QString&)",FileName);
}
int InitEngine()
{
int result = dynamicCall("InitEngine()").toInt();
return result;
}

New question
4) :confused:
I have method in CZKFPEngX:

bool GetFingerImage(QVariant& AFingerImage)
{
//bool result;
//static BYTE parms[] = VTS_PVARIANT ;
//InvokeHelper(0x2e, DISPATCH_METHOD, VT_BOOL, (void*)&result, parms, AFingerImage);
//return result;

bool result = dynamicCall("GetFingerImage(QVariant&)",AFingerImage).toBool(); //result is true, if finger was captured
return result;
}

When i'm invoke it from my dialog class:

void CDemoDlg::test()
{
QVariant image;
qDebug() << image.isNull();
qDebug() << zkfpEng.GetFingerImage(image);
qDebug() << image.isNull();
QImage img = image.value<QImage>();
if(!img.save("image.bmp","BMP"))
qDebug() << "some error";
}
at console:

true
true
true
some error

i.e. object object image is not written to by reference. With other similar methods the same situation. What am I doing wrong? Thanks.

d_stranz
19th November 2015, 17:30
A COM VARIANT is not a Qt QVariant. You can't pass a Qt QVariant by reference to a COM Dispatch function that expects a COM VARIANT pointer, which is what the COM method signature you have in the comment in GetFingerImage() is telling you.

You will have to google some more for what a COM VARIANT really looks like and whether Qt's ActiveX module offers any conversion between them.

magrif
19th November 2015, 17:57
But at docs (http://doc.qt.io/qt-5/qaxbase.html)says:
COM type___Qt property___in-parameter___out-parameter
VARIANT___type-dependent___const QVariant&___QVariant&

and this COM code:


dispinterface IControl
{
properties:
methods:
[id(4)] int fillList([in, out] SAFEARRAY(VARIANT) *list);
};

at Qt:

QList<QVariant> varlist;
QList<QVariant> parameters;
parameters << QVariant(varlist);
int n = object.dynamicCall("fillList(QList<QVariant>&)", parameters).toInt();

d_stranz
19th November 2015, 18:36
OK, it looks like the Qt Ax module does handle QVariant <-> VTS_PVARIANT conversion correctly. Do you know for certain that the reader creates something that can be converted to a QImage?

Your qDebug() statements seem to say that even though the dynamicCall seems to succeed, the QVariant is still uninitialized.

Try initializing the QVariant to a type before passing it in. I seem to remember from my ancient days in COM-land that this might be necessary in some cases.



void CDemoDlg::test()
{
QVariant image( QVariant::Image ); // or QVariant::Bitmap or QVariant::Pixmap or QVariant::ByteArray
qDebug() << image.isNull();
qDebug() << zkfpEng.GetFingerImage(image);
qDebug() << image.isNull();
QImage img = image.value<QImage>();
if(!img.save("image.bmp","BMP"))
qDebug() << "some error";
}

magrif
19th November 2015, 19:21
@#$%&,
QVariant QAxBase::dynamicCall(const char * function, const QVariant & var1 = QVariant(), ...)

need overload dynamicCall(const char * function, QList<QVariant> & vars)

for this method it works:

int IdentificationInFPCacheDB(int fpcHandle, QVariant pVerTemplate, int& Score, int& ProcessedFPNumber)
{
QList<QVariant> var;
var << QVariant(fpcHandle) << pVerTemplate << QVariant(Score) << QVariant(ProcessedFPNumber);
int result = dynamicCall("IdentificationInFPCacheDB(int,QVariant,int&,int&)",var).toInt();
Score = var.at(2).toInt();
ProcessedFPNumber = var.at(3).toInt();
return result;
}

but with QVariant still not working...

magrif
20th November 2015, 16:00
Try invoke directly through interface:


class CZKFPEngX : public QAxObject
{
Q_OBJECT
IDispatch *iface;

public:
CZKFPEngX()
{
setControl(QString::fromUtf8("{CA69969C-2F27-41D3-954D-A48B941C3BA7}"));
connect(this,SIGNAL(exception(int,QString,QString, QString)),this,SLOT(slotException(int,QString,QStr ing,QString)));

iface = 0;
QUuid id(QString::fromUtf8("{161A8D2D-3DDE-4744-BA38-08F900D10D6D}"));
queryInterface(id,(void **)&iface);
if (iface)
{
qDebug() << "Interface is loaded";
}
}
...
};



int GetFingerImage(VARIANT* AFingerImage)
{
//BOOL result;
//static BYTE parms[] = VTS_PVARIANT;
//InvokeHelper(0x2e, DISPATCH_METHOD, VT_BOOL, (void*)&result, parms, AFingerImage);
//return result;

int result = -1;
DISPPARAMS pDispParams;
VARIANTARG vArg[1];
pDispParams.cArgs = 1;
pDispParams.cNamedArgs = 0;
pDispParams.rgdispidNamedArgs = 0;
pDispParams.rgvarg = vArg;
VariantInit(&pDispParams.rgvarg[0]);
pDispParams.rgvarg[0].vt = VT_VARIANT | VT_BYREF;
pDispParams.rgvarg[0].pvarVal = AFingerImage;

VARIANT pVarResult;
EXCEPINFO pExcepInfo;
UINT puArgErr = (UINT)-1;
qDebug() << iface->Invoke(0x2e, IID_NULL, 0, DISPATCH_METHOD, &pDispParams, &pVarResult, &pExcepInfo, &puArgErr);
qDebug() << pExcepInfo.bstrDescription
<< pExcepInfo.bstrHelpFile
<< pExcepInfo.bstrSource
<< pExcepInfo.dwHelpContext
<< pExcepInfo.pfnDeferredFillIn
<< pExcepInfo.scode
<< pExcepInfo.wCode;

result = pVarResult.boolVal;

return result;
}


void CDemoDlg::test()
{
VARIANT *image = new VARIANT;
qDebug() << zkfpEng.GetFingerImage(image);
}

at console:

Interface is loaded
-2147352567 //HRESULT of Invoke
0xb44c0c 0x0 0xb37774 0 false -2147418113 0 //exception info
-1 //result

defines at winerror.h:
-2147352567 ~ DISP_E_EXCEPTION
-2147418113 ~ E_UNEXPECTED

Where is mistake? Thanks. :(

magrif
22nd November 2015, 16:25
int result;
DISPPARAMS pDispParams;
VARIANTARG vArg[1];
pDispParams.cArgs = 1;
pDispParams.cNamedArgs = 0;
pDispParams.rgdispidNamedArgs = 0;
pDispParams.rgvarg = vArg;
VariantInit(&pDispParams.rgvarg[0]);
pDispParams.rgvarg[0].vt = VT_VARIANT | VT_BYREF;
pDispParams.rgvarg[0].pvarVal = AFingerImage;

VARIANT pVarResult;
EXCEPINFO pExcepInfo;
UINT puArgErr = (UINT)0;

HRESULT hr = iface->Invoke(0x2e, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &pDispParams, &pVarResult, &pExcepInfo, &puArgErr);

seems to work
hr = S_OK

variant vartype:

qDebug() << AFingerImage.vt; //8209 ~ VT_ARRAY|VT_UI1

How get BMP format image from AFingerImage?

magrif
23rd November 2015, 10:36
I think I keep a blog:)
It's work now:


long iLBound, iUBound;
char * data;
SafeArrayGetLBound(AFingerImage.parray, AFingerImage.parray->cDims, &iLBound);
SafeArrayGetUBound(AFingerImage.parray, AFingerImage.parray->cDims, &iUBound);
SafeArrayAccessData(AFingerImage.parray,(void**)(&data));
long sz = iUBound - iLBound + 1;
QByteArray ba = QByteArray::fromRawData(data,sz);
FPImage.loadFromData(ba);
SafeArrayUnaccessData(AFingerImage.parray);

d_stranz
23rd November 2015, 18:01
Isn't COM fun? :D

magrif
24th November 2015, 06:37
that's horrible :crying:

Miss_Mc
20th December 2018, 14:54
hey, i know this is old but what Guuid did you use to get the iface here and where did you get it?




iface = 0;
QUuid id(QString::fromUtf8("{161A8D2D-3DDE-4744-BA38-08F900D10D6D}"));
queryInterface(id,(void **)&iface);
if (iface)

d_stranz
20th December 2018, 16:22
what Guuid did you use to get the iface here and where did you get it?

It's probably the IDispatch GUID for the fingerprint reader COM control, and as he said earlier in the thread, he got it by examining the COM DLL using the OLEVIEW utility from Microsoft. As I said also, you can retrieve the same information by using #import in your code to import the COM DLL and create .tli and .tlh header files that define the interfaces that are available in the DLL. Either way, you will have to find a COM interface that implements IDispatch.

The GUID used in the setControl() call in the first post is the GUID of the COM CoClass exposed by the DLL, and that loads the DLL and creates an instance of the COM class. The queryInterface() call later uses that COM class to get a pointer to the IDispatch interface. Both of these methods are members of the QAxObject base class.

Miss_Mc
10th January 2019, 12:21
Got a Qt to call methods over COM in a running Borland Builder app with this thread. Important to note that when invoking a method with multiple VARIANT* parameters the order of the parameters in the VARIANTARG array is applied in reverse order.




void IGDAS::ReturnVideoFile(VARIANT& VideoFile, VARIANT& Angles, VARIANT& log)
{
DISPPARAMS pDispParams;
VARIANTARG vArg[3];
pDispParams.cArgs = 3;
pDispParams.cNamedArgs = 0;
pDispParams.rgdispidNamedArgs = nullptr;
pDispParams.rgvarg = vArg;

VariantInit(&pDispParams.rgvarg[2]);
pDispParams.rgvarg[2].vt = VT_VARIANT | VT_BYREF;
pDispParams.rgvarg[2].pvarVal = &VideoFile;

VariantInit(&pDispParams.rgvarg[1]);
pDispParams.rgvarg[1].vt = VT_VARIANT | VT_BYREF;
pDispParams.rgvarg[1].pvarVal = &Angles;

VariantInit(&pDispParams.rgvarg[0]);
pDispParams.rgvarg[0].vt = VT_VARIANT | VT_BYREF;
pDispParams.rgvarg[0].pvarVal = &log;

VARIANT pVarResult;
EXCEPINFO pExcepInfo;
UINT puArgErr = (UINT)0;
iface->Invoke(0x0E, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD,
&pDispParams, &pVarResult, &pExcepInfo, &puArgErr);
}



It would now be nice to know how to use invoke over COM on a method with not only an array of VARIANT* arguments but an additional argument of type int.

Any help on this?