Use strlen or lstrlenA as appropriate to avoid signed/unsigned
[wine] / dlls / kernel / tests / process.c
1 /*
2  * Unit test suite for CreateProcess function.
3  *
4  * Copyright 2002 Eric Pouech
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <assert.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include "wine/test.h"
26 #include "winbase.h"
27 #include "winuser.h"
28
29 static char     base[MAX_PATH];
30 static char     selfname[MAX_PATH];
31 static char     resfile[MAX_PATH];
32
33 static int      myARGC;
34 static char**   myARGV;
35
36 /* ---------------- portable memory allocation thingie */
37
38 static char     memory[16384];
39 static char*    memory_index = memory;
40
41 static char*    grab_memory(size_t len)
42 {
43     char*       ret = memory_index;
44     /* align on dword */
45     len = (len + 3) & ~3;
46     memory_index += len;
47     assert(memory_index <= memory + sizeof(memory));
48     return ret;
49 }
50
51 static void     release_memory(void)
52 {
53     memory_index = memory;
54 }
55
56 /* ---------------- simplistic tool to encode/decode strings (to hide \ " ' and such) */
57
58 static char*    encodeA(const char* str)
59 {
60     char*       ptr;
61     size_t      len,i;
62
63     if (!str) return "";
64     len = strlen(str) + 1;
65     ptr = grab_memory(len * 2 + 1);
66     for (i = 0; i < len; i++)
67         sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
68     ptr[2 * len] = '\0';
69     return ptr;
70 }
71
72 static char*    encodeW(const WCHAR* str)
73 {
74     char*       ptr;
75     size_t      len,i;
76
77     if (!str) return "";
78     len = lstrlenW(str) + 1;
79     ptr = grab_memory(len * 4 + 1);
80     assert(ptr);
81     for (i = 0; i < len; i++)
82         sprintf(&ptr[i * 4], "%04x", (unsigned int)(unsigned short)str[i]);
83     ptr[4 * len] = '\0';
84     return ptr;
85 }
86
87 static unsigned decode_char(char c)
88 {
89     if (c >= '0' && c <= '9') return c - '0';
90     if (c >= 'a' && c <= 'f') return c - 'a' + 10;
91     assert(c >= 'A' && c <= 'F');
92     return c - 'A' + 10;
93 }
94
95 static char*    decodeA(const char* str)
96 {
97     char*       ptr;
98     size_t      len,i;
99
100     len = strlen(str) / 2;
101     if (!len--) return NULL;
102     ptr = grab_memory(len + 1);
103     for (i = 0; i < len; i++)
104         ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
105     ptr[len] = '\0';
106     return ptr;
107 }
108
109 static WCHAR*   decodeW(const char* str)
110 {
111     size_t      len;
112     WCHAR*      ptr;
113     int         i;
114
115     len = strlen(str) / 4;
116     if (!len--) return NULL;
117     ptr = (WCHAR*)grab_memory(len * 2 + 1);
118     for (i = 0; i < len; i++)
119         ptr[i] = (decode_char(str[4 * i]) << 12) |
120             (decode_char(str[4 * i + 1]) << 8) |
121             (decode_char(str[4 * i + 2]) << 4) |
122             (decode_char(str[4 * i + 3]) << 0);
123     ptr[len] = '\0';
124     return ptr;
125 }
126
127 /******************************************************************
128  *              init
129  *
130  * generates basic information like:
131  *      base:           absolute path to curr dir
132  *      selfname:       the way to reinvoke ourselves
133  */
134 static int     init(void)
135 {
136     myARGC = winetest_get_mainargs( &myARGV );
137     if (!GetCurrentDirectoryA(sizeof(base), base)) return 0;
138     strcpy(selfname, myARGV[0]);
139     return 1;
140 }
141
142 /******************************************************************
143  *              get_file_name
144  *
145  * generates an absolute file_name for temporary file
146  *
147  */
148 static void     get_file_name(char* buf)
149 {
150     char        path[MAX_PATH];
151
152     buf[0] = '\0';
153     GetTempPathA(sizeof(path), path);
154     GetTempFileNameA(path, "wt", 0, buf);
155 }
156
157 /******************************************************************
158  *              static void     childPrintf
159  *
160  */
161 static void     childPrintf(HANDLE h, const char* fmt, ...)
162 {
163     va_list     valist;
164     char        buffer[2048];
165     DWORD       w;
166
167     va_start(valist, fmt);
168     vsprintf(buffer, fmt, valist);
169     va_end(valist);
170     WriteFile(h, buffer, strlen(buffer), &w, NULL);
171 }
172
173
174 /******************************************************************
175  *              doChild
176  *
177  * output most of the information in the child process
178  */
179 static void     doChild(const char* file)
180 {
181     STARTUPINFOA        siA;
182     STARTUPINFOW        siW;
183     int                 i;
184     char*               ptrA;
185     WCHAR*              ptrW;
186     char                bufA[MAX_PATH];
187     WCHAR               bufW[MAX_PATH];
188     HANDLE              hFile = CreateFileA(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
189
190     if (hFile == INVALID_HANDLE_VALUE) return;
191
192     /* output of startup info (Ansi) */
193     GetStartupInfoA(&siA);
194     childPrintf(hFile,
195                 "[StartupInfoA]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
196                 "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
197                 "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
198                 "dwFlags=%lu\nwShowWindow=%u\n"
199                 "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
200                 siA.cb, encodeA(siA.lpDesktop), encodeA(siA.lpTitle),
201                 siA.dwX, siA.dwY, siA.dwXSize, siA.dwYSize,
202                 siA.dwXCountChars, siA.dwYCountChars, siA.dwFillAttribute,
203                 siA.dwFlags, siA.wShowWindow,
204                 (DWORD)siA.hStdInput, (DWORD)siA.hStdOutput, (DWORD)siA.hStdError);
205
206     /* since GetStartupInfoW is only implemented in win2k,
207      * zero out before calling so we can notice the difference
208      */
209     memset(&siW, 0, sizeof(siW));
210     GetStartupInfoW(&siW);
211     childPrintf(hFile,
212                 "[StartupInfoW]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
213                 "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
214                 "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
215                 "dwFlags=%lu\nwShowWindow=%u\n"
216                 "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
217                 siW.cb, encodeW(siW.lpDesktop), encodeW(siW.lpTitle),
218                 siW.dwX, siW.dwY, siW.dwXSize, siW.dwYSize,
219                 siW.dwXCountChars, siW.dwYCountChars, siW.dwFillAttribute,
220                 siW.dwFlags, siW.wShowWindow,
221                 (DWORD)siW.hStdInput, (DWORD)siW.hStdOutput, (DWORD)siW.hStdError);
222
223     /* Arguments */
224     childPrintf(hFile, "[Arguments]\nargcA=%d\n", myARGC);
225     for (i = 0; i < myARGC; i++)
226     {
227         childPrintf(hFile, "argvA%d=%s\n", i, encodeA(myARGV[i]));
228     }
229     childPrintf(hFile, "CommandLineA=%s\n", encodeA(GetCommandLineA()));
230
231 #if 0
232     int                 argcW;
233     WCHAR**             argvW;
234
235     /* this is part of shell32... and should be tested there */
236     argvW = CommandLineToArgvW(GetCommandLineW(), &argcW);
237     for (i = 0; i < argcW; i++)
238     {
239         childPrintf(hFile, "argvW%d=%s\n", i, encodeW(argvW[i]));
240     }
241 #endif
242     childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW()));
243
244     /* output of environment (Ansi) */
245     ptrA = GetEnvironmentStringsA();
246     if (ptrA)
247     {
248         childPrintf(hFile, "[EnvironmentA]\n");
249         i = 0;
250         while (*ptrA)
251         {
252             if (strlen(ptrA) < 128)
253             {
254                 childPrintf(hFile, "env%d=%s\n", i, encodeA(ptrA));
255                 i++;
256             }
257             ptrA += strlen(ptrA) + 1;
258         }
259         childPrintf(hFile, "\n");
260     }
261
262     /* output of environment (Unicode) */
263     ptrW = GetEnvironmentStringsW();
264     if (ptrW)
265     {
266         childPrintf(hFile, "[EnvironmentW]\n");
267         i = 0;
268         while (*ptrW)
269         {
270             if (lstrlenW(ptrW) < 128)
271             {
272                 childPrintf(hFile, "env%d=%s\n", i, encodeW(ptrW));
273                 i++;
274             }
275             ptrW += lstrlenW(ptrW) + 1;
276         }
277         childPrintf(hFile, "\n");
278     }
279
280     childPrintf(hFile, "[Misc]\n");
281     if (GetCurrentDirectoryA(sizeof(bufA), bufA))
282         childPrintf(hFile, "CurrDirA=%s\n", encodeA(bufA));
283     if (GetCurrentDirectoryW(sizeof(bufW) / sizeof(bufW[0]), bufW))
284         childPrintf(hFile, "CurrDirW=%s\n", encodeW(bufW));
285     childPrintf(hFile, "\n");
286
287     CloseHandle(hFile);
288 }
289
290 static char* getChildString(const char* sect, const char* key)
291 {
292     char        buf[1024];
293     char*       ret;
294
295     GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), resfile);
296     if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
297     assert(!(strlen(buf) & 1));
298     ret = decodeA(buf);
299     return ret;
300 }
301
302 /* FIXME: this may be moved to the wtmain.c file, because it may be needed by
303  * others... (windows uses stricmp while Un*x uses strcasecmp...)
304  */
305 static int wtstrcasecmp(const char* p1, const char* p2)
306 {
307     char c1, c2;
308
309     c1 = c2 = '@';
310     while (c1 == c2 && c1)
311     {
312         c1 = *p1++; c2 = *p2++;
313         if (c1 != c2)
314         {
315             c1 = toupper(c1); c2 = toupper(c2);
316         }
317     }
318     return c1 - c2;
319 }
320
321 static int strCmp(const char* s1, const char* s2, BOOL sensitive)
322 {
323     if (!s1 && !s2) return 0;
324     if (!s2) return -1;
325     if (!s1) return 1;
326     return (sensitive) ? strcmp(s1, s2) : wtstrcasecmp(s1, s2);
327 }
328
329 #define okChildString(sect, key, expect) \
330     do { \
331         char* result = getChildString((sect), (key)); \
332         ok(strCmp(result, expect, 1) == 0, "%s:%s expected %s, got %s", (sect), (key), (expect)?(expect):"(null)", result); \
333     } while (0)
334
335 #define okChildIString(sect, key, expect) \
336     do { \
337         char* result = getChildString(sect, key); \
338         ok(strCmp(result, expect, 0) == 0, "%s:%s expected %s, got %s", sect, key, expect, result); \
339     } while (0)
340
341 /* using !expect insures that the test will fail if the sect/key isn't present
342  * in result file
343  */
344 #define okChildInt(sect, key, expect) \
345     do { \
346         UINT result = GetPrivateProfileIntA((sect), (key), !(expect), resfile); \
347         ok(result == expect, "%s:%s expected %d, but got %d\n", (sect), (key), (int)(expect), result); \
348    } while (0)
349
350 static void test_Startup(void)
351 {
352     char                buffer[MAX_PATH];
353     PROCESS_INFORMATION info;
354     STARTUPINFOA        startup;
355
356     /* let's start simplistic */
357     memset(&startup, 0, sizeof(startup));
358     startup.cb = sizeof(startup);
359     startup.dwFlags = STARTF_USESHOWWINDOW;
360     startup.wShowWindow = SW_SHOWNORMAL;
361
362     get_file_name(resfile);
363     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
364     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
365     /* wait for child to terminate */
366     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
367     /* child process has changed result file, so let profile functions know about it */
368     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
369
370     okChildInt("StartupInfoA", "cb", startup.cb);
371     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
372     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
373     okChildInt("StartupInfoA", "dwX", startup.dwX);
374     okChildInt("StartupInfoA", "dwY", startup.dwY);
375     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
376     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
377     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
378     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
379     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
380     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
381     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
382     release_memory();
383     assert(DeleteFileA(resfile) != 0);
384
385     /* not so simplistic now */
386     memset(&startup, 0, sizeof(startup));
387     startup.cb = sizeof(startup);
388     startup.dwFlags = STARTF_USESHOWWINDOW;
389     startup.wShowWindow = SW_SHOWNORMAL;
390     startup.lpTitle = "I'm the title string";
391     startup.lpDesktop = "I'm the desktop string";
392     startup.dwXCountChars = 0x12121212;
393     startup.dwYCountChars = 0x23232323;
394     startup.dwX = 0x34343434;
395     startup.dwY = 0x45454545;
396     startup.dwXSize = 0x56565656;
397     startup.dwYSize = 0x67676767;
398     startup.dwFillAttribute = 0xA55A;
399
400     get_file_name(resfile);
401     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
402     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
403     /* wait for child to terminate */
404     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
405     /* child process has changed result file, so let profile functions know about it */
406     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
407
408     okChildInt("StartupInfoA", "cb", startup.cb);
409     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
410     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
411     okChildInt("StartupInfoA", "dwX", startup.dwX);
412     okChildInt("StartupInfoA", "dwY", startup.dwY);
413     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
414     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
415     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
416     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
417     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
418     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
419     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
420     release_memory();
421     assert(DeleteFileA(resfile) != 0);
422
423     /* not so simplistic now */
424     memset(&startup, 0, sizeof(startup));
425     startup.cb = sizeof(startup);
426     startup.dwFlags = STARTF_USESHOWWINDOW;
427     startup.wShowWindow = SW_SHOWNORMAL;
428     startup.lpTitle = "I'm the title string";
429     startup.lpDesktop = NULL;
430     startup.dwXCountChars = 0x12121212;
431     startup.dwYCountChars = 0x23232323;
432     startup.dwX = 0x34343434;
433     startup.dwY = 0x45454545;
434     startup.dwXSize = 0x56565656;
435     startup.dwYSize = 0x67676767;
436     startup.dwFillAttribute = 0xA55A;
437
438     get_file_name(resfile);
439     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
440     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
441     /* wait for child to terminate */
442     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
443     /* child process has changed result file, so let profile functions know about it */
444     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
445
446     okChildInt("StartupInfoA", "cb", startup.cb);
447     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
448     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
449     okChildInt("StartupInfoA", "dwX", startup.dwX);
450     okChildInt("StartupInfoA", "dwY", startup.dwY);
451     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
452     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
453     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
454     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
455     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
456     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
457     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
458     release_memory();
459     assert(DeleteFileA(resfile) != 0);
460
461     /* not so simplistic now */
462     memset(&startup, 0, sizeof(startup));
463     startup.cb = sizeof(startup);
464     startup.dwFlags = STARTF_USESHOWWINDOW;
465     startup.wShowWindow = SW_SHOWNORMAL;
466     startup.lpTitle = "I'm the title string";
467     startup.lpDesktop = "";
468     startup.dwXCountChars = 0x12121212;
469     startup.dwYCountChars = 0x23232323;
470     startup.dwX = 0x34343434;
471     startup.dwY = 0x45454545;
472     startup.dwXSize = 0x56565656;
473     startup.dwYSize = 0x67676767;
474     startup.dwFillAttribute = 0xA55A;
475
476     get_file_name(resfile);
477     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
478     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
479     /* wait for child to terminate */
480     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
481     /* child process has changed result file, so let profile functions know about it */
482     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
483
484     okChildInt("StartupInfoA", "cb", startup.cb);
485     todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
486     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
487     okChildInt("StartupInfoA", "dwX", startup.dwX);
488     okChildInt("StartupInfoA", "dwY", startup.dwY);
489     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
490     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
491     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
492     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
493     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
494     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
495     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
496     release_memory();
497     assert(DeleteFileA(resfile) != 0);
498
499     /* not so simplistic now */
500     memset(&startup, 0, sizeof(startup));
501     startup.cb = sizeof(startup);
502     startup.dwFlags = STARTF_USESHOWWINDOW;
503     startup.wShowWindow = SW_SHOWNORMAL;
504     startup.lpTitle = NULL;
505     startup.lpDesktop = "I'm the desktop string";
506     startup.dwXCountChars = 0x12121212;
507     startup.dwYCountChars = 0x23232323;
508     startup.dwX = 0x34343434;
509     startup.dwY = 0x45454545;
510     startup.dwXSize = 0x56565656;
511     startup.dwYSize = 0x67676767;
512     startup.dwFillAttribute = 0xA55A;
513
514     get_file_name(resfile);
515     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
516     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
517     /* wait for child to terminate */
518     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
519     /* child process has changed result file, so let profile functions know about it */
520     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
521
522     okChildInt("StartupInfoA", "cb", startup.cb);
523     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
524     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
525     okChildInt("StartupInfoA", "dwX", startup.dwX);
526     okChildInt("StartupInfoA", "dwY", startup.dwY);
527     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
528     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
529     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
530     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
531     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
532     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
533     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
534     release_memory();
535     assert(DeleteFileA(resfile) != 0);
536
537     /* not so simplistic now */
538     memset(&startup, 0, sizeof(startup));
539     startup.cb = sizeof(startup);
540     startup.dwFlags = STARTF_USESHOWWINDOW;
541     startup.wShowWindow = SW_SHOWNORMAL;
542     startup.lpTitle = "";
543     startup.lpDesktop = "I'm the desktop string";
544     startup.dwXCountChars = 0x12121212;
545     startup.dwYCountChars = 0x23232323;
546     startup.dwX = 0x34343434;
547     startup.dwY = 0x45454545;
548     startup.dwXSize = 0x56565656;
549     startup.dwYSize = 0x67676767;
550     startup.dwFillAttribute = 0xA55A;
551
552     get_file_name(resfile);
553     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
554     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
555     /* wait for child to terminate */
556     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
557     /* child process has changed result file, so let profile functions know about it */
558     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
559
560     okChildInt("StartupInfoA", "cb", startup.cb);
561     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
562     todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
563     okChildInt("StartupInfoA", "dwX", startup.dwX);
564     okChildInt("StartupInfoA", "dwY", startup.dwY);
565     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
566     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
567     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
568     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
569     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
570     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
571     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
572     release_memory();
573     assert(DeleteFileA(resfile) != 0);
574
575     /* not so simplistic now */
576     memset(&startup, 0, sizeof(startup));
577     startup.cb = sizeof(startup);
578     startup.dwFlags = STARTF_USESHOWWINDOW;
579     startup.wShowWindow = SW_SHOWNORMAL;
580     startup.lpTitle = "";
581     startup.lpDesktop = "";
582     startup.dwXCountChars = 0x12121212;
583     startup.dwYCountChars = 0x23232323;
584     startup.dwX = 0x34343434;
585     startup.dwY = 0x45454545;
586     startup.dwXSize = 0x56565656;
587     startup.dwYSize = 0x67676767;
588     startup.dwFillAttribute = 0xA55A;
589
590     get_file_name(resfile);
591     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
592     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
593     /* wait for child to terminate */
594     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
595     /* child process has changed result file, so let profile functions know about it */
596     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
597
598     okChildInt("StartupInfoA", "cb", startup.cb);
599     todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
600     todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
601     okChildInt("StartupInfoA", "dwX", startup.dwX);
602     okChildInt("StartupInfoA", "dwY", startup.dwY);
603     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
604     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
605     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
606     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
607     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
608     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
609     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
610     release_memory();
611     assert(DeleteFileA(resfile) != 0);
612
613     /* TODO: test for A/W and W/A and W/W */
614 }
615
616 static void test_CommandLine(void)
617 {
618     char                buffer[MAX_PATH];
619     PROCESS_INFORMATION info;
620     STARTUPINFOA        startup;
621
622     memset(&startup, 0, sizeof(startup));
623     startup.cb = sizeof(startup);
624     startup.dwFlags = STARTF_USESHOWWINDOW;
625     startup.wShowWindow = SW_SHOWNORMAL;
626
627     /* the basics */
628     get_file_name(resfile);
629     sprintf(buffer, "%s tests/process.c %s \"C:\\Program Files\\my nice app.exe\"", selfname, resfile);
630     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
631     /* wait for child to terminate */
632     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
633     /* child process has changed result file, so let profile functions know about it */
634     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
635
636     okChildInt("Arguments", "argcA", 4);
637     okChildString("Arguments", "argvA3", "C:\\Program Files\\my nice app.exe");
638     okChildString("Arguments", "argvA4", NULL);
639     okChildString("Arguments", "CommandLineA", buffer);
640     release_memory();
641     assert(DeleteFileA(resfile) != 0);
642
643     memset(&startup, 0, sizeof(startup));
644     startup.cb = sizeof(startup);
645     startup.dwFlags = STARTF_USESHOWWINDOW;
646     startup.wShowWindow = SW_SHOWNORMAL;
647
648     /* from Frangois */
649     get_file_name(resfile);
650     sprintf(buffer, "%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
651     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
652     /* wait for child to terminate */
653     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
654     /* child process has changed result file, so let profile functions know about it */
655     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
656
657     okChildInt("Arguments", "argcA", 6);
658     okChildString("Arguments", "argvA3", "a\"b\\");
659     okChildString("Arguments", "argvA4", "c\"");
660     okChildString("Arguments", "argvA5", "d");
661     okChildString("Arguments", "argvA6", NULL);
662     okChildString("Arguments", "CommandLineA", buffer);
663     release_memory();
664     assert(DeleteFileA(resfile) != 0);
665 }
666
667 static void test_Directory(void)
668 {
669     char                buffer[MAX_PATH];
670     PROCESS_INFORMATION info;
671     STARTUPINFOA        startup;
672     char windir[MAX_PATH];
673
674     memset(&startup, 0, sizeof(startup));
675     startup.cb = sizeof(startup);
676     startup.dwFlags = STARTF_USESHOWWINDOW;
677     startup.wShowWindow = SW_SHOWNORMAL;
678
679     /* the basics */
680     get_file_name(resfile);
681     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
682     GetWindowsDirectoryA( windir, sizeof(windir) );
683     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, windir, &startup, &info), "CreateProcess");
684     /* wait for child to terminate */
685     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
686     /* child process has changed result file, so let profile functions know about it */
687     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
688
689     okChildIString("Misc", "CurrDirA", windir);
690     release_memory();
691     assert(DeleteFileA(resfile) != 0);
692 }
693
694 START_TEST(process)
695 {
696     int b = init();
697     ok(b, "Basic init of CreateProcess test");
698     if (!b) return;
699
700     if (myARGC >= 3)
701     {
702         doChild(myARGV[2]);
703         return;
704     }
705     test_Startup();
706     test_CommandLine();
707     test_Directory();
708
709     /* things that can be tested:
710      *  lookup:         check the way program to be executed is searched
711      *  environment:    check environment string passing
712      *  handles:        check the handle inheritance stuff (+sec options)
713      *  console:        check if console creation parameters work
714      */
715 }