File indexing completed on 2024-04-28 04:32:19
0001 /* 0002 * SPDX-FileCopyrightText: 2002-2003 Stephan Stapel <stephan dot stapel at web dot de> 0003 * SPDX-FileCopyrightText: 2008-2009 Gilles Caulier <caulier dot gilles at gmail dot com> 0004 * SPDX-FileCopyrightText: 2009, 2017 Kare Sars <kare dot sars at iki dot fi> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0007 */ 0008 0009 // I renamed the class to KSaneWidgetPrivate to remove 0010 // the need for a wrapper class with this name. (Kare) 0011 0012 #include "twainiface.h" 0013 0014 #include "ksanewidget.h" 0015 0016 #define TWCPP_ANYCOUNT (-1) 0017 #define TWCPP_CANCELTHIS (1) 0018 #define TWCPP_CANCELALL (2) 0019 #define TWCPP_DOTRANSFER (0) 0020 0021 #include <QString> 0022 #include <QImage> 0023 #include <QDebug> 0024 0025 #include <cstring> 0026 0027 #define saneDebug() if (0) qDebug() 0028 0029 namespace KSaneIface 0030 { 0031 0032 KSaneWidgetPrivate::KSaneWidgetPrivate(): QWidget(0) 0033 { 0034 // This is a dummy widget not visible. We use Qwidget to dispatch Windows event to 0035 // Twain interface. This is not possible to do it using QObject as well. 0036 0037 m_hMessageWnd = 0; 0038 m_hTwainDLL = NULL; 0039 m_pDSMProc = NULL; 0040 m_bDSOpen = false; 0041 m_bDSMOpen = false; 0042 m_bSourceEnabled = false; 0043 m_bModalUI = true; 0044 m_nImageCount = TWCPP_ANYCOUNT; 0045 0046 InitTwain(); 0047 } 0048 0049 KSaneWidgetPrivate::~KSaneWidgetPrivate() 0050 { 0051 ReleaseTwain(); 0052 } 0053 0054 bool KSaneWidgetPrivate::nativeEvent(const QByteArray &eventType, void *message, long *result) 0055 { 0056 if (eventType == "windows_generic_MSG") { 0057 return ProcessMessage(*(MSG*)message); 0058 } 0059 return true; 0060 } 0061 0062 0063 /** Initializes TWAIN interface . Is already called from the constructor. 0064 It should be called again if ReleaseTwain is called. 0065 hWnd is the window which has to subclassed in order to receive 0066 Twain messaged. Normally - this would be your main application window. 0067 */ 0068 bool KSaneWidgetPrivate::InitTwain() 0069 { 0070 char libName[512]; 0071 0072 if ((m_hTwainDLL && m_pDSMProc)) { 0073 return true; 0074 } 0075 0076 memset(&m_AppId, 0, sizeof(m_AppId)); 0077 0078 if (!IsWindow((HWND)this->winId())) { 0079 return false; 0080 } 0081 0082 m_hMessageWnd = (HWND)this->winId(); 0083 strcpy(libName, "TWAIN_32.DLL"); 0084 0085 m_hTwainDLL = LoadLibraryA(libName); 0086 if (m_hTwainDLL != NULL) { 0087 if (!(m_pDSMProc = (DSMENTRYPROC)GetProcAddress(m_hTwainDLL, (LPCSTR)MAKEINTRESOURCE(1)))) { 0088 FreeLibrary(m_hTwainDLL); 0089 m_hTwainDLL = NULL; 0090 } 0091 } 0092 0093 if ((m_hTwainDLL && m_pDSMProc)) { 0094 // Expects all the fields in m_AppId to be set except for the id field. 0095 m_AppId.Id = 0; // Initialize to 0 (Source Manager will assign real value) 0096 m_AppId.Version.MajorNum = 0; // Your app's version number 0097 m_AppId.Version.MinorNum = 2; 0098 m_AppId.Version.Language = TWLG_USA; 0099 m_AppId.Version.Country = TWCY_USA; 0100 strcpy(m_AppId.Version.Info, "libksane"); 0101 0102 m_AppId.ProtocolMajor = TWON_PROTOCOLMAJOR; 0103 m_AppId.ProtocolMinor = TWON_PROTOCOLMINOR; 0104 m_AppId.SupportedGroups = DG_IMAGE | DG_CONTROL; 0105 strcpy(m_AppId.Manufacturer, "KDE"); 0106 strcpy(m_AppId.ProductFamily, "Generic"); 0107 strcpy(m_AppId.ProductName, "libksane"); 0108 0109 m_bDSMOpen = CallTwainProc(&m_AppId, NULL, DG_CONTROL, 0110 DAT_PARENT, MSG_OPENDSM, (TW_MEMREF)&m_hMessageWnd); 0111 return true; 0112 } else { 0113 return false; 0114 } 0115 } 0116 0117 /** Releases the twain interface . Need not be called unless you 0118 want to specifically shut it down. 0119 */ 0120 void KSaneWidgetPrivate::ReleaseTwain() 0121 { 0122 if ((m_hTwainDLL && m_pDSMProc)) { 0123 CloseDSM(); 0124 FreeLibrary(m_hTwainDLL); 0125 m_hTwainDLL = NULL; 0126 m_pDSMProc = NULL; 0127 } 0128 } 0129 0130 /** Entry point into Twain. For a complete description of this 0131 routine please refer to the Twain specification 1.8 0132 */ 0133 bool KSaneWidgetPrivate::CallTwainProc(pTW_IDENTITY pOrigin, pTW_IDENTITY pDest, 0134 TW_UINT32 DG, TW_UINT16 DAT, TW_UINT16 MSG, TW_MEMREF pData) 0135 { 0136 saneDebug() << "CallTwainProc" << pOrigin << pDest << DG << DAT << MSG << pData; 0137 if (!(m_hTwainDLL && m_pDSMProc)) { 0138 m_returnCode = TWRC_FAILURE; 0139 return false; 0140 } 0141 0142 m_returnCode = (*m_pDSMProc)(pOrigin, pDest, DG, DAT, MSG, pData); 0143 0144 if (m_returnCode == TWRC_FAILURE) { 0145 saneDebug() << "CallTwainProc m_returnCode == TWRC_FAILURE"; 0146 (*m_pDSMProc)(pOrigin, pDest, DG_CONTROL, DAT_STATUS, MSG_GET, &m_Status); 0147 } 0148 return (m_returnCode == TWRC_SUCCESS); 0149 } 0150 0151 /** Called to display a dialog box to select the Twain source to use. 0152 This can be overridden if a list of all sources is available 0153 to the application. These sources can be enumerated by Twain. 0154 it is not yet supported by KSaneWidgetPrivate. 0155 */ 0156 QString KSaneWidgetPrivate::SelectSource() 0157 { 0158 TW_IDENTITY src; 0159 memset(&src, 0, sizeof(src)); 0160 0161 // debug printouts 0162 //bool ret_ok = CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETFIRST, &src); 0163 //while (ret_ok) { 0164 // saneDebug() << QLatin1String(src.ProductName); 0165 // ret_ok = CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETNEXT, &src); 0166 //} 0167 0168 // set the default entry selected 0169 CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT, &src); 0170 0171 // now open the selection dialog 0172 if (!CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT, &src)) { 0173 return QString(); 0174 } 0175 0176 QString source; 0177 source += QLatin1String(src.ProductName); 0178 source += QLatin1Char(';'); 0179 source += QLatin1String(src.ProductFamily); 0180 source += QLatin1Char(';'); 0181 source += QLatin1String(src.Manufacturer); 0182 saneDebug()<< source; 0183 return source; 0184 } 0185 0186 /** Closes the Data Source 0187 */ 0188 void KSaneWidgetPrivate::CloseDS() 0189 { 0190 saneDebug() << "CloseDS"; 0191 if (DSIsOpen()) { 0192 if (m_bSourceEnabled) { 0193 TW_USERINTERFACE twUI; 0194 if (CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, 0195 DAT_USERINTERFACE, MSG_DISABLEDS, &twUI)) { 0196 m_bSourceEnabled = false; 0197 } 0198 } 0199 CallTwainProc(&m_AppId, NULL, DG_CONTROL, 0200 DAT_IDENTITY, MSG_CLOSEDS, (TW_MEMREF)&m_Source); 0201 m_bDSOpen = false; 0202 } 0203 } 0204 0205 /** Closes the Data Source Manager */ 0206 void KSaneWidgetPrivate::CloseDSM() 0207 { 0208 saneDebug() << "CloseDSM"; 0209 if (m_hTwainDLL && m_pDSMProc && m_bDSMOpen) { 0210 CloseDS(); 0211 CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM, (TW_MEMREF)&m_hMessageWnd); 0212 m_bDSMOpen = false; 0213 } 0214 } 0215 0216 /** Returns true if the Data Source is Open */ 0217 bool KSaneWidgetPrivate::DSIsOpen() const 0218 { 0219 saneDebug() << "DSOpen:" << m_hTwainDLL << m_pDSMProc << m_bDSMOpen << m_bDSOpen; 0220 return (m_hTwainDLL && m_pDSMProc) && m_bDSMOpen && m_bDSOpen; 0221 } 0222 0223 /** Opens a Data Source */ 0224 bool KSaneWidgetPrivate::OpenSource(const QString &source) 0225 { 0226 saneDebug() << "OpenSource:" << source; 0227 if (source.isEmpty()) { 0228 return false; 0229 } 0230 0231 QStringList splited = source.split(QLatin1Char(';')); 0232 if (splited.size() != 3) { 0233 return false; 0234 } 0235 0236 // go thorough the list and check if the source is available 0237 bool ret_ok = CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETFIRST, &m_Source); 0238 while (ret_ok) { 0239 saneDebug() << m_Source.Id << m_Source.Version.MajorNum << m_Source.Version.MinorNum; 0240 saneDebug() << m_Source.Manufacturer << m_Source.ProductFamily << m_Source.ProductName; 0241 if (QLatin1String(m_Source.ProductName) == splited[0]) { 0242 break; 0243 } 0244 ret_ok = CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETNEXT, &m_Source); 0245 } 0246 0247 if (!ret_ok) { 0248 saneDebug() << "CallTwainProc failed when reading beyond the last source"; 0249 // CallTwainProc failed when reading beyond the last source 0250 return false; 0251 } 0252 0253 // open the source 0254 if (m_hTwainDLL && m_pDSMProc && m_bDSMOpen) { 0255 m_bDSOpen = CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, (TW_MEMREF)&m_Source); 0256 saneDebug() << "OpenSource(qst..) m_bDSOpen" << m_bDSOpen; 0257 } 0258 0259 SetImageCount(TWCPP_ANYCOUNT); 0260 0261 return DSIsOpen(); 0262 } 0263 0264 /** Re-Opens a Data Source */ 0265 bool KSaneWidgetPrivate::ReOpenDialog() 0266 { 0267 saneDebug() << "ReOpenSource:" << m_hTwainDLL << m_pDSMProc << m_bDSMOpen << m_bDSOpen; 0268 0269 if (DSIsOpen()) { 0270 // already open 0271 return true; 0272 } 0273 // open the source 0274 if (m_hTwainDLL && m_pDSMProc && m_bDSMOpen) { 0275 m_bDSOpen = CallTwainProc(&m_AppId, NULL, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, (TW_MEMREF)&m_Source); 0276 //saneDebug() << "ReOpenSource() m_bDSOpen" << m_bDSOpen; 0277 } 0278 0279 SetImageCount(TWCPP_ANYCOUNT); 0280 0281 if (DSIsOpen()) { 0282 return EnableSource(true); 0283 } 0284 //else 0285 return false; 0286 } 0287 0288 /** Should be called from the main message loop of the application. Can always be called, 0289 it will not process the message unless a scan is in progress. */ 0290 bool KSaneWidgetPrivate::ProcessMessage(MSG msg) 0291 { 0292 // TODO: don't really know why... 0293 if (msg.message == 528) { 0294 return false; 0295 } 0296 if (msg.message == 289) { 0297 return false; 0298 } 0299 0300 if (m_hMessageWnd == 0) { 0301 return false; 0302 } 0303 0304 saneDebug() << "ProcessMessage:" << msg.message << m_bSourceEnabled; 0305 0306 if (m_bSourceEnabled) { 0307 TW_UINT16 twRC = TWRC_NOTDSEVENT; 0308 0309 TW_EVENT twEvent; 0310 twEvent.pEvent = (TW_MEMREF)&msg; 0311 //memset(&twEvent, 0, sizeof(TW_EVENT)); 0312 twEvent.TWMessage = MSG_NULL; 0313 0314 CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, 0315 DAT_EVENT, MSG_PROCESSEVENT, (TW_MEMREF)&twEvent); 0316 0317 saneDebug() << "ProcessMessage: retcode= " << m_returnCode; 0318 if (m_returnCode != TWRC_NOTDSEVENT) { 0319 TranslateMessage(twEvent); 0320 } 0321 return (twRC == TWRC_DSEVENT); 0322 } 0323 return false; 0324 } 0325 0326 /** Queries the capability of the Twain Data Source */ 0327 bool KSaneWidgetPrivate::GetCapability(TW_CAPABILITY &twCap, TW_UINT16 cap, TW_UINT16 conType) 0328 { 0329 saneDebug() << "GetCapability"; 0330 if (DSIsOpen()) { 0331 twCap.Cap = cap; 0332 twCap.ConType = conType; 0333 twCap.hContainer = NULL; 0334 0335 if (CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, 0336 DAT_CAPABILITY, MSG_GET, (TW_MEMREF)&twCap)) { 0337 return true; 0338 } 0339 } 0340 return false; 0341 } 0342 0343 /** Queries the capability of the Twain Data Source */ 0344 bool KSaneWidgetPrivate::GetCapability(TW_UINT16 cap, TW_UINT32 &value) 0345 { 0346 saneDebug() << "GetCapability2"; 0347 TW_CAPABILITY twCap; 0348 if (GetCapability(twCap, cap)) { 0349 pTW_ONEVALUE pVal; 0350 pVal = (pTW_ONEVALUE)GlobalLock(twCap.hContainer); 0351 0352 if (pVal) { 0353 value = pVal->Item; 0354 GlobalUnlock(pVal); 0355 GlobalFree(twCap.hContainer); 0356 return true; 0357 } 0358 } 0359 return false; 0360 } 0361 0362 /** Sets the capability of the Twain Data Source */ 0363 bool KSaneWidgetPrivate::SetCapability(TW_UINT16 cap, TW_UINT16 value, bool sign) 0364 { 0365 saneDebug() << "SetCapability"; 0366 if (DSIsOpen()) { 0367 TW_CAPABILITY twCap; 0368 pTW_ONEVALUE pVal; 0369 bool ret_value = false; 0370 twCap.Cap = cap; 0371 twCap.ConType = TWON_ONEVALUE; 0372 twCap.hContainer = GlobalAlloc(GHND, sizeof(TW_ONEVALUE)); 0373 0374 if (twCap.hContainer) { 0375 pVal = (pTW_ONEVALUE)GlobalLock(twCap.hContainer); 0376 pVal->ItemType = sign ? TWTY_INT16 : TWTY_UINT16; 0377 pVal->Item = (TW_UINT32)value; 0378 GlobalUnlock(twCap.hContainer); 0379 ret_value = SetCapability(twCap); 0380 GlobalFree(twCap.hContainer); 0381 } 0382 return ret_value; 0383 } 0384 return false; 0385 } 0386 0387 /** Sets the capability of the Twain Data Source */ 0388 bool KSaneWidgetPrivate::SetCapability(TW_CAPABILITY &cap) 0389 { 0390 saneDebug() << "SetCapability2"; 0391 if (DSIsOpen()) { 0392 return CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, 0393 DAT_CAPABILITY, MSG_SET, (TW_MEMREF)&cap); 0394 } 0395 return false; 0396 } 0397 0398 /** Sets the number of images which can be accepted by the application at one time */ 0399 bool KSaneWidgetPrivate::SetImageCount(TW_INT16 nCount) 0400 { 0401 saneDebug() << "SetImageCount" << nCount << (TW_UINT16)nCount; 0402 if (SetCapability(CAP_XFERCOUNT, (TW_UINT16)nCount, true)) { 0403 saneDebug() << "SetImageCount: nCount" << nCount; 0404 m_nImageCount = nCount; 0405 return true; 0406 } else { 0407 saneDebug() << "SetImageCount: failed:" << m_returnCode; 0408 if (m_returnCode == TWRC_CHECKSTATUS) { 0409 TW_UINT32 count; 0410 0411 if (GetCapability(CAP_XFERCOUNT, count)) { 0412 nCount = (TW_INT16)count; 0413 0414 if (SetCapability(CAP_XFERCOUNT, nCount)) { 0415 m_nImageCount = nCount; 0416 return true; 0417 } 0418 } 0419 } 0420 } 0421 return false; 0422 } 0423 0424 /** Called to enable the Twain Acquire Dialog. This too can be 0425 * overridden but is a helluva job. */ 0426 bool KSaneWidgetPrivate::OpenDialog() 0427 { 0428 saneDebug() << "OpenDialog"; 0429 EnableSource(true); 0430 return true; 0431 } 0432 0433 /** Called to enable the Twain Acquire Dialog. This too can be 0434 * overridden but is a helluva job. */ 0435 bool KSaneWidgetPrivate::EnableSource(bool showUI) 0436 { 0437 saneDebug() << "EnableSource: DSIsOpen() =" << DSIsOpen(); 0438 if (DSIsOpen() && !m_bSourceEnabled) { 0439 TW_USERINTERFACE twUI; 0440 twUI.ShowUI = showUI; 0441 twUI.hParent = (TW_HANDLE)m_hMessageWnd; 0442 0443 if (CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, 0444 DAT_USERINTERFACE, MSG_ENABLEDS, (TW_MEMREF)&twUI)) { 0445 m_bSourceEnabled = true; 0446 m_bModalUI = twUI.ModalUI; 0447 } else { 0448 m_bSourceEnabled = false; 0449 m_bModalUI = true; 0450 } 0451 saneDebug() << "EnableSource: ModalUI=" << twUI.ModalUI << m_returnCode; 0452 return m_bSourceEnabled; 0453 } 0454 return false; 0455 } 0456 0457 /** Called by ProcessMessage to Translate a TWAIN message */ 0458 void KSaneWidgetPrivate::TranslateMessage(TW_EVENT &twEvent) 0459 { 0460 saneDebug() << "TranslateMessage: twEvent.TWMessage =" << twEvent.TWMessage; 0461 switch (twEvent.TWMessage) { 0462 case MSG_XFERREADY: 0463 saneDebug() << "MSG_XFERREADY"; 0464 TransferImage(); 0465 break; 0466 0467 case MSG_CLOSEDSREQ: 0468 saneDebug() << "MSG_CLOSEDSREQ"; 0469 CloseDS(); 0470 break; 0471 } 0472 } 0473 0474 /** Gets Imageinfo for an image which is about to be transferred. */ 0475 bool KSaneWidgetPrivate::GetImageInfo(TW_IMAGEINFO &info) 0476 { 0477 saneDebug() << "GetImageInfo"; 0478 if (m_bSourceEnabled) { 0479 return CallTwainProc(&m_AppId, &m_Source, DG_IMAGE, 0480 DAT_IMAGEINFO, MSG_GET, (TW_MEMREF)&info); 0481 } 0482 return false; 0483 } 0484 0485 /** Transfers the image or cancels the transfer depending on the state of the TWAIN system */ 0486 void KSaneWidgetPrivate::TransferImage() 0487 { 0488 saneDebug() << "TransferImage()"; 0489 TW_IMAGEINFO info; 0490 bool bContinue = true; 0491 0492 while (bContinue) { 0493 if (GetImageInfo(info)) { 0494 int permission = TWCPP_DOTRANSFER; 0495 0496 switch (permission) { 0497 case TWCPP_CANCELTHIS: 0498 bContinue = EndTransfer(); 0499 break; 0500 0501 case TWCPP_CANCELALL: 0502 CancelTransfer(); 0503 bContinue = false; 0504 break; 0505 0506 case TWCPP_DOTRANSFER: 0507 bContinue = GetImage(info); 0508 break; 0509 } 0510 } 0511 } 0512 } 0513 0514 /** Ends the current transfer. 0515 Returns true if the more images are pending */ 0516 bool KSaneWidgetPrivate::EndTransfer() 0517 { 0518 saneDebug() << "EndTransfer"; 0519 TW_PENDINGXFERS twPend; 0520 if (CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, 0521 DAT_PENDINGXFERS, MSG_ENDXFER, (TW_MEMREF)&twPend)) { 0522 return twPend.Count != 0; 0523 } 0524 return false; 0525 } 0526 0527 /** Aborts all transfers */ 0528 void KSaneWidgetPrivate::CancelTransfer() 0529 { 0530 saneDebug() << "CancelTransfer"; 0531 TW_PENDINGXFERS twPend; 0532 CallTwainProc(&m_AppId, &m_Source, DG_CONTROL, DAT_PENDINGXFERS, MSG_RESET, (TW_MEMREF)&twPend); 0533 } 0534 0535 /** Calls TWAIN to actually get the image */ 0536 bool KSaneWidgetPrivate::GetImage(TW_IMAGEINFO &info) 0537 { 0538 saneDebug() << "GetImage"; 0539 TW_MEMREF pdata; 0540 CallTwainProc(&m_AppId, &m_Source, DG_IMAGE, DAT_IMAGENATIVEXFER, MSG_GET, &pdata); 0541 0542 switch (m_returnCode) { 0543 case TWRC_XFERDONE: 0544 saneDebug()<< "GetImage:TWRC_XFERDONE"; 0545 ImageData(pdata, info); 0546 break; 0547 0548 case TWRC_CANCEL: 0549 saneDebug()<< "GetImage:TWRC_CANCEL"; 0550 break; 0551 0552 case TWRC_FAILURE: 0553 saneDebug()<< "GetImage:TWRC_FAILURE"; 0554 CancelTransfer(); 0555 return false; 0556 break; 0557 0558 default: 0559 saneDebug()<< "GetImage:" << m_returnCode; 0560 } 0561 0562 GlobalFree(pdata); 0563 return EndTransfer(); 0564 } 0565 0566 void KSaneWidgetPrivate::ImageData(TW_MEMREF pdata, TW_IMAGEINFO &info) 0567 { 0568 saneDebug() << "ImageData"; 0569 if (pdata && (info.ImageWidth != -1) && (info.ImageLength != - 1)) { 0570 // Under Windows, Twain interface return a DIB data structure. 0571 // See http://en.wikipedia.org/wiki/Device-independent_bitmap#DIBs_in_memory for details. 0572 HGLOBAL hDIB = (HGLOBAL)(qptrdiff)pdata; 0573 int size = (int)GlobalSize(hDIB); 0574 const char *bits = (const char *)GlobalLock(hDIB); 0575 0576 // DIB is BMP without header. we will add it to load data in QImage using std loader from Qt. 0577 QByteArray baBmp; 0578 QDataStream ds(&baBmp, QIODevice::WriteOnly); 0579 0580 ds.writeRawData("BM", 2); 0581 0582 qint32 filesize = size + 14; 0583 ds << filesize; 0584 0585 qint16 reserved = 0; 0586 ds << reserved; 0587 ds << reserved; 0588 0589 qint32 pixOffset = 14 + 40 + 0; 0590 ds << pixOffset; 0591 0592 ds.writeRawData(bits, size); 0593 0594 Q_EMIT ImageReady(baBmp, 0, 0, 0, (int)KSaneWidget::FormatBMP); 0595 Q_EMIT qImageReady(QImage::fromData(baBmp, "BMP")); 0596 0597 GlobalUnlock(hDIB); 0598 0599 } 0600 } 0601 0602 } 0603 0604 #include "moc_twainiface.cpp"