1 /* Unit test suite for Twain DSM functions
3 * Copyright 2009 Jeremy White, CodeWeavers, Inc.
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.
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.
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
29 #include "wine/test.h"
31 static DSMENTRYPROC pDSM_Entry
;
33 static BOOL
dsm_RegisterWindowClasses(void)
38 cls
.lpfnWndProc
= DefWindowProc
;
41 cls
.hInstance
= GetModuleHandleA(0);
43 cls
.hCursor
= LoadCursorA(0, IDC_ARROW
);
44 cls
.hbrBackground
= GetStockObject(WHITE_BRUSH
);
45 cls
.lpszMenuName
= NULL
;
46 cls
.lpszClassName
= "TWAIN_dsm_class";
47 if (!RegisterClassA(&cls
)) return FALSE
;
53 static void get_condition_code(TW_IDENTITY
*appid
, TW_IDENTITY
*source
, TW_STATUS
*status
)
56 rc
= pDSM_Entry(appid
, source
, DG_CONTROL
, DAT_STATUS
, MSG_GET
, status
);
57 ok(rc
== TWRC_SUCCESS
, "Condition code not available, rc %d\n", rc
);
60 static BOOL
get_onevalue(TW_HANDLE hcontainer
, TW_UINT32
*ret
, TW_UINT16
*type
)
63 onev
= GlobalLock(hcontainer
);
68 *type
= onev
->ItemType
;
69 GlobalUnlock(hcontainer
);
75 static TW_HANDLE
alloc_and_set_onevalue(TW_UINT32 val
, TW_UINT16 type
)
79 hcontainer
= GlobalAlloc(0, sizeof(*onev
));
82 onev
= GlobalLock(hcontainer
);
85 onev
->ItemType
= type
;
87 GlobalUnlock(hcontainer
);
91 GlobalFree(hcontainer
);
98 static void check_get(TW_CAPABILITY
*pCapability
, TW_INT32 actual_support
,
99 TW_UINT32 orig_value
, TW_UINT32 default_value
, TW_UINT32
*suggested_set_value
)
102 if (suggested_set_value
)
103 *suggested_set_value
= orig_value
+ 1;
104 p
= GlobalLock(pCapability
->hContainer
);
107 if (pCapability
->ConType
== TWON_ONEVALUE
)
109 TW_ONEVALUE
*onev
= (TW_ONEVALUE
*) p
;
110 ok(onev
->Item
== orig_value
|| !(actual_support
& TWQC_GETCURRENT
), "MSG_GET of 0x%x returned 0x%x, expecting 0x%x\n",
111 pCapability
->Cap
, onev
->Item
, orig_value
);
113 else if (pCapability
->ConType
== TWON_ENUMERATION
)
119 TW_ENUMERATION
*enumv
= (TW_ENUMERATION
*) p
;
120 p8
= enumv
->ItemList
;
121 p16
= (TW_UINT16
*) p8
;
122 p32
= (TW_UINT32
*) p8
;
123 trace("MSG_GET of 0x%x returned %d items:\n", pCapability
->Cap
, enumv
->NumItems
);
124 for (i
= 0; i
< enumv
->NumItems
; i
++)
126 if (enumv
->ItemType
== TWTY_UINT8
|| enumv
->ItemType
== TWTY_INT8
)
127 trace(" %d: 0x%x\n", i
, p8
[i
]);
128 if (enumv
->ItemType
== TWTY_UINT16
|| enumv
->ItemType
== TWTY_INT16
)
129 trace(" %d: 0x%x\n", i
, p16
[i
]);
130 if (enumv
->ItemType
== TWTY_UINT32
|| enumv
->ItemType
== TWTY_INT32
)
131 trace(" %d: 0x%x\n", i
, p32
[i
]);
133 if (enumv
->ItemType
== TWTY_UINT16
|| enumv
->ItemType
== TWTY_INT16
)
135 ok(p16
[enumv
->CurrentIndex
] == orig_value
,
136 "Type 0x%x, values from MSG_GET (0x%x) and MSG_GETCURRENT (0x%x) do not match.\n",
137 pCapability
->Cap
, p16
[enumv
->CurrentIndex
], orig_value
);
138 ok(p16
[enumv
->DefaultIndex
] == default_value
,
139 "Type 0x%x, values from MSG_GET (0x%x) and MSG_GETDEFAULT (0x%x) do not match.\n",
140 pCapability
->Cap
, p16
[enumv
->DefaultIndex
], default_value
);
141 if (suggested_set_value
)
142 *suggested_set_value
= p16
[(enumv
->CurrentIndex
+ 1) % enumv
->NumItems
];
144 if (enumv
->ItemType
== TWTY_UINT32
|| enumv
->ItemType
== TWTY_INT32
)
146 ok(p32
[enumv
->CurrentIndex
] == orig_value
,
147 "Type 0x%x, values from MSG_GET (0x%x) and MSG_GETCURRENT (0x%x) do not match.\n",
148 pCapability
->Cap
, p32
[enumv
->CurrentIndex
], orig_value
);
149 ok(p32
[enumv
->DefaultIndex
] == default_value
,
150 "Type 0x%x, values from MSG_GET (0x%x) and MSG_GETDEFAULT (0x%x) do not match.\n",
151 pCapability
->Cap
, p32
[enumv
->DefaultIndex
], default_value
);
152 if (suggested_set_value
)
153 *suggested_set_value
= p32
[(enumv
->CurrentIndex
+ 1) % enumv
->NumItems
];
157 trace("MSG_GET on type 0x%x returned type 0x%x, which we didn't check.\n", pCapability
->Cap
, pCapability
->ConType
);
158 GlobalUnlock(pCapability
->hContainer
);
162 static void test_onevalue_cap(TW_IDENTITY
*appid
, TW_IDENTITY
*source
, TW_UINT16 captype
, TW_UINT16 type
, TW_INT32 minimum_support
)
168 TW_UINT32 orig_value
= 0;
170 TW_UINT32 default_value
= 0;
171 TW_INT32 actual_support
;
173 memset(&cap
, 0, sizeof(cap
));
175 cap
.ConType
= TWON_DONTCARE16
;
177 rc
= pDSM_Entry(appid
, source
, DG_CONTROL
, DAT_CAPABILITY
, MSG_QUERYSUPPORT
, &cap
);
178 get_condition_code(appid
, source
, &status
);
179 ok(rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
,
180 "Error [rc %d|cc %d] doing MSG_QUERYSUPPORT for type 0x%x\n", rc
, status
.ConditionCode
, captype
);
181 if (rc
!= TWRC_SUCCESS
)
183 ok(get_onevalue(cap
.hContainer
, (TW_UINT32
*) &actual_support
, NULL
), "Returned cap.hContainer invalid for QuerySupport on type 0x%x\n", captype
);
184 ok((actual_support
& minimum_support
) == minimum_support
,
185 "Error: minimum support 0x%x for type 0x%x, got 0x%x\n", minimum_support
,
186 captype
, actual_support
);
189 if (actual_support
& TWQC_GETCURRENT
)
191 memset(&cap
, 0, sizeof(cap
));
193 cap
.ConType
= TWON_DONTCARE16
;
195 rc
= pDSM_Entry(appid
, source
, DG_CONTROL
, DAT_CAPABILITY
, MSG_GETCURRENT
, &cap
);
196 get_condition_code(appid
, source
, &status
);
197 ok(rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
,
198 "Error [rc %d|cc %d] doing MSG_GETCURRENT for type 0x%x\n", rc
, status
.ConditionCode
, captype
);
199 if (rc
== TWRC_SUCCESS
)
201 ok(get_onevalue(cap
.hContainer
, &orig_value
, &rtype
), "Returned cap.hContainer invalid for GETCURRENT on type 0x%x\n", captype
);
202 ok(rtype
== type
, "Returned GETCURRENT type 0x%x for cap 0x%x is not expected 0x%x\n", rtype
, captype
, type
);
203 GlobalFree(cap
.hContainer
);
207 if (actual_support
& TWQC_GETDEFAULT
)
209 memset(&cap
, 0, sizeof(cap
));
211 cap
.ConType
= TWON_DONTCARE16
;
213 rc
= pDSM_Entry(appid
, source
, DG_CONTROL
, DAT_CAPABILITY
, MSG_GETDEFAULT
, &cap
);
214 get_condition_code(appid
, source
, &status
);
215 ok(rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
,
216 "Error [rc %d|cc %d] doing MSG_GETDEFAULT for type 0x%x\n", rc
, status
.ConditionCode
, captype
);
217 if (rc
== TWRC_SUCCESS
)
219 ok(get_onevalue(cap
.hContainer
, &default_value
, &rtype
), "Returned cap.hContainer invalid for GETDEFAULT on type 0x%x\n", captype
);
220 ok(rtype
== type
, "Returned GETDEFAULT type 0x%x for cap 0x%x is not expected 0x%x\n", rtype
, captype
, type
);
221 GlobalFree(cap
.hContainer
);
225 new_value
= orig_value
;
226 if (actual_support
& TWQC_GET
)
228 memset(&cap
, 0, sizeof(cap
));
230 cap
.ConType
= TWON_DONTCARE16
;
232 rc
= pDSM_Entry(appid
, source
, DG_CONTROL
, DAT_CAPABILITY
, MSG_GET
, &cap
);
233 get_condition_code(appid
, source
, &status
);
234 ok(rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
,
235 "Error [rc %d|cc %d] doing MSG_GET for type 0x%x\n", rc
, status
.ConditionCode
, captype
);
236 check_get(&cap
, actual_support
, orig_value
, default_value
, &new_value
);
237 if (rc
== TWRC_SUCCESS
)
238 GlobalFree(cap
.hContainer
);
241 if (actual_support
& TWQC_SET
)
243 memset(&cap
, 0, sizeof(cap
));
245 cap
.ConType
= TWON_ONEVALUE
;
246 cap
.hContainer
= alloc_and_set_onevalue(new_value
, type
);
248 rc
= pDSM_Entry(appid
, source
, DG_CONTROL
, DAT_CAPABILITY
, MSG_SET
, &cap
);
249 get_condition_code(appid
, source
, &status
);
250 ok(rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
,
251 "Error [rc %d|cc %d] doing MSG_SET for type 0x%x\n", rc
, status
.ConditionCode
, captype
);
252 GlobalFree(cap
.hContainer
);
255 if (actual_support
& TWQC_RESET
)
257 memset(&cap
, 0, sizeof(cap
));
259 cap
.ConType
= TWON_DONTCARE16
;
261 rc
= pDSM_Entry(appid
, source
, DG_CONTROL
, DAT_CAPABILITY
, MSG_RESET
, &cap
);
262 get_condition_code(appid
, source
, &status
);
263 ok(rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
,
264 "Error [rc %d|cc %d] doing MSG_RESET for type 0x%x\n", rc
, status
.ConditionCode
, captype
);
265 if (rc
== TWRC_SUCCESS
)
266 GlobalFree(cap
.hContainer
);
271 static void test_single_source(TW_IDENTITY
*appid
, TW_IDENTITY
*source
)
276 UINT16 capabilities
[CAP_CUSTOMBASE
];
278 memset(&cap
, 0, sizeof(cap
));
279 cap
.Cap
= CAP_SUPPORTEDCAPS
;
280 cap
.ConType
= TWON_DONTCARE16
;
282 rc
= pDSM_Entry(appid
, source
, DG_CONTROL
, DAT_CAPABILITY
, MSG_GET
, &cap
);
283 get_condition_code(appid
, source
, &status
);
284 ok(rc
== TWRC_SUCCESS
|| status
.ConditionCode
== TWCC_SUCCESS
,
285 "Error obtaining CAP_SUPPORTEDCAPS\n");
287 memset(capabilities
, 0, sizeof(capabilities
));
288 if (rc
== TWRC_SUCCESS
&& cap
.ConType
== TWON_ARRAY
)
291 a
= GlobalLock(cap
.hContainer
);
294 if (a
->ItemType
== TWTY_UINT16
)
297 UINT16
*u
= (UINT16
*) a
->ItemList
;
298 trace("%d Capabilities:\n", a
->NumItems
);
299 for (i
= 0; i
< a
->NumItems
; i
++)
300 if (u
[i
] < sizeof(capabilities
) / sizeof(capabilities
[0]))
302 capabilities
[u
[i
]] = 1;
303 trace(" %d: 0x%x\n", i
, u
[i
]);
306 GlobalUnlock(cap
.hContainer
);
310 /* For Twain 1.6, all sources must support: */
311 ok(capabilities
[CAP_SUPPORTEDCAPS
], "CAP_SUPPORTEDCAPS not supported\n");
312 ok(capabilities
[CAP_XFERCOUNT
], "CAP_XFERCOUNT not supported\n");
313 if (capabilities
[CAP_XFERCOUNT
])
314 test_onevalue_cap(appid
, source
, CAP_XFERCOUNT
, TWTY_INT16
,
315 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
316 ok(capabilities
[CAP_UICONTROLLABLE
], "CAP_UICONTROLLABLE not supported\n");
317 if (capabilities
[CAP_UICONTROLLABLE
])
318 test_onevalue_cap(appid
, source
, CAP_UICONTROLLABLE
, TWTY_BOOL
, TWQC_GET
);
320 if (source
->SupportedGroups
& DG_IMAGE
)
323 Sources that supply image information must support DG_CONTROL / DAT_CAPABILITY /
324 MSG_GET, MSG_GETCURRENT, MSG_GETDEFAULT on:
326 ok(capabilities
[ICAP_COMPRESSION
], "ICAP_COMPRESSION not supported\n");
327 if (capabilities
[ICAP_COMPRESSION
])
328 test_onevalue_cap(appid
, source
, ICAP_COMPRESSION
, TWTY_UINT16
,
329 TWQC_GET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
);
331 ok(capabilities
[ICAP_PLANARCHUNKY
], "ICAP_PLANARCHUNKY not supported\n");
333 ok(capabilities
[ICAP_PHYSICALHEIGHT
], "ICAP_PHYSICALHEIGHT not supported\n");
335 ok(capabilities
[ICAP_PHYSICALWIDTH
], "ICAP_PHYSICALWIDTH not supported\n");
336 ok(capabilities
[ICAP_PIXELFLAVOR
], "ICAP_PIXELFLAVOR not supported\n");
337 if (capabilities
[ICAP_PIXELFLAVOR
])
338 test_onevalue_cap(appid
, source
, ICAP_PIXELFLAVOR
, TWTY_UINT16
,
339 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
342 Sources that supply image information must support DG_CONTROL / DAT_CAPABILITY /
343 MSG_GET, MSG_GETCURRENT, MSG_GETDEFAULT, MSG_RESET and MSG_SET on:
346 ok(capabilities
[ICAP_BITDEPTH
], "ICAP_BITDEPTH not supported\n");
348 ok(capabilities
[ICAP_BITORDER
], "ICAP_BITORDER not supported\n");
349 ok(capabilities
[ICAP_PIXELTYPE
], "ICAP_PIXELTYPE not supported\n");
350 if (capabilities
[ICAP_PIXELTYPE
])
351 test_onevalue_cap(appid
, source
, ICAP_PIXELTYPE
, TWTY_UINT16
,
352 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
354 ok(capabilities
[ICAP_UNITS
], "ICAP_UNITS not supported\n");
355 ok(capabilities
[ICAP_XFERMECH
], "ICAP_XFERMECH not supported\n");
356 if (capabilities
[ICAP_XFERMECH
])
357 test_onevalue_cap(appid
, source
, ICAP_XFERMECH
, TWTY_UINT16
,
358 TWQC_GET
| TWQC_SET
| TWQC_GETDEFAULT
| TWQC_GETCURRENT
| TWQC_RESET
);
360 ok(capabilities
[ICAP_XRESOLUTION
], "ICAP_XRESOLUTION not supported\n");
362 ok(capabilities
[ICAP_YRESOLUTION
], "ICAP_YRESOLUTION not supported\n");
366 static void test_sources(TW_IDENTITY
*appid
)
371 int scannercount
= 0;
373 memset(&source
, 0, sizeof(source
));
374 rc
= pDSM_Entry(appid
, NULL
, DG_CONTROL
, DAT_IDENTITY
, MSG_GETFIRST
, &source
);
375 get_condition_code(appid
, NULL
, &status
);
376 ok( (rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
) ||
377 (rc
== TWRC_FAILURE
&& status
.ConditionCode
== TWCC_NODS
),
378 "Get first invalid condition code, rc %d, cc %d\n", rc
, status
.ConditionCode
);
380 while (rc
== TWRC_SUCCESS
)
383 trace("[Scanner %d|Version %d.%d(%s)|Protocol %d.%d|SupportedGroups 0x%x|Manufacturer %s|Family %s|ProductName %s]\n",
385 source
.Version
.MajorNum
, source
.Version
.MinorNum
, source
.Version
.Info
,
386 source
.ProtocolMajor
, source
.ProtocolMinor
, source
.SupportedGroups
,
387 source
.Manufacturer
, source
.ProductFamily
, source
.ProductName
);
388 memset(&source
, 0, sizeof(source
));
389 rc
= pDSM_Entry(appid
, NULL
, DG_CONTROL
, DAT_IDENTITY
, MSG_GETNEXT
, &source
);
390 get_condition_code(appid
, NULL
, &status
);
391 ok(rc
== TWRC_SUCCESS
|| rc
== TWRC_ENDOFLIST
, "Get next source failed, rc %d, cc %d\n", rc
, status
.ConditionCode
);
394 memset(&source
, 0, sizeof(source
));
395 rc
= pDSM_Entry(appid
, NULL
, DG_CONTROL
, DAT_IDENTITY
, MSG_GETDEFAULT
, &source
);
396 get_condition_code(appid
, NULL
, &status
);
397 ok( (rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
) ||
398 (rc
== TWRC_FAILURE
&& status
.ConditionCode
== TWCC_NODS
),
399 "Get default invalid condition code, rc %d, cc %d\n", rc
, status
.ConditionCode
);
401 if (rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
)
403 rc
= pDSM_Entry(appid
, NULL
, DG_CONTROL
, DAT_IDENTITY
, MSG_OPENDS
, &source
);
404 get_condition_code(appid
, NULL
, &status
);
406 if (rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
)
408 rc
= pDSM_Entry(appid
, NULL
, DG_CONTROL
, DAT_IDENTITY
, MSG_CLOSEDS
, &source
);
409 get_condition_code(appid
, NULL
, &status
);
410 ok(rc
== TWRC_SUCCESS
, "Close DS Failed, rc %d, cc %d\n", rc
, status
.ConditionCode
);
414 if (winetest_interactive
)
416 trace("Interactive, so trying userselect\n");
417 memset(&source
, 0, sizeof(source
));
418 rc
= pDSM_Entry(appid
, NULL
, DG_CONTROL
, DAT_IDENTITY
, MSG_USERSELECT
, &source
);
419 get_condition_code(appid
, NULL
, &status
);
420 ok(rc
== TWRC_SUCCESS
|| rc
== TWRC_CANCEL
, "Userselect failed, rc %d, cc %d\n", rc
, status
.ConditionCode
);
422 if (rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
)
424 rc
= pDSM_Entry(appid
, NULL
, DG_CONTROL
, DAT_IDENTITY
, MSG_OPENDS
, &source
);
425 get_condition_code(appid
, NULL
, &status
);
426 if (rc
== TWRC_SUCCESS
&& status
.ConditionCode
== TWCC_SUCCESS
)
428 test_single_source(appid
, &source
);
429 rc
= pDSM_Entry(appid
, NULL
, DG_CONTROL
, DAT_IDENTITY
, MSG_CLOSEDS
, &source
);
430 get_condition_code(appid
, NULL
, &status
);
431 ok(rc
== TWRC_SUCCESS
, "Close DS Failed, rc %d, cc %d\n", rc
, status
.ConditionCode
);
445 if (!dsm_RegisterWindowClasses()) assert(0);
447 htwain
= LoadLibraryA("twain_32.dll");
450 skip("twain_32.dll not available, skipping tests\n");
453 pDSM_Entry
= (void*)GetProcAddress(htwain
, "DSM_Entry");
454 ok(pDSM_Entry
!= NULL
, "Unable to GetProcAddress DSM_Entry\n");
457 skip("DSM_Entry not available, skipping tests\n");
461 memset(&appid
, 0, sizeof(appid
));
462 appid
.Version
.Language
= TWLG_ENGLISH_USA
;
463 appid
.Version
.Country
= TWCY_USA
;
464 appid
.ProtocolMajor
= TWON_PROTOCOLMAJOR
;
465 appid
.ProtocolMinor
= TWON_PROTOCOLMINOR
;
466 appid
.SupportedGroups
= DG_CONTROL
| DG_IMAGE
;
468 hwnd
= CreateWindow("TWAIN_dsm_class", "Twain Test", 0,
469 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
470 NULL
, NULL
, GetModuleHandleA(0), NULL
);
472 rc
= pDSM_Entry(&appid
, NULL
, DG_CONTROL
, DAT_PARENT
, MSG_OPENDSM
, (TW_MEMREF
) &hwnd
);
473 ok(rc
== TWRC_SUCCESS
, "MSG_OPENDSM returned %d\n", rc
);
475 test_sources(&appid
);
477 rc
= pDSM_Entry(&appid
, NULL
, DG_CONTROL
, DAT_PARENT
, MSG_CLOSEDSM
, (TW_MEMREF
) &hwnd
);
478 ok(rc
== TWRC_SUCCESS
, "MSG_CLOSEDSM returned %d\n", rc
);