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