ecere/gui/Window: Prevent uninitialized values if base Window methods not overridden...
[sdk] / extras / wia.ec
1 #define COBJMACROS
2 #define WIN32_LEAN_AND_MEAN
3 #define UNICODE
4 #define uint _uint
5 #define Method _Method
6 #define Array _Array
7 #define byte _byte
8 #define bool _bool
9 #define int64 _int64
10
11 #include <objbase.h>
12
13 #define __RPC_H__
14 #define _OLE2_H_
15 #define _BASETSD_H_
16 #define GUID_DEFINED
17 #define __LPGUID_DEFINED__
18 #define __wtypes_h__
19 #define __objidl_h__
20 #define __unknwn_h__
21 #define __propidl_h__
22
23 #include <wia.h>
24 #include <sti.h>
25
26 WINOLEAPI PropVariantClear ( PROPVARIANT * pvar );
27
28 #undef bool
29 #undef uint
30 #undef Method
31 #undef Array
32 #undef byte
33 #undef int64
34
35 #if defined(ECERE_STATIC)
36 public import static "ecere"
37 #else
38 public import "ecere"
39 #endif
40
41 class MSCOM_Base { }
42
43 __on_register_module()
44 {
45    class(MSCOM_Base).vTblSize = 0;
46    delete class(MSCOM_Base)._vTbl;
47 }
48
49 class MSCOM_IUnknown : MSCOM_Base
50 {
51 public:
52    virtual uint stdcall QueryInterface(REFIID iid, void ** ppvObj)
53    {
54       if(!ppvObj)
55          return E_POINTER;
56       if(IsEqualGUID(iid, &IID_IUnknown))
57       {
58          if(!_refCount) _refCount = 1;
59          *ppvObj = this;
60       }
61       else
62       {
63          *ppvObj = null;
64          return E_NOINTERFACE;
65       }
66       AddRef();
67       return S_OK;
68    }
69
70    virtual uint stdcall AddRef()
71    {
72       return (uint)InterlockedIncrement((void *)&_refCount);
73    }
74
75    virtual uint stdcall Release()
76    {
77       uint cRef = InterlockedDecrement((void *)&_refCount);
78       if(cRef == 0)
79          delete this;
80       return cRef;
81    }
82 }
83
84 class WiaDataCallback : MSCOM_IUnknown
85 {
86    uint stdcall QueryInterface(REFIID iid, void ** ppvObj)
87    {
88       if(!ppvObj)
89          return E_POINTER;
90       if(IsEqualGUID(iid, &IID_IWiaDataCallback))
91       {
92          *ppvObj = this;
93          if(!_refCount) _refCount = 1;
94          AddRef();
95          return S_OK;
96       }
97       return MSCOM_IUnknown::QueryInterface(iid, ppvObj);
98    }
99
100    virtual uint stdcall BandedDataCallback(uint lReason, uint lStatus, uint lPercentComplete, uint lOffset, uint lLength,
101       uint lReserved, uint lResLength, byte * pbBuffer);
102 };
103
104 class WiaDataTransfer
105 {
106    IWiaDataTransfer * pWiaDataTransfer;
107
108    HRESULT idtGetBandedData(PWIA_DATA_TRANSFER_INFO pWiaDataTransInfo, WiaDataCallback wiaDataCallback)
109    {
110       return (HRESULT)IWiaDataTransfer_idtGetBandedData(pWiaDataTransfer, pWiaDataTransInfo, (IWiaDataCallback *)wiaDataCallback);
111    }
112 }
113
114 class WiaPropertyStorage
115 {
116    IWiaPropertyStorage *pWiaPropertyStorage;
117
118    bool ReadLong(const PROPSPEC *pPropSpec, uint *plResult)
119    {
120       bool result = true;
121       PROPVARIANT propVariant;
122       HRESULT hr = ReadMultiple(1, pPropSpec, &propVariant);
123       if(SUCCEEDED(hr))
124       {
125          switch(propVariant.vt)
126          {
127             case VT_I1: *plResult = (uint)propVariant.cVal; break;
128             case VT_UI1: *plResult = (uint) propVariant.bVal; break;
129             case VT_I2: *plResult = (uint) propVariant.iVal; break;
130             case VT_UI2: *plResult = (uint) propVariant.uiVal; break;
131             case VT_I4: *plResult = (uint) propVariant.lVal; break;
132             case VT_UI4: *plResult = (uint) propVariant.ulVal; break;
133             /*
134             case VT_INT: *plResult = (uint) propVariant.intVal; break;
135             case VT_UINT: *plResult = (uint) propVariant.uintVal; break;
136             */
137             case VT_R4: *plResult = (uint) (propVariant.fltVal + 0.5); break;
138             case VT_R8: *plResult = (uint) (propVariant.dblVal + 0.5); break;
139             default: hr = E_FAIL; result = false; break;
140          }
141       }
142       else
143          result = false;
144       PropVariantClear(&propVariant);
145       return result;
146    }
147
148    HRESULT WriteMultiple(ULONG cpspec, const PROPSPEC *rgpspec, const PROPVARIANT * rgpropvar,PROPID propidNameFirst)
149    {
150       return (HRESULT)IWiaPropertyStorage_WriteMultiple(pWiaPropertyStorage, cpspec, rgpspec, rgpropvar, propidNameFirst);
151    }
152
153    HRESULT ReadMultiple(ULONG cpspec, const PROPSPEC * rgpspec, PROPVARIANT * rgpropvar)
154    {
155       return (HRESULT)IWiaPropertyStorage_ReadMultiple(pWiaPropertyStorage, cpspec, rgpspec, rgpropvar);
156    }
157
158    ~WiaPropertyStorage()
159    {
160       if(pWiaPropertyStorage)
161       {
162          IWiaPropertyStorage_Release(pWiaPropertyStorage);
163          pWiaPropertyStorage = null;
164       }
165    }
166 }
167
168 class ScanningProgress : Window
169 {
170    autoCreate = false;
171    text = "Scanning in progress. Please wait.";
172    borderStyle = fixed;
173    size = { 300, 60 };
174    ProgressBar progressBar { this, anchor = { 4, 4, 4, 4 }, range = 100 };
175 }
176
177 ScanningProgress scanProgress { };
178
179 bool nextPage = false;
180
181 class MyWiaDataCallback : WiaDataCallback
182 {
183    TempFile f { };
184
185    uint stdcall BandedDataCallback(uint lReason, uint lStatus, uint lPercentComplete, uint lOffset, uint lLength,
186       uint lReserved, uint lResLength, byte * pbBuffer)
187    {
188       switch(lReason)
189       {
190          case IT_MSG_DATA_HEADER:
191          case IT_MSG_DATA:
192             ((GuiApplication)__thisModule.application).Lock();
193             scanProgress.progressBar.progress = lPercentComplete;
194             ((GuiApplication)__thisModule.application).Unlock();
195             f.Seek(lOffset, start);
196             f.WriteData(pbBuffer, lLength);
197             break;
198          case IT_MSG_STATUS:
199             ((GuiApplication)__thisModule.application).Lock();
200             scanProgress.progressBar.progress = lPercentComplete;
201             ((GuiApplication)__thisModule.application).Unlock();
202             break;
203          case IT_MSG_TERMINATION:
204             break;
205          case IT_MSG_NEW_PAGE:
206             nextPage = true;
207             break;
208       }
209       ((GuiApplication)__thisModule.application).Lock();
210       ((GuiApplication)__thisModule.application).ProcessInput(true);
211       ((GuiApplication)__thisModule.application).UpdateDisplay();
212       ((GuiApplication)__thisModule.application).Unlock();
213
214       return S_OK;
215    }
216 }
217
218 class WiaItem
219 {
220    IWiaItem *pWiaItem;
221
222    HRESULT DeviceDlg(HWND hWndParent, uint lFlags, uint lIntent, LONG * count, void * images)
223    {
224       return (HRESULT)IWiaItem_DeviceDlg(pWiaItem, hWndParent, lFlags, lIntent, count, images);
225    }
226
227    property WiaPropertyStorage propertyStorage
228    {
229       get
230       {
231          WiaPropertyStorage ps { };
232          HRESULT hr = IWiaItem_QueryInterface(pWiaItem, &IID_IWiaPropertyStorage, &ps.pWiaPropertyStorage);
233          if(FAILED(hr))
234          {
235             delete ps;
236             printf("Error for WiaItem::propertyStorage (IWiaItem_QueryInterface)\n");
237          }
238          return ps;
239       }
240    }
241
242    property WiaDataTransfer dataTransfer
243    {
244       get
245       {
246          WiaDataTransfer dt { };
247          HRESULT hr = IWiaItem_QueryInterface(pWiaItem, &IID_IWiaDataTransfer, &dt.pWiaDataTransfer);
248          if(FAILED(hr))
249          {
250             delete dt;
251             printf("Error for WiaItem::dataTransfer (IWiaItem_QueryInterface)\n");
252          }
253          return dt;
254       }
255    }
256
257    ~WiaItem()
258    {
259       if(pWiaItem)
260       {
261          IWiaItem_Release(pWiaItem);
262          pWiaItem = null;
263       }
264    }
265
266 public:
267    List<Bitmap> GetBitmaps(/*File * file*/)
268    {
269       List<Bitmap> result = null;
270       IWiaItem ** ppIWiaItem;
271       LONG count;
272       Window window = ((GuiApplication)__thisModule.application).desktop.activeChild;
273       if(window) window = window.rootWindow;
274
275       if(!DeviceDlg(window ? window.systemHandle : 0, 0, WIA_INTENT_NONE, &count, &ppIWiaItem))
276       {
277          // Feeder Code
278          // if (!(lFlags & WIA_DEVICE_DIALOG_SINGLE_IMAGE))
279          WiaPropertyStorage scannerProp = propertyStorage;
280          PROPSPEC specDevType;
281          uint nDevType = 0;
282          specDevType.ulKind = PRSPEC_PROPID;
283          specDevType.propid = WIA_DIP_DEV_TYPE;
284
285          if(scannerProp.ReadLong(&specDevType, &nDevType) &&
286             GET_STIDEVICE_TYPE(nDevType) == StiDeviceTypeScanner)
287          {
288             PROPSPEC specDocumentHandlingSelect;
289             uint nDocumentHandlingSelect;
290
291             specDocumentHandlingSelect.ulKind = PRSPEC_PROPID;
292             specDocumentHandlingSelect.propid = WIA_DPS_DOCUMENT_HANDLING_SELECT;
293
294             if(scannerProp.ReadLong(&specDocumentHandlingSelect, &nDocumentHandlingSelect) &&
295                (nDocumentHandlingSelect & FEEDER))
296             {
297                PROPSPEC specPages;
298                PROPVARIANT varPages;
299
300                specPages.ulKind = PRSPEC_PROPID;
301                specPages.propid = WIA_DPS_PAGES;
302
303                varPages.vt = VT_I4;
304                varPages.lVal = ALL_PAGES;
305
306                scannerProp.WriteMultiple(1, &specPages, &varPages, WIA_DPS_FIRST);
307
308                PropVariantClear(&varPages);
309             }
310          }
311          delete scannerProp;
312
313          nextPage = true;
314          while(nextPage)
315          {
316             int i = 0;
317             nextPage = false;
318             for(i = 0; i < count; i++)
319             {
320                WiaItem wiaItem { pWiaItem = ppIWiaItem[i] };
321                WiaPropertyStorage wiaPropertyStorage = wiaItem.propertyStorage;
322                WiaDataTransfer wiaDataTransfer = wiaItem.dataTransfer;
323
324                if(wiaPropertyStorage && wiaDataTransfer)
325                {
326                   PROPSPEC specTymed;
327                   PROPVARIANT varTymed;
328
329                   specTymed.ulKind = PRSPEC_PROPID;
330                   specTymed.propid = WIA_IPA_TYMED;
331
332                   varTymed.vt = VT_I4;
333                   varTymed.lVal = TYMED_CALLBACK;
334
335                   if(!wiaPropertyStorage.WriteMultiple(1, &specTymed, &varTymed, WIA_IPA_FIRST))
336                   {
337                      GUID guidFormat = WiaImgFmt_BMP;
338                      GUID preferredFormat;
339                      const String format = "bmp";
340                      PROPSPEC specPreferredFormat;
341
342                      specPreferredFormat.ulKind = PRSPEC_PROPID;
343                      specPreferredFormat.propid = WIA_IPA_PREFERRED_FORMAT;
344
345                      if(ReadPropertyGuid(wiaPropertyStorage, &specPreferredFormat, &preferredFormat))
346                      {
347                         if(!memcmp(&preferredFormat, &WiaImgFmt_MEMORYBMP, sizeof(preferredFormat)))
348                         {
349                            guidFormat = WiaImgFmt_MEMORYBMP;
350                            format = "memorybmp";
351                         }
352                      }
353
354                      {
355                         bool suitableFormat = false;
356                         PROPSPEC specFormat;
357                         PROPVARIANT varFormat;
358
359                         specFormat.ulKind = PRSPEC_PROPID;
360                         specFormat.propid = WIA_IPA_FORMAT;
361
362                         varFormat.vt = VT_CLSID;
363                         varFormat.puuid = &guidFormat;
364
365                         if(!wiaPropertyStorage.WriteMultiple(1, &specFormat, &varFormat, WIA_IPA_FIRST))
366                            suitableFormat = true;
367                         else
368                         {
369                            guidFormat = WiaImgFmt_MEMORYBMP;
370                            if(!wiaPropertyStorage.WriteMultiple(1, &specFormat, &varFormat, WIA_IPA_FIRST))
371                            {
372                               format = "memorybmp";
373                               suitableFormat = true;
374                            }
375                         }
376
377                         if(suitableFormat)
378                         {
379                            PROPSPEC specBufferSize;
380                            uint nBufferSize;
381                            WIA_DATA_TRANSFER_INFO WiaDataTransferInfo = { 0 };
382                            MyWiaDataCallback dataCallback { };
383
384                            specBufferSize.ulKind = PRSPEC_PROPID;
385                            specBufferSize.propid = WIA_IPA_BUFFER_SIZE;
386
387                            if(!wiaPropertyStorage.ReadLong(&specBufferSize, &nBufferSize))
388                               nBufferSize = 64 * 1024;
389                            WiaDataTransferInfo.ulSize        = sizeof(WIA_DATA_TRANSFER_INFO);
390                            WiaDataTransferInfo.ulBufferSize  = 2 * nBufferSize;
391                            WiaDataTransferInfo.bDoubleBuffer = TRUE;
392
393                            ((GuiApplication)__thisModule).Lock();
394                            scanProgress.progressBar.progress = 0;
395                            scanProgress.Create();
396                            ((GuiApplication)__thisModule).Unlock();
397                            {
398                            uint errorCode = wiaDataTransfer.idtGetBandedData(&WiaDataTransferInfo, dataCallback);
399                            //if(!wiaDataTransfer.idtGetBandedData(&WiaDataTransferInfo, dataCallback))
400                            if(!errorCode)
401                            {
402                               File source = dataCallback.f;
403                               Bitmap bitmap { };
404                               //source.MakeACopy("output.bmp");
405                               source.Seek(0, start);
406                               if(bitmap.LoadFromFile(source, format, null))
407                               {
408                                  /*if(file)
409                                  {
410                                     *file = source;
411                                     dataCallback.f = null;
412                                  }*/
413
414                                  if(!result) result = { };
415                                  result.Add(bitmap);
416                               }
417                               else
418                                  delete bitmap;
419                            }
420                            }
421                            ((GuiApplication)__thisModule).Lock();
422                            scanProgress.Destroy(0);
423                            ((GuiApplication)__thisModule).Unlock();
424
425                            delete dataCallback;
426                         }
427                         varFormat.puuid = null;
428                         PropVariantClear(&varFormat);
429                      }
430                   }
431                   PropVariantClear(&varTymed);
432                }
433                delete wiaPropertyStorage;
434                delete wiaDataTransfer;
435                delete wiaItem;
436             }
437          }
438       }
439       return result;
440    }
441 }
442
443 class WiaDeviceManager
444 {
445    IWiaDevMgr *pWiaDevMgr;
446
447    WiaDeviceManager()
448    {
449       if(CoCreateInstance(&CLSID_WiaDevMgr, null, /*CLSCTX_INPROC_SERVER*/CLSCTX_LOCAL_SERVER, &IID_IWiaDevMgr, (void*)&pWiaDevMgr))
450          printf("Error for CoCreateInstance with CLSID_WiaDevMgr\n");
451    }
452
453    ~WiaDeviceManager()
454    {
455       if(pWiaDevMgr)
456       {
457          IWiaDevMgr_Release(pWiaDevMgr);
458          pWiaDevMgr = null;
459       }
460    }
461 }
462
463 define MAX_GUID_STRING_LEN = 39;
464
465 bool ReadPropertyGuid(WiaPropertyStorage  pWiaPropertyStorage, const PROPSPEC *pPropSpec, GUID *pguidResult)
466 {
467    bool result = false;
468    PROPVARIANT propVariant;
469    if(!pWiaPropertyStorage.ReadMultiple(1, pPropSpec, &propVariant))
470    {
471       switch (propVariant.vt)
472       {
473          case VT_CLSID: *pguidResult = *propVariant.puuid; result = true; break;
474          case VT_BSTR: result = CLSIDFromString(propVariant.bstrVal, pguidResult) == 0; break;
475          case VT_LPWSTR: result = CLSIDFromString(propVariant.pwszVal, pguidResult) == 0; break;
476          case VT_LPSTR:
477          {
478              uint16 wszGuid[MAX_GUID_STRING_LEN];
479              UTF8toUTF16Buffer(propVariant.pszVal, wszGuid, MAX_GUID_STRING_LEN-1);
480              result = CLSIDFromString(wszGuid, pguidResult) == 0;
481              break;
482          }
483       }
484     }
485     PropVariantClear(&propVariant);
486     return result;
487 }
488
489 static bool comInited = false;
490
491 bool InitCOM()
492 {
493    if(!comInited && CoInitialize(null) == 0)
494       comInited = true;
495    return comInited;
496 }
497
498 void TerminateCOM()
499 {
500    if(comInited)
501    {
502       CoUninitialize();
503       comInited = false;
504    }
505 }
506
507 WiaItem GetScanner(bool alwaysAsk)
508 {
509    WiaItem result = null;
510    if(InitCOM())
511    {
512       WiaDeviceManager devMan { };
513       IWiaItem *pItemRoot;
514       Window window = ((GuiApplication)__thisModule.application).desktop.activeChild;
515       if(window) window = window.rootWindow;
516       if(!IWiaDevMgr_SelectDeviceDlg(devMan.pWiaDevMgr, window ? window.systemHandle : 0,
517          StiDeviceTypeScanner /*StiDeviceTypeDefault*/, alwaysAsk ? WIA_SELECT_DEVICE_NODEFAULT : 0, 0, &pItemRoot))
518       {
519          result = { pWiaItem = pItemRoot };
520       }
521       delete devMan;
522    }
523    return result;
524 }