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