1 /*
2  * Copyright (C) 2005 Mike McCormack for CodeWeavers
3  *
4  * A test program for MSI records
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <windows.h>
22 #include <msi.h>
23 #include <msiquery.h>
24 
25 #include "wine/test.h"
26 
27 static const char *msifile = "winetest-record.msi";
28 static const WCHAR msifileW[] = L"winetest-record.msi";
29 
30 static BOOL create_temp_file(char *name)
31 {
32     UINT r;
33     unsigned char buffer[26], i;
34     DWORD sz;
35     HANDLE handle;
36 
37     r = GetTempFileNameA(".", "msitest",0,name);
38     if(!r)
39         return r;
40     handle = CreateFileA(name, GENERIC_READ|GENERIC_WRITE,
41         0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
42     if(handle==INVALID_HANDLE_VALUE)
43         return FALSE;
44     for(i=0; i<26; i++)
45         buffer[i]=i+'a';
46     r = WriteFile(handle,buffer,sizeof buffer,&sz,NULL);
47     CloseHandle(handle);
48     return r;
49 }
50 
51 static void test_msirecord(void)
52 {
53     DWORD r, sz;
54     INT i;
55     MSIHANDLE h;
56     char buf[10];
57     WCHAR bufW[10];
58     const char str[] = "hello";
59     char filename[MAX_PATH];
60 
61     /* check behaviour with an invalid record */
62     r = MsiRecordGetFieldCount(0);
63     ok(r==-1, "field count for invalid record not -1\n");
64     SetLastError(0);
65     r = MsiRecordIsNull(0, 0);
66     ok(r==0, "invalid handle not considered to be non-null...\n");
67     ok(GetLastError()==0, "MsiRecordIsNull set LastError\n");
68     r = MsiRecordGetInteger(0,0);
69     ok(r == MSI_NULL_INTEGER, "got integer from invalid record\n");
70     r = MsiRecordSetInteger(0,0,0);
71     ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n");
72     r = MsiRecordSetInteger(0,-1,0);
73     ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n");
74     SetLastError(0);
75     h = MsiCreateRecord(-1);
76     ok(h==0, "created record with -1 elements\n");
77     h = MsiCreateRecord(0x10000);
78     ok(h==0, "created record with 0x10000 elements\n");
79     /* doesn't set LastError */
80     ok(GetLastError()==0, "MsiCreateRecord set last error\n");
81     r = MsiRecordClearData(0);
82     ok(r == ERROR_INVALID_HANDLE, "MsiRecordClearData returned wrong error\n");
83     r = MsiRecordDataSize(0,0);
84     ok(r == 0, "MsiRecordDataSize returned wrong error\n");
85 
86 
87     /* check behaviour of a record with 0 elements */
88     h = MsiCreateRecord(0);
89     ok(h!=0, "couldn't create record with zero elements\n");
90     r = MsiRecordGetFieldCount(h);
91     ok(r==0, "field count should be zero\n");
92     r = MsiRecordIsNull(h,0);
93     ok(r, "new record wasn't null\n");
94     r = MsiRecordIsNull(h,1);
95     ok(r, "out of range record wasn't null\n");
96     r = MsiRecordIsNull(h,-1);
97     ok(r, "out of range record wasn't null\n");
98     r = MsiRecordDataSize(h,0);
99     ok(r==0, "size of null record is 0\n");
100     sz = sizeof buf;
101     strcpy(buf,"x");
102     r = MsiRecordGetStringA(h, 0, buf, &sz);
103     ok(r==ERROR_SUCCESS, "failed to get null string\n");
104     ok(sz==0, "null string too long\n");
105     ok(buf[0]==0, "null string not set\n");
106 
107     /* same record, but add an integer to it */
108     r = MsiRecordSetInteger(h, 0, 0);
109     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 0\n");
110     r = MsiRecordIsNull(h,0);
111     ok(r==0, "new record is null after setting an integer\n");
112     r = MsiRecordDataSize(h,0);
113     ok(r==sizeof(DWORD), "size of integer record is 4\n");
114     r = MsiRecordSetInteger(h, 0, 1);
115     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 1\n");
116     r = MsiRecordSetInteger(h, 1, 1);
117     ok(r == ERROR_INVALID_PARAMETER, "set integer at 1\n");
118     r = MsiRecordSetInteger(h, -1, 0);
119     ok(r == ERROR_INVALID_PARAMETER, "set integer at -1\n");
120     r = MsiRecordIsNull(h,0);
121     ok(r==0, "new record is null after setting an integer\n");
122     r = MsiRecordGetInteger(h, 0);
123     ok(r == 1, "failed to get integer\n");
124 
125     /* same record, but add a null or empty string to it */
126     r = MsiRecordSetStringA(h, 0, NULL);
127     ok(r == ERROR_SUCCESS, "Failed to set null string at 0\n");
128     r = MsiRecordIsNull(h, 0);
129     ok(r == TRUE, "null string not null field\n");
130     r = MsiRecordDataSize(h, 0);
131     ok(r == 0, "size of string record is strlen\n");
132     buf[0] = 0;
133     sz = sizeof buf;
134     r = MsiRecordGetStringA(h, 0, buf, &sz);
135     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
136     ok(buf[0] == 0, "MsiRecordGetStringA returned the wrong string\n");
137     ok(sz == 0, "MsiRecordGetStringA returned the wrong length\n");
138     bufW[0] = 0;
139     sz = ARRAY_SIZE(bufW);
140     r = MsiRecordGetStringW(h, 0, bufW, &sz);
141     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
142     ok(bufW[0] == 0, "MsiRecordGetStringW returned the wrong string\n");
143     ok(sz == 0, "MsiRecordGetStringW returned the wrong length\n");
144     r = MsiRecordSetStringA(h, 0, "");
145     ok(r == ERROR_SUCCESS, "Failed to set empty string at 0\n");
146     r = MsiRecordIsNull(h, 0);
147     ok(r == TRUE, "null string not null field\n");
148     r = MsiRecordDataSize(h, 0);
149     ok(r == 0, "size of string record is strlen\n");
150     buf[0] = 0;
151     sz = sizeof buf;
152     r = MsiRecordGetStringA(h, 0, buf, &sz);
153     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
154     ok(buf[0] == 0, "MsiRecordGetStringA returned the wrong string\n");
155     ok(sz == 0, "MsiRecordGetStringA returned the wrong length\n");
156     bufW[0] = 0;
157     sz = ARRAY_SIZE(bufW);
158     r = MsiRecordGetStringW(h, 0, bufW, &sz);
159     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
160     ok(bufW[0] == 0, "MsiRecordGetStringW returned the wrong string\n");
161     ok(sz == 0, "MsiRecordGetStringW returned the wrong length\n");
162 
163     /* same record, but add a null integer to it */
164     r = MsiRecordSetInteger(h, 0, 1);
165     ok(r == ERROR_SUCCESS, "Failed to set integer at 0\n");
166     r = MsiRecordIsNull(h, 0);
167     ok(r == FALSE, "expected field to be non-null\n");
168     r = MsiRecordSetInteger(h, 0, MSI_NULL_INTEGER);
169     ok(r == ERROR_SUCCESS, "Failed to set integer at 0\n");
170     r = MsiRecordIsNull(h, 0);
171     ok(r == TRUE, "expected field to be null\n");
172     sz = sizeof buf;
173     r = MsiRecordGetStringA(h, 0, buf, &sz);
174     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
175     ok(buf[0] == 0, "MsiRecordGetStringA returned the wrong string\n");
176     ok(sz == 0, "MsiRecordGetStringA returned the wrong length\n");
177 
178     /* same record, but add a string to it */
179     r = MsiRecordSetStringA(h,0,str);
180     ok(r == ERROR_SUCCESS, "Failed to set string at 0\n");
181     r = MsiRecordGetInteger(h, 0);
182     ok(r == MSI_NULL_INTEGER, "should get invalid integer\n");
183     r = MsiRecordDataSize(h,0);
184     ok(r==sizeof str-1, "size of string record is strlen\n");
185     buf[0]=0;
186     sz = sizeof buf;
187     r = MsiRecordGetStringA(h,0,buf,&sz);
188     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
189     ok(0==strcmp(buf,str), "MsiRecordGetStringA returned the wrong string\n");
190     ok(sz == sizeof str-1, "MsiRecordGetStringA returned the wrong length\n");
191     buf[0]=0;
192     sz = sizeof str - 2;
193     r = MsiRecordGetStringA(h,0,buf,&sz);
194     ok(r == ERROR_MORE_DATA, "small buffer should yield ERROR_MORE_DATA\n");
195     ok(sz == sizeof str-1, "MsiRecordGetStringA returned the wrong length\n");
196     ok(0==strncmp(buf,str,sizeof str-3), "MsiRecordGetStringA returned the wrong string\n");
197     ok(buf[sizeof str - 3]==0, "string wasn't nul terminated\n");
198 
199     buf[0]=0;
200     sz = sizeof str;
201     r = MsiRecordGetStringA(h,0,buf,&sz);
202     ok(r == ERROR_SUCCESS, "wrong error\n");
203     ok(sz == sizeof str-1, "MsiRecordGetStringA returned the wrong length\n");
204     ok(0==strcmp(buf,str), "MsiRecordGetStringA returned the wrong string\n");
205 
206 
207     memset(bufW, 0, sizeof bufW);
208     sz = 5;
209     r = MsiRecordGetStringW(h,0,bufW,&sz);
210     ok(r == ERROR_MORE_DATA, "wrong error\n");
211     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
212     ok(!memcmp(bufW, L"hello", 8), "MsiRecordGetStringA returned the wrong string\n");
213 
214     sz = 0;
215     bufW[0] = 'x';
216     r = MsiRecordGetStringW(h,0,bufW,&sz);
217     ok(r == ERROR_MORE_DATA, "wrong error\n");
218     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
219     ok('x'==bufW[0], "MsiRecordGetStringA returned the wrong string\n");
220 
221     memset(buf, 0, sizeof buf);
222     sz = 5;
223     r = MsiRecordGetStringA(h,0,buf,&sz);
224     ok(r == ERROR_MORE_DATA, "wrong error\n");
225     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
226     ok(0==memcmp(buf,str,4), "MsiRecordGetStringA returned the wrong string\n");
227 
228     sz = 0;
229     buf[0] = 'x';
230     r = MsiRecordGetStringA(h,0,buf,&sz);
231     ok(r == ERROR_MORE_DATA, "wrong error\n");
232     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
233     ok('x'==buf[0], "MsiRecordGetStringA returned the wrong string\n");
234 
235     /* same record, check we can wipe all the data */
236     r = MsiRecordClearData(h);
237     ok(r == ERROR_SUCCESS, "Failed to clear record\n");
238     r = MsiRecordClearData(h);
239     ok(r == ERROR_SUCCESS, "Failed to clear record again\n");
240     r = MsiRecordIsNull(h,0);
241     ok(r, "cleared record wasn't null\n");
242 
243     /* same record, try converting strings to integers */
244     i = MsiRecordSetStringA(h,0,"42");
245     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
246     i = MsiRecordGetInteger(h, 0);
247     ok(i == 42, "should get invalid integer\n");
248     i = MsiRecordSetStringA(h,0,"-42");
249     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
250     i = MsiRecordGetInteger(h, 0);
251     ok(i == -42, "should get invalid integer\n");
252     i = MsiRecordSetStringA(h,0," 42");
253     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
254     i = MsiRecordGetInteger(h, 0);
255     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
256     i = MsiRecordSetStringA(h,0,"42 ");
257     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
258     i = MsiRecordGetInteger(h, 0);
259     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
260     i = MsiRecordSetStringA(h,0,"42.0");
261     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
262     i = MsiRecordGetInteger(h, 0);
263     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
264     i = MsiRecordSetStringA(h,0,"0x42");
265     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
266     i = MsiRecordGetInteger(h, 0);
267     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
268     i = MsiRecordSetStringA(h,0,"1000000000000000");
269     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
270     i = MsiRecordGetInteger(h, 0);
271     ok(i == -1530494976, "should get truncated integer\n");
272     i = MsiRecordSetStringA(h,0,"2147483647");
273     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
274     i = MsiRecordGetInteger(h, 0);
275     ok(i == 2147483647, "should get maxint\n");
276     i = MsiRecordSetStringA(h,0,"-2147483647");
277     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
278     i = MsiRecordGetInteger(h, 0);
279     ok(i == -2147483647, "should get -maxint-1\n");
280     i = MsiRecordSetStringA(h,0,"4294967297");
281     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
282     i = MsiRecordGetInteger(h, 0);
283     ok(i == 1, "should get one\n");
284     i = MsiRecordSetStringA(h,0,"foo");
285     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
286     i = MsiRecordGetInteger(h, 0);
287     ok(i == MSI_NULL_INTEGER, "should get zero\n");
288     i = MsiRecordSetStringA(h,0,"");
289     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
290     i = MsiRecordGetInteger(h, 0);
291     ok(i == MSI_NULL_INTEGER, "should get zero\n");
292     i = MsiRecordSetStringA(h,0,"+1");
293     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
294     i = MsiRecordGetInteger(h, 0);
295     ok(i == MSI_NULL_INTEGER, "should get zero\n");
296 
297     /* same record, try converting integers to strings */
298     r = MsiRecordSetInteger(h, 0, 32);
299     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n");
300     sz = 1;
301     r = MsiRecordGetStringA(h, 0, NULL, &sz);
302     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
303     ok(sz == 2, "length wrong\n");
304     buf[0]=0;
305     sz = sizeof buf;
306     r = MsiRecordGetStringA(h, 0, buf, &sz);
307     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
308     ok(0==strcmp(buf,"32"), "failed to get string from integer\n");
309     r = MsiRecordSetInteger(h, 0, -32);
310     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n");
311     buf[0]=0;
312     sz = 1;
313     r = MsiRecordGetStringA(h, 0, NULL, &sz);
314     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
315     ok(sz == 3, "length wrong\n");
316     sz = sizeof buf;
317     r = MsiRecordGetStringA(h, 0, buf, &sz);
318     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
319     ok(0==strcmp(buf,"-32"), "failed to get string from integer\n");
320     buf[0]=0;
321 
322     /* same record, now try streams */
323     r = MsiRecordSetStreamA(h, 0, NULL);
324     ok(r == ERROR_INVALID_PARAMETER, "set NULL stream\n");
325     sz = sizeof buf;
326     r = MsiRecordReadStream(h, 0, buf, &sz);
327     ok(r == ERROR_INVALID_DATATYPE, "read non-stream type\n");
328     ok(sz == sizeof buf, "set sz\n");
329     r = MsiRecordDataSize( h, -1);
330     ok(r == 0,"MsiRecordDataSize returned wrong size\n");
331     r = MsiRecordDataSize( h, 0);
332     ok(r == 4,"MsiRecordDataSize returned wrong size\n");
333 
334     /* same record, now close it */
335     r = MsiCloseHandle(h);
336     ok(r == ERROR_SUCCESS, "Failed to close handle\n");
337 
338     /* now try streams in a new record - need to create a file to play with */
339     r = create_temp_file(filename);
340     if(!r)
341         return;
342 
343     /* streams can't be inserted in field 0 for some reason */
344     h = MsiCreateRecord(2);
345     ok(h, "couldn't create a two field record\n");
346     r = MsiRecordSetStreamA(h, 0, filename);
347     ok(r == ERROR_INVALID_PARAMETER, "added stream to field 0\n");
348     r = MsiRecordSetStreamA(h, 1, filename);
349     ok(r == ERROR_SUCCESS, "failed to add stream to record\n");
350     r = MsiRecordReadStream(h, 1, buf, NULL);
351     ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
352     DeleteFileA(filename); /* Windows 98 doesn't like this at all, so don't check return. */
353     r = MsiRecordReadStream(h, 1, NULL, NULL);
354     ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
355     sz = sizeof buf;
356     r = MsiRecordReadStream(h, 1, NULL, &sz);
357     ok(r == ERROR_SUCCESS, "failed to read stream\n");
358     ok(sz==26,"couldn't get size of stream\n");
359     sz = 0;
360     r = MsiRecordReadStream(h, 1, buf, &sz);
361     ok(r == ERROR_SUCCESS, "failed to read stream\n");
362     ok(sz==0,"short read\n");
363     sz = sizeof buf;
364     r = MsiRecordReadStream(h, 1, buf, &sz);
365     ok(r == ERROR_SUCCESS, "failed to read stream\n");
366     ok(sz==sizeof buf,"short read\n");
367     ok(!strncmp(buf,"abcdefghij",10), "read the wrong thing\n");
368     sz = sizeof buf;
369     r = MsiRecordReadStream(h, 1, buf, &sz);
370     ok(r == ERROR_SUCCESS, "failed to read stream\n");
371     ok(sz==sizeof buf,"short read\n");
372     ok(!strncmp(buf,"klmnopqrst",10), "read the wrong thing\n");
373     memset(buf,0,sizeof buf);
374     sz = sizeof buf;
375     r = MsiRecordReadStream(h, 1, buf, &sz);
376     ok(r == ERROR_SUCCESS, "failed to read stream\n");
377     ok(sz==6,"short read\n");
378     ok(!strcmp(buf,"uvwxyz"), "read the wrong thing\n");
379     memset(buf,0,sizeof buf);
380     sz = sizeof buf;
381     r = MsiRecordReadStream(h, 1, buf, &sz);
382     ok(r == ERROR_SUCCESS, "failed to read stream\n");
383     ok(sz==0,"size non-zero at end of stream\n");
384     ok(buf[0]==0, "read something at end of the stream\n");
385     r = MsiRecordSetStreamA(h, 1, NULL);
386     ok(r == ERROR_SUCCESS, "failed to reset stream\n");
387     sz = 0;
388     r = MsiRecordReadStream(h, 1, NULL, &sz);
389     ok(r == ERROR_SUCCESS, "bytes left wrong after reset\n");
390     ok(sz==26,"couldn't get size of stream\n");
391     r = MsiRecordDataSize(h,1);
392     ok(r == 26,"MsiRecordDataSize returned wrong size\n");
393 
394     /* now close the stream record */
395     r = MsiCloseHandle(h);
396     ok(r == ERROR_SUCCESS, "Failed to close handle\n");
397     DeleteFileA(filename); /* Delete it for sure, when everything else is closed. */
398 }
399 
400 static void test_MsiRecordGetString(void)
401 {
402     MSIHANDLE rec;
403     CHAR buf[MAX_PATH];
404     DWORD sz;
405     UINT r;
406 
407     rec = MsiCreateRecord(2);
408     ok(rec != 0, "Expected a valid handle\n");
409 
410     sz = MAX_PATH;
411     r = MsiRecordGetStringA(rec, 1, NULL, &sz);
412     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n",r);
413     ok(sz == 0, "Expected 0, got %lu\n", sz);
414 
415     sz = MAX_PATH;
416     lstrcpyA(buf, "apple");
417     r = MsiRecordGetStringA(rec, 1, buf, &sz);
418     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
419     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
420     ok(sz == 0, "Expected 0, got %lu\n", sz);
421 
422     sz = MAX_PATH;
423     lstrcpyA(buf, "apple");
424     r = MsiRecordGetStringA(rec, 10, buf, &sz);
425     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
426     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
427     ok(sz == 0, "Expected 0, got %lu\n", sz);
428 
429     MsiCloseHandle(rec);
430 
431     rec = MsiCreateRecord(1);
432     ok(rec != 0, "Expected a valid handle\n");
433 
434     r = MsiRecordSetInteger(rec, 1, 5);
435     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
436 
437     sz = MAX_PATH;
438     r = MsiRecordGetStringA(rec, 1, NULL, &sz);
439     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n",r);
440     ok(sz == 1, "Expected 1, got %lu\n",sz);
441 
442     sz = MAX_PATH;
443     lstrcpyA(buf, "apple");
444     r = MsiRecordGetStringA(rec, 1, buf, &sz);
445     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
446     ok(!lstrcmpA(buf, "5"), "Expected \"5\", got \"%s\"\n", buf);
447     ok(sz == 1, "Expected 1, got %lu\n", sz);
448 
449     r = MsiRecordSetInteger(rec, 1, -5);
450     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
451 
452     sz = MAX_PATH;
453     lstrcpyA(buf, "apple");
454     r = MsiRecordGetStringA(rec, 1, buf, &sz);
455     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
456     ok(!lstrcmpA(buf, "-5"), "Expected \"-5\", got \"%s\"\n", buf);
457     ok(sz == 2, "Expected 2, got %lu\n", sz);
458 
459     MsiCloseHandle(rec);
460 }
461 
462 static void test_MsiRecordGetInteger(void)
463 {
464     MSIHANDLE rec;
465     INT val;
466     UINT r;
467 
468     rec = MsiCreateRecord(1);
469     ok(rec != 0, "Expected a valid handle\n");
470 
471     r = MsiRecordSetStringA(rec, 1, "5");
472     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
473 
474     val = MsiRecordGetInteger(rec, 1);
475     ok(val == 5, "Expected 5, got %d\n", val);
476 
477     r = MsiRecordSetStringA(rec, 1, "-5");
478     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
479 
480     val = MsiRecordGetInteger(rec, 1);
481     ok(val == -5, "Expected -5, got %d\n", val);
482 
483     r = MsiRecordSetStringA(rec, 1, "5apple");
484     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
485 
486     val = MsiRecordGetInteger(rec, 1);
487     ok(val == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", val);
488 
489     MsiCloseHandle(rec);
490 }
491 
492 static void test_fieldzero(void)
493 {
494     MSIHANDLE hdb, hview, rec;
495     CHAR buf[MAX_PATH];
496     LPCSTR query;
497     DWORD sz;
498     UINT r;
499 
500     rec = MsiCreateRecord(1);
501     ok(rec != 0, "Expected a valid handle\n");
502 
503     r = MsiRecordGetInteger(rec, 0);
504     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
505 
506     sz = MAX_PATH;
507     lstrcpyA(buf, "apple");
508     r = MsiRecordGetStringA(rec, 0, buf, &sz);
509     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
510     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
511     ok(sz == 0, "Expected 0, got %lu\n", sz);
512 
513     r = MsiRecordIsNull(rec, 0);
514     ok(r == TRUE, "Expected TRUE, got %d\n", r);
515 
516     r = MsiRecordGetInteger(rec, 1);
517     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
518 
519     r = MsiRecordSetInteger(rec, 1, 42);
520     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
521 
522     r = MsiRecordGetInteger(rec, 0);
523     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
524 
525     sz = MAX_PATH;
526     lstrcpyA(buf, "apple");
527     r = MsiRecordGetStringA(rec, 0, buf, &sz);
528     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
529     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
530     ok(sz == 0, "Expected 0, got %lu\n", sz);
531 
532     r = MsiRecordIsNull(rec, 0);
533     ok(r == TRUE, "Expected TRUE, got %d\n", r);
534 
535     r = MsiRecordGetInteger(rec, 1);
536     ok(r == 42, "Expected 42, got %d\n", r);
537 
538     r = MsiRecordSetStringA(rec, 1, "bologna");
539     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
540 
541     r = MsiRecordGetInteger(rec, 0);
542     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
543 
544     sz = MAX_PATH;
545     lstrcpyA(buf, "apple");
546     r = MsiRecordGetStringA(rec, 0, buf, &sz);
547     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
548     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
549     ok(sz == 0, "Expected 0, got %lu\n", sz);
550 
551     r = MsiRecordIsNull(rec, 0);
552     ok(r == TRUE, "Expected TRUE, got %d\n", r);
553 
554     sz = MAX_PATH;
555     lstrcpyA(buf, "apple");
556     r = MsiRecordGetStringA(rec, 1, buf, &sz);
557     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
558     ok(!lstrcmpA(buf, "bologna"), "Expected \"bologna\", got \"%s\"\n", buf);
559     ok(sz == 7, "Expected 7, got %lu\n", sz);
560 
561     MsiCloseHandle(rec);
562 
563     r = MsiOpenDatabaseW(msifileW, MSIDBOPEN_CREATE, &hdb);
564     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
565 
566     query = "CREATE TABLE `drone` ( "
567            "`id` INT, `name` CHAR(32), `number` CHAR(32) "
568            "PRIMARY KEY `id`)";
569     r = MsiDatabaseOpenViewA(hdb, query, &hview);
570     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
571     r = MsiViewExecute(hview, 0);
572     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
573     r = MsiViewClose(hview);
574     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
575     r = MsiCloseHandle(hview);
576     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
577 
578     query = "INSERT INTO `drone` ( `id`, `name`, `number` )"
579            "VALUES('1', 'Abe', '8675309')";
580     r = MsiDatabaseOpenViewA(hdb, query, &hview);
581     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
582     r = MsiViewExecute(hview, 0);
583     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
584     r = MsiViewClose(hview);
585     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
586     r = MsiCloseHandle(hview);
587     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
588 
589     r = MsiDatabaseGetPrimaryKeysA(hdb, "drone", &rec);
590     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
591 
592     r = MsiRecordGetInteger(rec, 0);
593     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
594 
595     sz = MAX_PATH;
596     lstrcpyA(buf, "apple");
597     r = MsiRecordGetStringA(rec, 0, buf, &sz);
598     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
599     ok(!lstrcmpA(buf, "drone"), "Expected \"drone\", got \"%s\"\n", buf);
600     ok(sz == 5, "Expected 5, got %lu\n", sz);
601 
602     r = MsiRecordIsNull(rec, 0);
603     ok(r == FALSE, "Expected FALSE, got %d\n", r);
604 
605     MsiCloseHandle(rec);
606 
607     r = MsiDatabaseGetPrimaryKeysA(hdb, "nosuchtable", &rec);
608     ok(r == ERROR_INVALID_TABLE, "Expected ERROR_INVALID_TABLE, got %d\n", r);
609 
610     query = "SELECT * FROM `drone` WHERE `id` = 1";
611     r = MsiDatabaseOpenViewA(hdb, query, &hview);
612     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
613     r = MsiViewExecute(hview, 0);
614     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
615     r = MsiViewFetch(hview, &rec);
616     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
617 
618     r = MsiRecordGetInteger(rec, 0);
619     ok(r != MSI_NULL_INTEGER && r != 0, "Expected non-NULL value, got %d\n", r);
620 
621     r = MsiRecordIsNull(rec, 0);
622     ok(r == FALSE, "Expected FALSE, got %d\n", r);
623 
624     r = MsiCloseHandle(hview);
625     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
626     MsiCloseHandle(rec);
627     MsiCloseHandle(hdb);
628     DeleteFileA(msifile);
629 }
630 
631 START_TEST(record)
632 {
633     test_msirecord();
634     test_MsiRecordGetString();
635     test_MsiRecordGetInteger();
636     test_fieldzero();
637 }
638