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