1 /* Unit test suite for Twain DSM functions
2  *
3  * Copyright 2009 Jeremy White, CodeWeavers, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  *
19  */
20 #include <stdarg.h>
21 
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "winerror.h"
26 #include "winuser.h"
27 #include "twain.h"
28 
29 #include "wine/test.h"
30 
31 static DSMENTRYPROC pDSM_Entry;
32 
33 static BOOL dsm_RegisterWindowClasses(void)
34 {
35     WNDCLASSA cls;
36     BOOL rc;
37 
38     cls.style = 0;
39     cls.lpfnWndProc = DefWindowProcA;
40     cls.cbClsExtra = 0;
41     cls.cbWndExtra = 0;
42     cls.hInstance = GetModuleHandleA(0);
43     cls.hIcon = 0;
44     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
45     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
46     cls.lpszMenuName = NULL;
47     cls.lpszClassName = "TWAIN_dsm_class";
48 
49     rc = RegisterClassA(&cls);
50     ok(rc, "RegisterClassA failed: le=%u\n", GetLastError());
51     return rc;
52 }
53 
54 
55 static void get_condition_code(TW_IDENTITY *appid, TW_IDENTITY *source, TW_STATUS *status)
56 {
57     TW_UINT16 rc;
58     rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_STATUS, MSG_GET, status);
59     ok(rc == TWRC_SUCCESS, "Condition code not available, rc %d\n", rc);
60 }
61 
62 static BOOL get_onevalue(TW_HANDLE hcontainer, TW_UINT32 *ret, TW_UINT16 *type)
63 {
64     TW_ONEVALUE *onev;
65     onev = GlobalLock(hcontainer);
66     if (onev)
67     {
68         *ret = onev->Item;
69         if (type)
70             *type = onev->ItemType;
71         GlobalUnlock(hcontainer);
72         return TRUE;
73     }
74     else
75         *ret = 0;
76     return FALSE;
77 }
78 
79 static TW_HANDLE alloc_and_set_onevalue(TW_UINT32 val, TW_UINT16 type)
80 {
81     TW_HANDLE hcontainer;
82     TW_ONEVALUE *onev;
83     hcontainer = GlobalAlloc(0, sizeof(*onev));
84     if (hcontainer)
85     {
86         onev = GlobalLock(hcontainer);
87         if (onev)
88         {
89             onev->ItemType = type;
90             onev->Item = val;
91             GlobalUnlock(hcontainer);
92         }
93         else
94         {
95             GlobalFree(hcontainer);
96             hcontainer = 0;
97         }
98     }
99     return hcontainer;
100 }
101 
102 static void check_get(TW_CAPABILITY *pCapability, TW_INT32 actual_support,
103                 TW_UINT32 orig_value, TW_UINT32 default_value, TW_UINT32 *suggested_set_value)
104 {
105     void *p;
106     if (suggested_set_value)
107         *suggested_set_value = orig_value + 1;
108     p = GlobalLock(pCapability->hContainer);
109     if (p)
110     {
111         if (pCapability->ConType == TWON_ONEVALUE)
112         {
113             TW_ONEVALUE *onev = p;
114             ok(onev->Item == orig_value || !(actual_support & TWQC_GETCURRENT), "MSG_GET of 0x%x returned 0x%x, expecting 0x%x\n",
115                 pCapability->Cap, onev->Item, orig_value);
116             trace("MSG_GET of 0x%x returned val 0x%x, type %d\n", pCapability->Cap, onev->Item, onev->ItemType);
117             if (suggested_set_value)
118                 *suggested_set_value = onev->Item;
119         }
120         else if (pCapability->ConType == TWON_ENUMERATION)
121         {
122             int i;
123             TW_UINT8 *p8;
124             TW_UINT16 *p16;
125             TW_UINT32 *p32;
126             TW_ENUMERATION *enumv = p;
127             p8 = enumv->ItemList;
128             p16 = (TW_UINT16 *) p8;
129             p32 = (TW_UINT32 *) p8;
130             trace("MSG_GET of 0x%x returned %d items:\n", pCapability->Cap, enumv->NumItems);
131             for (i = 0; i < enumv->NumItems; i++)
132             {
133                 if (enumv->ItemType == TWTY_UINT8 || enumv->ItemType == TWTY_INT8)
134                     trace("  %d: 0x%x\n", i, p8[i]);
135                 if (enumv->ItemType == TWTY_UINT16 || enumv->ItemType == TWTY_INT16)
136                     trace("  %d: 0x%x\n", i, p16[i]);
137                 if (enumv->ItemType == TWTY_UINT32 || enumv->ItemType == TWTY_INT32)
138                     trace("  %d: 0x%x\n", i, p32[i]);
139             }
140             if (enumv->ItemType == TWTY_UINT16 || enumv->ItemType == TWTY_INT16)
141             {
142                 ok(p16[enumv->CurrentIndex] == orig_value,
143                     "Type 0x%x, values from MSG_GET (0x%x) and MSG_GETCURRENT (0x%x) do not match.\n",
144                     pCapability->Cap, p16[enumv->CurrentIndex], orig_value);
145                 ok(p16[enumv->DefaultIndex] == default_value,
146                     "Type 0x%x, values from MSG_GET (0x%x) and MSG_GETDEFAULT (0x%x) do not match.\n",
147                     pCapability->Cap, p16[enumv->DefaultIndex], default_value);
148                 if (suggested_set_value)
149                     *suggested_set_value = p16[(enumv->CurrentIndex + 1) % enumv->NumItems];
150             }
151             if (enumv->ItemType == TWTY_UINT32 || enumv->ItemType == TWTY_INT32)
152             {
153                 ok(p32[enumv->CurrentIndex] == orig_value,
154                     "Type 0x%x, values from MSG_GET (0x%x) and MSG_GETCURRENT (0x%x) do not match.\n",
155                     pCapability->Cap, p32[enumv->CurrentIndex], orig_value);
156                 ok(p32[enumv->DefaultIndex] == default_value,
157                     "Type 0x%x, values from MSG_GET (0x%x) and MSG_GETDEFAULT (0x%x) do not match.\n",
158                     pCapability->Cap, p32[enumv->DefaultIndex], default_value);
159                 if (suggested_set_value)
160                     *suggested_set_value = p32[(enumv->CurrentIndex + 1) % enumv->NumItems];
161             }
162         }
163         else
164             trace("MSG_GET on type 0x%x returned type 0x%x, which we didn't check.\n", pCapability->Cap, pCapability->ConType);
165         GlobalUnlock(pCapability->hContainer);
166     }
167 }
168 
169 static void test_onevalue_cap(TW_IDENTITY *appid, TW_IDENTITY *source, TW_UINT16 captype, TW_UINT16 type, TW_INT32 minimum_support)
170 {
171     TW_UINT16 rc;
172     TW_UINT16 rtype;
173     TW_STATUS status;
174     TW_CAPABILITY cap;
175     TW_UINT32 orig_value = 0;
176     TW_UINT32 new_value;
177     TW_UINT32 default_value = 0;
178     TW_INT32 actual_support;
179 
180     memset(&cap, 0, sizeof(cap));
181     cap.Cap = captype;
182     cap.ConType = TWON_DONTCARE16;
183 
184     rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_QUERYSUPPORT, &cap);
185     get_condition_code(appid, source, &status);
186     ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
187             "Error [rc %d|cc %d] doing MSG_QUERYSUPPORT for type 0x%x\n", rc, status.ConditionCode, captype);
188     if (rc != TWRC_SUCCESS)
189         return;
190     ok(get_onevalue(cap.hContainer, (TW_UINT32 *) &actual_support, NULL), "Returned cap.hContainer invalid for QuerySupport on type 0x%x\n", captype);
191     ok((actual_support & minimum_support) == minimum_support,
192             "Error:  minimum support 0x%x for type 0x%x, got 0x%x\n", minimum_support,
193             captype, actual_support);
194 
195 
196     if (actual_support & TWQC_GETCURRENT)
197     {
198         memset(&cap, 0, sizeof(cap));
199         cap.Cap = captype;
200         cap.ConType = TWON_DONTCARE16;
201 
202         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETCURRENT, &cap);
203         get_condition_code(appid, source, &status);
204         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
205                 "Error [rc %d|cc %d] doing MSG_GETCURRENT for type 0x%x\n", rc, status.ConditionCode, captype);
206         if (rc == TWRC_SUCCESS)
207         {
208             ok(get_onevalue(cap.hContainer, &orig_value, &rtype), "Returned cap.hContainer invalid for GETCURRENT on type 0x%x\n", captype);
209             ok(rtype == type, "Returned GETCURRENT type 0x%x for cap 0x%x is not expected 0x%x\n", rtype, captype, type);
210             GlobalFree(cap.hContainer);
211         }
212     }
213 
214     if (actual_support & TWQC_GETDEFAULT)
215     {
216         memset(&cap, 0, sizeof(cap));
217         cap.Cap = captype;
218         cap.ConType = TWON_DONTCARE16;
219 
220         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETDEFAULT, &cap);
221         get_condition_code(appid, source, &status);
222         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
223                 "Error [rc %d|cc %d] doing MSG_GETDEFAULT for type 0x%x\n", rc, status.ConditionCode, captype);
224         if (rc == TWRC_SUCCESS)
225         {
226             ok(get_onevalue(cap.hContainer, &default_value, &rtype), "Returned cap.hContainer invalid for GETDEFAULT on type 0x%x\n", captype);
227             ok(rtype == type, "Returned GETDEFAULT type 0x%x for cap 0x%x is not expected 0x%x\n", rtype, captype, type);
228             GlobalFree(cap.hContainer);
229         }
230     }
231 
232     new_value = orig_value;
233     if (actual_support & TWQC_GET)
234     {
235         memset(&cap, 0, sizeof(cap));
236         cap.Cap = captype;
237         cap.ConType = TWON_DONTCARE16;
238 
239         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GET, &cap);
240         get_condition_code(appid, source, &status);
241         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
242                 "Error [rc %d|cc %d] doing MSG_GET for type 0x%x\n", rc, status.ConditionCode, captype);
243         check_get(&cap, actual_support, orig_value, default_value, &new_value);
244         if (rc == TWRC_SUCCESS)
245             GlobalFree(cap.hContainer);
246     }
247 
248     if (actual_support & TWQC_SET)
249     {
250         memset(&cap, 0, sizeof(cap));
251         cap.Cap = captype;
252         cap.ConType = TWON_ONEVALUE;
253         cap.hContainer = alloc_and_set_onevalue(new_value, type);
254 
255         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_SET, &cap);
256         get_condition_code(appid, source, &status);
257         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
258                 "Error [rc %d|cc %d] doing MSG_SET for type 0x%x\n", rc, status.ConditionCode, captype);
259         GlobalFree(cap.hContainer);
260     }
261 
262     if (actual_support & TWQC_RESET)
263     {
264         memset(&cap, 0, sizeof(cap));
265         cap.Cap = captype;
266         cap.ConType = TWON_DONTCARE16;
267 
268         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_RESET, &cap);
269         get_condition_code(appid, source, &status);
270         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
271                 "Error [rc %d|cc %d] doing MSG_RESET for type 0x%x\n", rc, status.ConditionCode, captype);
272         if (rc == TWRC_SUCCESS)
273             GlobalFree(cap.hContainer);
274     }
275 }
276 
277 static void test_resolution(TW_IDENTITY *appid, TW_IDENTITY *source, TW_UINT16 captype, TW_INT32 minimum_support)
278 {
279     TW_UINT16 rc;
280     TW_STATUS status;
281     TW_CAPABILITY cap;
282     TW_UINT32 val;
283     TW_UINT16 type;
284     TW_INT32 actual_support;
285     TW_FIX32 orig_value = { 0, 0 };
286     TW_UINT32 new_value = 0;
287     TW_FIX32 default_value = { 0, 0 };
288 
289     memset(&cap, 0, sizeof(cap));
290     cap.Cap = captype;
291     cap.ConType = TWON_DONTCARE16;
292 
293     rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_QUERYSUPPORT, &cap);
294     get_condition_code(appid, source, &status);
295     ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
296             "Error [rc %d|cc %d] doing MSG_QUERYSUPPORT for type 0x%x\n", rc, status.ConditionCode, captype);
297     if (rc != TWRC_SUCCESS)
298         return;
299     ok(get_onevalue(cap.hContainer, (TW_UINT32 *) &actual_support, NULL), "Returned cap.hContainer invalid for QuerySupport on type 0x%x\n", captype);
300     ok((actual_support & minimum_support) == minimum_support,
301             "Error:  minimum support 0x%x for type 0x%x, got 0x%x\n", minimum_support,
302             captype, actual_support);
303 
304 
305     if (actual_support & TWQC_GETCURRENT)
306     {
307         memset(&cap, 0, sizeof(cap));
308         cap.Cap = captype;
309         cap.ConType = TWON_DONTCARE16;
310 
311         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETCURRENT, &cap);
312         get_condition_code(appid, source, &status);
313         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
314                 "Error [rc %d|cc %d] doing MSG_GETCURRENT for type 0x%x\n", rc, status.ConditionCode, captype);
315         if (rc == TWRC_SUCCESS)
316         {
317             get_onevalue(cap.hContainer, &val, &type);
318             ok(type == TWTY_FIX32, "GETCURRENT for RESOLUTION is not type FIX32, is type %d\n", type);
319             memcpy(&orig_value, &val, sizeof(orig_value));
320             GlobalFree(cap.hContainer);
321         }
322     }
323 
324     if (actual_support & TWQC_GETDEFAULT)
325     {
326         memset(&cap, 0, sizeof(cap));
327         cap.Cap = captype;
328         cap.ConType = TWON_DONTCARE16;
329 
330         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETDEFAULT, &cap);
331         get_condition_code(appid, source, &status);
332         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
333                 "Error [rc %d|cc %d] doing MSG_GETDEFAULT for type 0x%x\n", rc, status.ConditionCode, captype);
334         if (rc == TWRC_SUCCESS)
335         {
336             get_onevalue(cap.hContainer, &val, &type);
337             ok(type == TWTY_FIX32, "GETDEFAULT for RESOLUTION is not type FIX32, is type %d\n", type);
338             memcpy(&default_value, &val, sizeof(default_value));
339             GlobalFree(cap.hContainer);
340         }
341     }
342 
343     if (actual_support & TWQC_GET)
344     {
345         memset(&cap, 0, sizeof(cap));
346         cap.Cap = captype;
347         cap.ConType = TWON_DONTCARE16;
348 
349         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GET, &cap);
350         get_condition_code(appid, source, &status);
351         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
352                 "Error [rc %d|cc %d] doing MSG_GET for type 0x%x\n", rc, status.ConditionCode, captype);
353         if (rc == TWRC_SUCCESS)
354         {
355             TW_RANGE *range;
356             ok(cap.ConType == TWON_RANGE, "MSG_GET for ICAP_[XY]RESOLUTION did not return TWON_RANGE, but %d\n", cap.ConType);
357             range = GlobalLock(cap.hContainer);
358             trace("MSG_GET of 0x%x returned [ItemType %d|MinValue %d|MaxValue %d|StepSize %d|DefaultValue %d|CurrentValue %d]:\n",
359                     cap.Cap, range->ItemType, range->MinValue, range->MaxValue, range->StepSize,
360                     range->DefaultValue, range->CurrentValue);
361             for (new_value = range->MinValue; new_value < range->MaxValue; new_value += range->StepSize)
362                 if (new_value != range->CurrentValue)
363                     break;
364             GlobalUnlock(cap.hContainer);
365             GlobalFree(cap.hContainer);
366         }
367     }
368 
369     if (actual_support & TWQC_SET)
370     {
371         memset(&cap, 0, sizeof(cap));
372         cap.Cap = captype;
373         cap.ConType = TWON_ONEVALUE;
374         cap.hContainer = alloc_and_set_onevalue(new_value, TWTY_FIX32);
375 
376         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_SET, &cap);
377         get_condition_code(appid, source, &status);
378         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
379                 "Error [rc %d|cc %d] doing MSG_SET for type 0x%x\n", rc, status.ConditionCode, captype);
380         GlobalFree(cap.hContainer);
381 
382     }
383 
384     if (actual_support & TWQC_RESET)
385     {
386         memset(&cap, 0, sizeof(cap));
387         cap.Cap = captype;
388         cap.ConType = TWON_DONTCARE16;
389 
390         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_RESET, &cap);
391         get_condition_code(appid, source, &status);
392         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
393                 "Error [rc %d|cc %d] doing MSG_RESET for type 0x%x\n", rc, status.ConditionCode, captype);
394         if (rc == TWRC_SUCCESS)
395             GlobalFree(cap.hContainer);
396     }
397 }
398 
399 static void test_physical(TW_IDENTITY *appid, TW_IDENTITY *source, TW_UINT16 captype, TW_INT32 minimum_support)
400 {
401     TW_UINT16 rc;
402     TW_STATUS status;
403     TW_CAPABILITY cap;
404     TW_UINT32 val;
405     TW_UINT16 type;
406     TW_INT32 actual_support;
407 
408     memset(&cap, 0, sizeof(cap));
409     cap.Cap = captype;
410     cap.ConType = TWON_DONTCARE16;
411 
412     rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_QUERYSUPPORT, &cap);
413     get_condition_code(appid, source, &status);
414     ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
415             "Error [rc %d|cc %d] doing MSG_QUERYSUPPORT for type 0x%x\n", rc, status.ConditionCode, captype);
416     if (rc != TWRC_SUCCESS)
417         return;
418     ok(get_onevalue(cap.hContainer, (TW_UINT32 *) &actual_support, NULL), "Returned cap.hContainer invalid for QuerySupport on type 0x%x\n", captype);
419     ok((actual_support & minimum_support) == minimum_support,
420             "Error:  minimum support 0x%x for type 0x%x, got 0x%x\n", minimum_support,
421             captype, actual_support);
422 
423 
424     if (actual_support & TWQC_GETCURRENT)
425     {
426         memset(&cap, 0, sizeof(cap));
427         cap.Cap = captype;
428         cap.ConType = TWON_DONTCARE16;
429 
430         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETCURRENT, &cap);
431         get_condition_code(appid, source, &status);
432         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
433                 "Error [rc %d|cc %d] doing MSG_GETCURRENT for type 0x%x\n", rc, status.ConditionCode, captype);
434         if (rc == TWRC_SUCCESS)
435         {
436             get_onevalue(cap.hContainer, &val, &type);
437             ok(type == TWTY_FIX32, "GETCURRENT for PHYSICALXXX is not type FIX32, is type %d\n", type);
438             GlobalFree(cap.hContainer);
439         }
440     }
441 
442     if (actual_support & TWQC_GETDEFAULT)
443     {
444         memset(&cap, 0, sizeof(cap));
445         cap.Cap = captype;
446         cap.ConType = TWON_DONTCARE16;
447 
448         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETDEFAULT, &cap);
449         get_condition_code(appid, source, &status);
450         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
451                 "Error [rc %d|cc %d] doing MSG_GETDEFAULT for type 0x%x\n", rc, status.ConditionCode, captype);
452         if (rc == TWRC_SUCCESS)
453         {
454             get_onevalue(cap.hContainer, &val, &type);
455             ok(type == TWTY_FIX32, "GETDEFAULT for PHYSICALXXX is not type FIX32, is type %d\n", type);
456             GlobalFree(cap.hContainer);
457         }
458     }
459 
460     if (actual_support & TWQC_GET)
461     {
462         memset(&cap, 0, sizeof(cap));
463         cap.Cap = captype;
464         cap.ConType = TWON_DONTCARE16;
465 
466         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GET, &cap);
467         get_condition_code(appid, source, &status);
468         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
469                 "Error [rc %d|cc %d] doing MSG_GET for type 0x%x\n", rc, status.ConditionCode, captype);
470         if (rc == TWRC_SUCCESS)
471         {
472             get_onevalue(cap.hContainer, &val, &type);
473             ok(type == TWTY_FIX32, "GET for PHYSICALXXX is not type FIX32, is type %d\n", type);
474             trace("GET for Physical type 0x%x returns 0x%x\n", captype, val);
475             GlobalFree(cap.hContainer);
476         }
477     }
478 
479 }
480 
481 static void test_supported_sizes(TW_IDENTITY *appid, TW_IDENTITY *source, TW_INT32 minimum_support)
482 {
483     TW_UINT16 rc;
484     TW_STATUS status;
485     TW_CAPABILITY cap;
486     TW_UINT32 val;
487     TW_UINT16 type;
488     TW_INT32 actual_support;
489     TW_UINT32 orig_value = TWSS_NONE;
490     TW_UINT32 default_value = TWSS_NONE;
491     TW_UINT32 new_value = TWSS_NONE;
492 
493 
494     memset(&cap, 0, sizeof(cap));
495     cap.Cap = ICAP_SUPPORTEDSIZES;
496     cap.ConType = TWON_DONTCARE16;
497 
498     rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_QUERYSUPPORT, &cap);
499     get_condition_code(appid, source, &status);
500     ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
501             "Error [rc %d|cc %d] doing MSG_QUERYSUPPORT for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
502     if (rc != TWRC_SUCCESS)
503         return;
504     ok(get_onevalue(cap.hContainer, (TW_UINT32 *) &actual_support, NULL), "Returned cap.hContainer invalid for QuerySupport on ICAP_SUPPORTEDSIZES\n");
505     ok((actual_support & minimum_support) == minimum_support,
506             "Error:  minimum support 0x%x for ICAP_SUPPORTEDSIZES, got 0x%x\n", minimum_support, actual_support);
507 
508     if (actual_support & TWQC_GETCURRENT)
509     {
510         memset(&cap, 0, sizeof(cap));
511         cap.Cap = ICAP_SUPPORTEDSIZES;
512         cap.ConType = TWON_DONTCARE16;
513 
514         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETCURRENT, &cap);
515         get_condition_code(appid, source, &status);
516         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
517                 "Error [rc %d|cc %d] doing MSG_GETCURRENT for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
518         if (rc == TWRC_SUCCESS)
519         {
520             get_onevalue(cap.hContainer, &val, &type);
521             ok(type == TWTY_UINT16, "GETCURRENT for ICAP_SUPPORTEDSIZES is not type UINT16, is type %d\n", type);
522             trace("Current size is %d\n", val);
523             GlobalFree(cap.hContainer);
524             orig_value = val;
525         }
526     }
527 
528     if (actual_support & TWQC_GETDEFAULT)
529     {
530         memset(&cap, 0, sizeof(cap));
531         cap.Cap = ICAP_SUPPORTEDSIZES;
532         cap.ConType = TWON_DONTCARE16;
533 
534         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GETDEFAULT, &cap);
535         get_condition_code(appid, source, &status);
536         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
537                 "Error [rc %d|cc %d] doing MSG_GETDEFAULT for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
538         if (rc == TWRC_SUCCESS)
539         {
540             get_onevalue(cap.hContainer, &val, &type);
541             ok(type == TWTY_UINT16, "GETDEFAULT for PHYSICALXXX is not type TWTY_UINT16, is type %d\n", type);
542             trace("Default size is %d\n", val);
543             GlobalFree(cap.hContainer);
544             default_value = val;
545         }
546     }
547 
548     if (actual_support & TWQC_GET)
549     {
550         memset(&cap, 0, sizeof(cap));
551         cap.Cap = ICAP_SUPPORTEDSIZES;
552         cap.ConType = TWON_DONTCARE16;
553 
554         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GET, &cap);
555         get_condition_code(appid, source, &status);
556         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
557                 "Error [rc %d|cc %d] doing MSG_GET for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
558         check_get(&cap, actual_support, orig_value, default_value, &new_value);
559     }
560 
561     if (actual_support & TWQC_SET)
562     {
563         memset(&cap, 0, sizeof(cap));
564         cap.Cap = ICAP_SUPPORTEDSIZES;
565         cap.ConType = TWON_ONEVALUE;
566         cap.hContainer = alloc_and_set_onevalue(new_value, TWTY_UINT16);
567 
568         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_SET, &cap);
569         get_condition_code(appid, source, &status);
570         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
571                 "Error [rc %d|cc %d] doing MSG_SET for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
572         GlobalFree(cap.hContainer);
573 
574     }
575 
576     if (actual_support & TWQC_RESET)
577     {
578         memset(&cap, 0, sizeof(cap));
579         cap.Cap = ICAP_SUPPORTEDSIZES;
580         cap.ConType = TWON_DONTCARE16;
581 
582         rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_RESET, &cap);
583         get_condition_code(appid, source, &status);
584         ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
585                 "Error [rc %d|cc %d] doing MSG_RESET for ICAP_SUPPORTEDSIZES\n", rc, status.ConditionCode);
586         if (rc == TWRC_SUCCESS)
587             GlobalFree(cap.hContainer);
588     }
589 }
590 
591 static void test_imagelayout(TW_IDENTITY *appid, TW_IDENTITY *source)
592 {
593     TW_UINT16 rc;
594     TW_STATUS status;
595     TW_IMAGELAYOUT layout;
596 
597     rc = pDSM_Entry(appid, source, DG_IMAGE, DAT_IMAGELAYOUT, MSG_GET, &layout);
598     get_condition_code(appid, source, &status);
599     ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
600             "Error [rc %d|cc %d] doing MSG_GET for DG_IMAGE/DAT_IMAGELAYOUT\n", rc, status.ConditionCode);
601     if (rc != TWRC_SUCCESS)
602         return;
603     trace("ImageLayout [Left %x.%x|Top %x.%x|Right %x.%x|Bottom %x.%x|Document %d|Page %d|Frame %d]\n",
604             layout.Frame.Left.Whole, layout.Frame.Left.Frac,
605             layout.Frame.Top.Whole, layout.Frame.Top.Frac,
606             layout.Frame.Right.Whole, layout.Frame.Right.Frac,
607             layout.Frame.Bottom.Whole, layout.Frame.Bottom.Frac,
608             layout.DocumentNumber, layout.PageNumber, layout.FrameNumber);
609 
610     memset(&layout, 0, sizeof(layout));
611     layout.Frame.Left.Whole = 1;
612     layout.Frame.Right.Whole = 2;
613     layout.Frame.Top.Whole = 1;
614     layout.Frame.Bottom.Whole = 2;
615     rc = pDSM_Entry(appid, source, DG_IMAGE, DAT_IMAGELAYOUT, MSG_SET, &layout);
616     get_condition_code(appid, source, &status);
617     ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
618             "Error [rc %d|cc %d] doing MSG_SET for DG_IMAGE/DAT_IMAGELAYOUT\n", rc, status.ConditionCode);
619     if (rc != TWRC_SUCCESS)
620         return;
621 
622     rc = pDSM_Entry(appid, source, DG_IMAGE, DAT_IMAGELAYOUT, MSG_GET, &layout);
623     get_condition_code(appid, source, &status);
624     ok(rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS,
625             "Error [rc %d|cc %d] doing MSG_GET for DG_IMAGE/DAT_IMAGELAYOUT\n", rc, status.ConditionCode);
626     if (rc != TWRC_SUCCESS)
627         return;
628     trace("ImageLayout after set [Left %x.%x|Top %x.%x|Right %x.%x|Bottom %x.%x|Document %d|Page %d|Frame %d]\n",
629             layout.Frame.Left.Whole, layout.Frame.Left.Frac,
630             layout.Frame.Top.Whole, layout.Frame.Top.Frac,
631             layout.Frame.Right.Whole, layout.Frame.Right.Frac,
632             layout.Frame.Bottom.Whole, layout.Frame.Bottom.Frac,
633             layout.DocumentNumber, layout.PageNumber, layout.FrameNumber);
634 }
635 
636 
637 static void test_single_source(TW_IDENTITY *appid, TW_IDENTITY *source)
638 {
639     TW_UINT16 rc;
640     TW_STATUS status;
641     TW_CAPABILITY cap;
642     UINT16 capabilities[CAP_CUSTOMBASE];
643 
644     memset(&cap, 0, sizeof(cap));
645     cap.Cap = CAP_SUPPORTEDCAPS;
646     cap.ConType = TWON_DONTCARE16;
647 
648     rc = pDSM_Entry(appid, source, DG_CONTROL, DAT_CAPABILITY, MSG_GET, &cap);
649     get_condition_code(appid, source, &status);
650     ok(rc == TWRC_SUCCESS || status.ConditionCode == TWCC_SUCCESS,
651             "Error obtaining CAP_SUPPORTEDCAPS\n");
652 
653     memset(capabilities, 0, sizeof(capabilities));
654     if (rc == TWRC_SUCCESS && cap.ConType == TWON_ARRAY)
655     {
656         TW_ARRAY *a;
657         a = GlobalLock(cap.hContainer);
658         if (a)
659         {
660             if (a->ItemType == TWTY_UINT16)
661             {
662                 int i;
663                 UINT16 *u = (UINT16 *) a->ItemList;
664                 trace("%d Capabilities:\n", a->NumItems);
665                 for (i = 0; i < a->NumItems; i++)
666                     if (u[i] < ARRAY_SIZE(capabilities))
667                     {
668                         capabilities[u[i]] = 1;
669                         trace("  %d: 0x%x\n", i, u[i]);
670                     }
671             }
672             GlobalUnlock(cap.hContainer);
673         }
674     }
675 
676     /* All sources must support: */
677     ok(capabilities[CAP_SUPPORTEDCAPS], "CAP_SUPPORTEDCAPS not supported\n");
678     ok(capabilities[CAP_XFERCOUNT], "CAP_XFERCOUNT not supported\n");
679     if (capabilities[CAP_XFERCOUNT])
680         test_onevalue_cap(appid, source, CAP_XFERCOUNT, TWTY_INT16,
681             TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
682     ok(capabilities[CAP_UICONTROLLABLE], "CAP_UICONTROLLABLE not supported\n");
683     if (capabilities[CAP_UICONTROLLABLE])
684         test_onevalue_cap(appid, source, CAP_UICONTROLLABLE, TWTY_BOOL, TWQC_GET);
685 
686     if (source->SupportedGroups & DG_IMAGE)
687     {
688         /*
689             Sources that supply image information must support DG_CONTROL / DAT_CAPABILITY /
690             MSG_GET, MSG_GETCURRENT, MSG_GETDEFAULT on:
691         */
692         ok(capabilities[ICAP_COMPRESSION], "ICAP_COMPRESSION not supported\n");
693         if (capabilities[ICAP_COMPRESSION])
694             test_onevalue_cap(appid, source, ICAP_COMPRESSION, TWTY_UINT16,
695                 TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT);
696         todo_wine
697         ok(capabilities[ICAP_PLANARCHUNKY], "ICAP_PLANARCHUNKY not supported\n");
698         ok(capabilities[ICAP_PHYSICALHEIGHT], "ICAP_PHYSICALHEIGHT not supported\n");
699         if (capabilities[ICAP_PHYSICALHEIGHT])
700             test_physical(appid, source, ICAP_PHYSICALHEIGHT,
701                 TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT);
702         ok(capabilities[ICAP_PHYSICALWIDTH], "ICAP_PHYSICALWIDTH not supported\n");
703         if (capabilities[ICAP_PHYSICALWIDTH])
704             test_physical(appid, source, ICAP_PHYSICALWIDTH,
705                 TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT);
706         ok(capabilities[ICAP_PIXELFLAVOR], "ICAP_PIXELFLAVOR not supported\n");
707         if (capabilities[ICAP_PIXELFLAVOR])
708             test_onevalue_cap(appid, source, ICAP_PIXELFLAVOR, TWTY_UINT16,
709                 TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT);
710 
711         /*
712             Sources that supply image information must support DG_CONTROL / DAT_CAPABILITY /
713             MSG_GET, MSG_GETCURRENT, MSG_GETDEFAULT, MSG_RESET and MSG_SET on:
714         */
715         ok(capabilities[ICAP_BITDEPTH], "ICAP_BITDEPTH not supported\n");
716         if (capabilities[ICAP_BITDEPTH])
717             test_onevalue_cap(appid, source, ICAP_BITDEPTH, TWTY_UINT16,
718                 TWQC_GET | TWQC_GETDEFAULT | TWQC_GETCURRENT );
719         todo_wine
720         ok(capabilities[ICAP_BITORDER], "ICAP_BITORDER not supported\n");
721         ok(capabilities[ICAP_PIXELTYPE], "ICAP_PIXELTYPE not supported\n");
722         if (capabilities[ICAP_PIXELTYPE])
723             test_onevalue_cap(appid, source, ICAP_PIXELTYPE, TWTY_UINT16,
724                 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
725         ok(capabilities[ICAP_UNITS], "ICAP_UNITS not supported\n");
726         if (capabilities[ICAP_UNITS])
727             test_onevalue_cap(appid, source, ICAP_UNITS, TWTY_UINT16,
728                 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
729         ok(capabilities[ICAP_XFERMECH], "ICAP_XFERMECH not supported\n");
730         if (capabilities[ICAP_XFERMECH])
731             test_onevalue_cap(appid, source, ICAP_XFERMECH, TWTY_UINT16,
732                 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
733         ok(capabilities[ICAP_XRESOLUTION], "ICAP_XRESOLUTION not supported\n");
734         if (capabilities[ICAP_XRESOLUTION])
735             test_resolution(appid, source, ICAP_XRESOLUTION,
736                 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
737         ok(capabilities[ICAP_YRESOLUTION], "ICAP_YRESOLUTION not supported\n");
738         if (capabilities[ICAP_YRESOLUTION])
739             test_resolution(appid, source, ICAP_YRESOLUTION,
740                 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
741 
742         /* Optional capabilities */
743         if (capabilities[CAP_AUTOFEED])
744             test_onevalue_cap(appid, source, CAP_AUTOFEED, TWTY_BOOL,
745                 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
746         if (capabilities[CAP_FEEDERENABLED])
747             test_onevalue_cap(appid, source, CAP_FEEDERENABLED, TWTY_BOOL,
748                 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
749         if (capabilities[ICAP_SUPPORTEDSIZES])
750             test_supported_sizes(appid, source,
751                 TWQC_GET | TWQC_SET | TWQC_GETDEFAULT | TWQC_GETCURRENT | TWQC_RESET);
752 
753         /* Additional tests */
754         test_imagelayout(appid, source);
755 
756     }
757 }
758 
759 static void test_sources(TW_IDENTITY *appid)
760 {
761     TW_UINT16 rc;
762     TW_IDENTITY source;
763     TW_STATUS status;
764     int scannercount = 0;
765 
766     memset(&source, 0, sizeof(source));
767     rc = pDSM_Entry(appid, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETFIRST, &source);
768     get_condition_code(appid, NULL, &status);
769     ok( (rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS) ||
770         (rc == TWRC_FAILURE && status.ConditionCode == TWCC_NODS),
771             "Get first invalid condition code, rc %d, cc %d\n", rc, status.ConditionCode);
772 
773     while (rc == TWRC_SUCCESS)
774     {
775         scannercount++;
776         trace("[Scanner %d|Version %d.%d(%s)|Protocol %d.%d|SupportedGroups 0x%x|Manufacturer %s|Family %s|ProductName %s]\n",
777             scannercount,
778             source.Version.MajorNum, source.Version.MinorNum, source.Version.Info,
779             source.ProtocolMajor, source.ProtocolMinor, source.SupportedGroups,
780             source.Manufacturer, source.ProductFamily, source.ProductName);
781         memset(&source, 0, sizeof(source));
782         rc = pDSM_Entry(appid, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETNEXT, &source);
783         get_condition_code(appid, NULL, &status);
784         ok(rc == TWRC_SUCCESS || rc == TWRC_ENDOFLIST, "Get next source failed, rc %d, cc %d\n", rc, status.ConditionCode);
785     }
786 
787     memset(&source, 0, sizeof(source));
788     rc = pDSM_Entry(appid, NULL, DG_CONTROL, DAT_IDENTITY, MSG_GETDEFAULT, &source);
789     get_condition_code(appid, NULL, &status);
790     ok( (rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS) ||
791         (rc == TWRC_FAILURE && status.ConditionCode == TWCC_NODS),
792             "Get default invalid condition code, rc %d, cc %d\n", rc, status.ConditionCode);
793 
794     /* A DS might display a Popup during MSG_OPENDS, when the scanner is not connected */
795     if (rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS && winetest_interactive)
796     {
797         rc = pDSM_Entry(appid, NULL, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, &source);
798         get_condition_code(appid, NULL, &status);
799 
800         if (rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS)
801         {
802             rc = pDSM_Entry(appid, NULL, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, &source);
803             get_condition_code(appid, NULL, &status);
804             ok(rc == TWRC_SUCCESS, "Close DS Failed, rc %d, cc %d\n", rc, status.ConditionCode);
805         }
806     }
807 
808     if (winetest_interactive)
809     {
810         trace("Interactive, so trying userselect\n");
811         memset(&source, 0, sizeof(source));
812         rc = pDSM_Entry(appid, NULL, DG_CONTROL, DAT_IDENTITY, MSG_USERSELECT, &source);
813         get_condition_code(appid, NULL, &status);
814         ok(rc == TWRC_SUCCESS || rc == TWRC_CANCEL, "Userselect failed, rc %d, cc %d\n", rc, status.ConditionCode);
815 
816         if (rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS)
817         {
818             rc = pDSM_Entry(appid, NULL, DG_CONTROL, DAT_IDENTITY, MSG_OPENDS, &source);
819             get_condition_code(appid, NULL, &status);
820             if (rc == TWRC_SUCCESS && status.ConditionCode == TWCC_SUCCESS)
821             {
822                 test_single_source(appid, &source);
823                 rc = pDSM_Entry(appid, NULL, DG_CONTROL, DAT_IDENTITY, MSG_CLOSEDS, &source);
824                 get_condition_code(appid, NULL, &status);
825                 ok(rc == TWRC_SUCCESS, "Close DS Failed, rc %d, cc %d\n", rc, status.ConditionCode);
826             }
827         }
828     }
829 
830 }
831 
832 START_TEST(dsm)
833 {
834     TW_IDENTITY appid;
835     TW_UINT16 rc;
836     HANDLE hwnd;
837     HMODULE htwain;
838 
839     if (!dsm_RegisterWindowClasses())
840     {
841         skip("Could not register the test class, skipping tests\n");
842         return;
843     }
844 
845     htwain = LoadLibraryA("twain_32.dll");
846     if (! htwain)
847     {
848         win_skip("twain_32.dll not available, skipping tests\n");
849         return;
850     }
851     pDSM_Entry = (void*)GetProcAddress(htwain, "DSM_Entry");
852     ok(pDSM_Entry != NULL, "Unable to GetProcAddress DSM_Entry\n");
853     if (! pDSM_Entry)
854     {
855         win_skip("DSM_Entry not available, skipping tests\n");
856         return;
857     }
858 
859     memset(&appid, 0, sizeof(appid));
860     appid.Version.Language = TWLG_ENGLISH_USA;
861     appid.Version.Country = TWCY_USA;
862     appid.ProtocolMajor = TWON_PROTOCOLMAJOR;
863     appid.ProtocolMinor = TWON_PROTOCOLMINOR;
864     appid.SupportedGroups = DG_CONTROL | DG_IMAGE;
865 
866     hwnd = CreateWindowA("TWAIN_dsm_class", "Twain Test", 0, CW_USEDEFAULT, CW_USEDEFAULT,
867                          CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandleA(NULL), NULL);
868 
869     rc = pDSM_Entry(&appid, NULL, DG_CONTROL, DAT_PARENT, MSG_OPENDSM, (TW_MEMREF) &hwnd);
870     ok(rc == TWRC_SUCCESS, "MSG_OPENDSM returned %d\n", rc);
871 
872     test_sources(&appid);
873 
874     rc = pDSM_Entry(&appid, NULL, DG_CONTROL, DAT_PARENT, MSG_CLOSEDSM, (TW_MEMREF) &hwnd);
875     ok(rc == TWRC_SUCCESS, "MSG_CLOSEDSM returned %d\n", rc);
876 
877     DestroyWindow(hwnd);
878     FreeLibrary(htwain);
879 }
880