Fixed header dependencies to be fully compatible with the 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 <stdarg.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include "wine/test.h"
27 #include "ntstatus.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "wincon.h"
32 #include "winnls.h"
33
34 static char     base[MAX_PATH];
35 static char     selfname[MAX_PATH];
36 static char     resfile[MAX_PATH];
37
38 static int      myARGC;
39 static char**   myARGV;
40
41 /* as some environment variables get very long on Unix, we only test for
42  * the first 127 bytes
43  */
44 #define MAX_LISTED_ENV_VAR      128
45
46 /* ---------------- portable memory allocation thingie */
47
48 static char     memory[1024*32];
49 static char*    memory_index = memory;
50
51 static char*    grab_memory(size_t len)
52 {
53     char*       ret = memory_index;
54     /* align on dword */
55     len = (len + 3) & ~3;
56     memory_index += len;
57     assert(memory_index <= memory + sizeof(memory));
58     return ret;
59 }
60
61 static void     release_memory(void)
62 {
63     memory_index = memory;
64 }
65
66 /* ---------------- simplistic tool to encode/decode strings (to hide \ " ' and such) */
67
68 static char*    encodeA(const char* str)
69 {
70     char*       ptr;
71     size_t      len,i;
72
73     if (!str) return "";
74     len = strlen(str) + 1;
75     ptr = grab_memory(len * 2 + 1);
76     for (i = 0; i < len; i++)
77         sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
78     ptr[2 * len] = '\0';
79     return ptr;
80 }
81
82 static char*    encodeW(const WCHAR* str)
83 {
84     char*       ptr;
85     size_t      len,i;
86
87     if (!str) return "";
88     len = lstrlenW(str) + 1;
89     ptr = grab_memory(len * 4 + 1);
90     assert(ptr);
91     for (i = 0; i < len; i++)
92         sprintf(&ptr[i * 4], "%04x", (unsigned int)(unsigned short)str[i]);
93     ptr[4 * len] = '\0';
94     return ptr;
95 }
96
97 static unsigned decode_char(char c)
98 {
99     if (c >= '0' && c <= '9') return c - '0';
100     if (c >= 'a' && c <= 'f') return c - 'a' + 10;
101     assert(c >= 'A' && c <= 'F');
102     return c - 'A' + 10;
103 }
104
105 static char*    decodeA(const char* str)
106 {
107     char*       ptr;
108     size_t      len,i;
109
110     len = strlen(str) / 2;
111     if (!len--) return NULL;
112     ptr = grab_memory(len + 1);
113     for (i = 0; i < len; i++)
114         ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
115     ptr[len] = '\0';
116     return ptr;
117 }
118
119 #if 0
120 /* This will be needed to decode Unicode strings saved by the child process
121  * when we test Unicode functions.
122  */
123 static WCHAR*   decodeW(const char* str)
124 {
125     size_t      len;
126     WCHAR*      ptr;
127     int         i;
128
129     len = strlen(str) / 4;
130     if (!len--) return NULL;
131     ptr = (WCHAR*)grab_memory(len * 2 + 1);
132     for (i = 0; i < len; i++)
133         ptr[i] = (decode_char(str[4 * i]) << 12) |
134             (decode_char(str[4 * i + 1]) << 8) |
135             (decode_char(str[4 * i + 2]) << 4) |
136             (decode_char(str[4 * i + 3]) << 0);
137     ptr[len] = '\0';
138     return ptr;
139 }
140 #endif
141
142 /******************************************************************
143  *              init
144  *
145  * generates basic information like:
146  *      base:           absolute path to curr dir
147  *      selfname:       the way to reinvoke ourselves
148  */
149 static int     init(void)
150 {
151     myARGC = winetest_get_mainargs( &myARGV );
152     if (!GetCurrentDirectoryA(sizeof(base), base)) return 0;
153     strcpy(selfname, myARGV[0]);
154     return 1;
155 }
156
157 /******************************************************************
158  *              get_file_name
159  *
160  * generates an absolute file_name for temporary file
161  *
162  */
163 static void     get_file_name(char* buf)
164 {
165     char        path[MAX_PATH];
166
167     buf[0] = '\0';
168     GetTempPathA(sizeof(path), path);
169     GetTempFileNameA(path, "wt", 0, buf);
170 }
171
172 /******************************************************************
173  *              static void     childPrintf
174  *
175  */
176 static void     childPrintf(HANDLE h, const char* fmt, ...)
177 {
178     va_list     valist;
179     char        buffer[2048];
180     DWORD       w;
181
182     va_start(valist, fmt);
183     vsprintf(buffer, fmt, valist);
184     va_end(valist);
185     WriteFile(h, buffer, strlen(buffer), &w, NULL);
186 }
187
188
189 /******************************************************************
190  *              doChild
191  *
192  * output most of the information in the child process
193  */
194 static void     doChild(const char* file, const char* option)
195 {
196     STARTUPINFOA        siA;
197     STARTUPINFOW        siW;
198     int                 i;
199     char*               ptrA;
200     WCHAR*              ptrW;
201     char                bufA[MAX_PATH];
202     WCHAR               bufW[MAX_PATH];
203     HANDLE              hFile = CreateFileA(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
204
205     if (hFile == INVALID_HANDLE_VALUE) return;
206
207     /* output of startup info (Ansi) */
208     GetStartupInfoA(&siA);
209     childPrintf(hFile,
210                 "[StartupInfoA]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
211                 "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
212                 "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
213                 "dwFlags=%lu\nwShowWindow=%u\n"
214                 "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
215                 siA.cb, encodeA(siA.lpDesktop), encodeA(siA.lpTitle),
216                 siA.dwX, siA.dwY, siA.dwXSize, siA.dwYSize,
217                 siA.dwXCountChars, siA.dwYCountChars, siA.dwFillAttribute,
218                 siA.dwFlags, siA.wShowWindow,
219                 (DWORD)siA.hStdInput, (DWORD)siA.hStdOutput, (DWORD)siA.hStdError);
220
221     /* since GetStartupInfoW is only implemented in win2k,
222      * zero out before calling so we can notice the difference
223      */
224     memset(&siW, 0, sizeof(siW));
225     GetStartupInfoW(&siW);
226     childPrintf(hFile,
227                 "[StartupInfoW]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
228                 "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
229                 "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
230                 "dwFlags=%lu\nwShowWindow=%u\n"
231                 "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
232                 siW.cb, encodeW(siW.lpDesktop), encodeW(siW.lpTitle),
233                 siW.dwX, siW.dwY, siW.dwXSize, siW.dwYSize,
234                 siW.dwXCountChars, siW.dwYCountChars, siW.dwFillAttribute,
235                 siW.dwFlags, siW.wShowWindow,
236                 (DWORD)siW.hStdInput, (DWORD)siW.hStdOutput, (DWORD)siW.hStdError);
237
238     /* Arguments */
239     childPrintf(hFile, "[Arguments]\nargcA=%d\n", myARGC);
240     for (i = 0; i < myARGC; i++)
241     {
242         childPrintf(hFile, "argvA%d=%s\n", i, encodeA(myARGV[i]));
243     }
244     childPrintf(hFile, "CommandLineA=%s\n", encodeA(GetCommandLineA()));
245
246 #if 0
247     int                 argcW;
248     WCHAR**             argvW;
249
250     /* this is part of shell32... and should be tested there */
251     argvW = CommandLineToArgvW(GetCommandLineW(), &argcW);
252     for (i = 0; i < argcW; i++)
253     {
254         childPrintf(hFile, "argvW%d=%s\n", i, encodeW(argvW[i]));
255     }
256 #endif
257     childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW()));
258
259     /* output of environment (Ansi) */
260     ptrA = GetEnvironmentStringsA();
261     if (ptrA)
262     {
263         char    env_var[MAX_LISTED_ENV_VAR];
264
265         childPrintf(hFile, "[EnvironmentA]\n");
266         i = 0;
267         while (*ptrA)
268         {
269             strncpy(env_var, ptrA, MAX_LISTED_ENV_VAR - 1);
270             env_var[MAX_LISTED_ENV_VAR - 1] = '\0';
271             childPrintf(hFile, "env%d=%s\n", i, encodeA(env_var));
272             i++;
273             ptrA += strlen(ptrA) + 1;
274         }
275         childPrintf(hFile, "len=%d\n\n", i);
276     }
277
278     /* output of environment (Unicode) */
279     ptrW = GetEnvironmentStringsW();
280     if (ptrW)
281     {
282         WCHAR   env_var[MAX_LISTED_ENV_VAR];
283
284         childPrintf(hFile, "[EnvironmentW]\n");
285         i = 0;
286         while (*ptrW)
287         {
288             lstrcpynW(env_var, ptrW, MAX_LISTED_ENV_VAR - 1);
289             env_var[MAX_LISTED_ENV_VAR - 1] = '\0';
290             childPrintf(hFile, "env%d=%s\n", i, encodeW(ptrW));
291             i++;
292             ptrW += lstrlenW(ptrW) + 1;
293         }
294         childPrintf(hFile, "len=%d\n\n", i);
295     }
296
297     childPrintf(hFile, "[Misc]\n");
298     if (GetCurrentDirectoryA(sizeof(bufA), bufA))
299         childPrintf(hFile, "CurrDirA=%s\n", encodeA(bufA));
300     if (GetCurrentDirectoryW(sizeof(bufW) / sizeof(bufW[0]), bufW))
301         childPrintf(hFile, "CurrDirW=%s\n", encodeW(bufW));
302     childPrintf(hFile, "\n");
303
304     if (option && strcmp(option, "console") == 0)
305     {
306         CONSOLE_SCREEN_BUFFER_INFO      sbi;
307         HANDLE hConIn  = GetStdHandle(STD_INPUT_HANDLE);
308         HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
309         DWORD modeIn, modeOut;
310
311         childPrintf(hFile, "[Console]\n");
312         if (GetConsoleScreenBufferInfo(hConOut, &sbi))
313         {
314             childPrintf(hFile, "SizeX=%d\nSizeY=%d\nCursorX=%d\nCursorY=%d\nAttributes=%d\n",
315                         sbi.dwSize.X, sbi.dwSize.Y, sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y, sbi.wAttributes);
316             childPrintf(hFile, "winLeft=%d\nwinTop=%d\nwinRight=%d\nwinBottom=%d\n",
317                         sbi.srWindow.Left, sbi.srWindow.Top, sbi.srWindow.Right, sbi.srWindow.Bottom);
318             childPrintf(hFile, "maxWinWidth=%d\nmaxWinHeight=%d\n",
319                         sbi.dwMaximumWindowSize.X, sbi.dwMaximumWindowSize.Y);
320         }
321         childPrintf(hFile, "InputCP=%d\nOutputCP=%d\n",
322                     GetConsoleCP(), GetConsoleOutputCP());
323         if (GetConsoleMode(hConIn, &modeIn))
324             childPrintf(hFile, "InputMode=%ld\n", modeIn);
325         if (GetConsoleMode(hConOut, &modeOut))
326             childPrintf(hFile, "OutputMode=%ld\n", modeOut);
327
328         /* now that we have written all relevant information, let's change it */
329         ok(SetConsoleCP(1252), "Setting CP");
330         ok(SetConsoleOutputCP(1252), "Setting SB CP");
331         ok(SetConsoleMode(hConIn, modeIn ^ 1), "Setting mode (%ld)", GetLastError());
332         ok(SetConsoleMode(hConOut, modeOut ^ 1), "Setting mode (%ld)", GetLastError());
333         sbi.dwCursorPosition.X ^= 1;
334         sbi.dwCursorPosition.Y ^= 1;
335         ok(SetConsoleCursorPosition(hConOut, sbi.dwCursorPosition), "Setting cursor position (%ld)", GetLastError());
336     }
337     if (option && strcmp(option, "stdhandle") == 0)
338     {
339         HANDLE hStdIn  = GetStdHandle(STD_INPUT_HANDLE);
340         HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
341
342         if (hStdIn != INVALID_HANDLE_VALUE || hStdOut != INVALID_HANDLE_VALUE)
343         {
344             char buf[1024];
345             DWORD r, w;
346
347             ok(ReadFile(hStdIn, buf, sizeof(buf), &r, NULL) && r > 0, "Reading message from input pipe");
348             childPrintf(hFile, "[StdHandle]\nmsg=%s\n\n", encodeA(buf));
349             ok(WriteFile(hStdOut, buf, r, &w, NULL) && w == r, "Writting message to output pipe");
350         }
351     }
352
353     if (option && strcmp(option, "exit_code") == 0)
354     {
355         childPrintf(hFile, "[ExitCode]\nvalue=%d\n\n", 123);
356         CloseHandle(hFile);
357         ExitProcess(123);
358     }
359
360     CloseHandle(hFile);
361 }
362
363 static char* getChildString(const char* sect, const char* key)
364 {
365     char        buf[1024];
366     char*       ret;
367
368     GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), resfile);
369     if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
370     assert(!(strlen(buf) & 1));
371     ret = decodeA(buf);
372     return ret;
373 }
374
375 /* FIXME: this may be moved to the wtmain.c file, because it may be needed by
376  * others... (windows uses stricmp while Un*x uses strcasecmp...)
377  */
378 static int wtstrcasecmp(const char* p1, const char* p2)
379 {
380     char c1, c2;
381
382     c1 = c2 = '@';
383     while (c1 == c2 && c1)
384     {
385         c1 = *p1++; c2 = *p2++;
386         if (c1 != c2)
387         {
388             c1 = toupper(c1); c2 = toupper(c2);
389         }
390     }
391     return c1 - c2;
392 }
393
394 static int strCmp(const char* s1, const char* s2, BOOL sensitive)
395 {
396     if (!s1 && !s2) return 0;
397     if (!s2) return -1;
398     if (!s1) return 1;
399     return (sensitive) ? strcmp(s1, s2) : wtstrcasecmp(s1, s2);
400 }
401
402 #define okChildString(sect, key, expect) \
403     do { \
404         char* result = getChildString((sect), (key)); \
405         ok(strCmp(result, expect, 1) == 0, "%s:%s expected '%s', got '%s'", (sect), (key), (expect)?(expect):"(null)", result); \
406     } while (0)
407
408 #define okChildIString(sect, key, expect) \
409     do { \
410         char* result = getChildString(sect, key); \
411         ok(strCmp(result, expect, 0) == 0, "%s:%s expected '%s', got '%s'", sect, key, expect, result); \
412     } while (0)
413
414 /* using !expect insures that the test will fail if the sect/key isn't present
415  * in result file
416  */
417 #define okChildInt(sect, key, expect) \
418     do { \
419         UINT result = GetPrivateProfileIntA((sect), (key), !(expect), resfile); \
420         ok(result == expect, "%s:%s expected %d, but got %d", (sect), (key), (int)(expect), result); \
421    } while (0)
422
423 static void test_Startup(void)
424 {
425     char                buffer[MAX_PATH];
426     PROCESS_INFORMATION info;
427     STARTUPINFOA        startup,si;
428
429     /* let's start simplistic */
430     memset(&startup, 0, sizeof(startup));
431     startup.cb = sizeof(startup);
432     startup.dwFlags = STARTF_USESHOWWINDOW;
433     startup.wShowWindow = SW_SHOWNORMAL;
434
435     get_file_name(resfile);
436     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
437     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
438     /* wait for child to terminate */
439     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
440     /* child process has changed result file, so let profile functions know about it */
441     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
442
443     GetStartupInfoA(&si);
444     okChildInt("StartupInfoA", "cb", startup.cb);
445     okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
446     okChildString("StartupInfoA", "lpTitle", si.lpTitle);
447     okChildInt("StartupInfoA", "dwX", startup.dwX);
448     okChildInt("StartupInfoA", "dwY", startup.dwY);
449     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
450     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
451     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
452     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
453     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
454     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
455     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
456     release_memory();
457     assert(DeleteFileA(resfile) != 0);
458
459     /* not so simplistic now */
460     memset(&startup, 0, sizeof(startup));
461     startup.cb = sizeof(startup);
462     startup.dwFlags = STARTF_USESHOWWINDOW;
463     startup.wShowWindow = SW_SHOWNORMAL;
464     startup.lpTitle = "I'm the title string";
465     startup.lpDesktop = "I'm the desktop string";
466     startup.dwXCountChars = 0x12121212;
467     startup.dwYCountChars = 0x23232323;
468     startup.dwX = 0x34343434;
469     startup.dwY = 0x45454545;
470     startup.dwXSize = 0x56565656;
471     startup.dwYSize = 0x67676767;
472     startup.dwFillAttribute = 0xA55A;
473
474     get_file_name(resfile);
475     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
476     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
477     /* wait for child to terminate */
478     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
479     /* child process has changed result file, so let profile functions know about it */
480     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
481
482     okChildInt("StartupInfoA", "cb", startup.cb);
483     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
484     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
485     okChildInt("StartupInfoA", "dwX", startup.dwX);
486     okChildInt("StartupInfoA", "dwY", startup.dwY);
487     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
488     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
489     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
490     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
491     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
492     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
493     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
494     release_memory();
495     assert(DeleteFileA(resfile) != 0);
496
497     /* not so simplistic now */
498     memset(&startup, 0, sizeof(startup));
499     startup.cb = sizeof(startup);
500     startup.dwFlags = STARTF_USESHOWWINDOW;
501     startup.wShowWindow = SW_SHOWNORMAL;
502     startup.lpTitle = "I'm the title string";
503     startup.lpDesktop = NULL;
504     startup.dwXCountChars = 0x12121212;
505     startup.dwYCountChars = 0x23232323;
506     startup.dwX = 0x34343434;
507     startup.dwY = 0x45454545;
508     startup.dwXSize = 0x56565656;
509     startup.dwYSize = 0x67676767;
510     startup.dwFillAttribute = 0xA55A;
511
512     get_file_name(resfile);
513     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
514     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
515     /* wait for child to terminate */
516     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
517     /* child process has changed result file, so let profile functions know about it */
518     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
519
520     okChildInt("StartupInfoA", "cb", startup.cb);
521     okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
522     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
523     okChildInt("StartupInfoA", "dwX", startup.dwX);
524     okChildInt("StartupInfoA", "dwY", startup.dwY);
525     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
526     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
527     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
528     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
529     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
530     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
531     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
532     release_memory();
533     assert(DeleteFileA(resfile) != 0);
534
535     /* not so simplistic now */
536     memset(&startup, 0, sizeof(startup));
537     startup.cb = sizeof(startup);
538     startup.dwFlags = STARTF_USESHOWWINDOW;
539     startup.wShowWindow = SW_SHOWNORMAL;
540     startup.lpTitle = "I'm the title string";
541     startup.lpDesktop = "";
542     startup.dwXCountChars = 0x12121212;
543     startup.dwYCountChars = 0x23232323;
544     startup.dwX = 0x34343434;
545     startup.dwY = 0x45454545;
546     startup.dwXSize = 0x56565656;
547     startup.dwYSize = 0x67676767;
548     startup.dwFillAttribute = 0xA55A;
549
550     get_file_name(resfile);
551     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
552     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
553     /* wait for child to terminate */
554     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
555     /* child process has changed result file, so let profile functions know about it */
556     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
557
558     okChildInt("StartupInfoA", "cb", startup.cb);
559     todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
560     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
561     okChildInt("StartupInfoA", "dwX", startup.dwX);
562     okChildInt("StartupInfoA", "dwY", startup.dwY);
563     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
564     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
565     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
566     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
567     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
568     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
569     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
570     release_memory();
571     assert(DeleteFileA(resfile) != 0);
572
573     /* not so simplistic now */
574     memset(&startup, 0, sizeof(startup));
575     startup.cb = sizeof(startup);
576     startup.dwFlags = STARTF_USESHOWWINDOW;
577     startup.wShowWindow = SW_SHOWNORMAL;
578     startup.lpTitle = NULL;
579     startup.lpDesktop = "I'm the desktop string";
580     startup.dwXCountChars = 0x12121212;
581     startup.dwYCountChars = 0x23232323;
582     startup.dwX = 0x34343434;
583     startup.dwY = 0x45454545;
584     startup.dwXSize = 0x56565656;
585     startup.dwYSize = 0x67676767;
586     startup.dwFillAttribute = 0xA55A;
587
588     get_file_name(resfile);
589     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
590     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
591     /* wait for child to terminate */
592     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
593     /* child process has changed result file, so let profile functions know about it */
594     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
595
596     okChildInt("StartupInfoA", "cb", startup.cb);
597     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
598     okChildString("StartupInfoA", "lpTitle", si.lpTitle);
599     okChildInt("StartupInfoA", "dwX", startup.dwX);
600     okChildInt("StartupInfoA", "dwY", startup.dwY);
601     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
602     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
603     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
604     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
605     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
606     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
607     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
608     release_memory();
609     assert(DeleteFileA(resfile) != 0);
610
611     /* not so simplistic now */
612     memset(&startup, 0, sizeof(startup));
613     startup.cb = sizeof(startup);
614     startup.dwFlags = STARTF_USESHOWWINDOW;
615     startup.wShowWindow = SW_SHOWNORMAL;
616     startup.lpTitle = "";
617     startup.lpDesktop = "I'm the desktop string";
618     startup.dwXCountChars = 0x12121212;
619     startup.dwYCountChars = 0x23232323;
620     startup.dwX = 0x34343434;
621     startup.dwY = 0x45454545;
622     startup.dwXSize = 0x56565656;
623     startup.dwYSize = 0x67676767;
624     startup.dwFillAttribute = 0xA55A;
625
626     get_file_name(resfile);
627     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
628     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
629     /* wait for child to terminate */
630     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
631     /* child process has changed result file, so let profile functions know about it */
632     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
633
634     okChildInt("StartupInfoA", "cb", startup.cb);
635     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
636     todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
637     okChildInt("StartupInfoA", "dwX", startup.dwX);
638     okChildInt("StartupInfoA", "dwY", startup.dwY);
639     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
640     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
641     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
642     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
643     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
644     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
645     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
646     release_memory();
647     assert(DeleteFileA(resfile) != 0);
648
649     /* not so simplistic now */
650     memset(&startup, 0, sizeof(startup));
651     startup.cb = sizeof(startup);
652     startup.dwFlags = STARTF_USESHOWWINDOW;
653     startup.wShowWindow = SW_SHOWNORMAL;
654     startup.lpTitle = "";
655     startup.lpDesktop = "";
656     startup.dwXCountChars = 0x12121212;
657     startup.dwYCountChars = 0x23232323;
658     startup.dwX = 0x34343434;
659     startup.dwY = 0x45454545;
660     startup.dwXSize = 0x56565656;
661     startup.dwYSize = 0x67676767;
662     startup.dwFillAttribute = 0xA55A;
663
664     get_file_name(resfile);
665     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
666     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
667     /* wait for child to terminate */
668     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
669     /* child process has changed result file, so let profile functions know about it */
670     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
671
672     okChildInt("StartupInfoA", "cb", startup.cb);
673     todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
674     todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
675     okChildInt("StartupInfoA", "dwX", startup.dwX);
676     okChildInt("StartupInfoA", "dwY", startup.dwY);
677     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
678     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
679     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
680     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
681     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
682     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
683     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
684     release_memory();
685     assert(DeleteFileA(resfile) != 0);
686
687     /* TODO: test for A/W and W/A and W/W */
688 }
689
690 static void test_CommandLine(void)
691 {
692     char                buffer[MAX_PATH];
693     PROCESS_INFORMATION info;
694     STARTUPINFOA        startup;
695
696     memset(&startup, 0, sizeof(startup));
697     startup.cb = sizeof(startup);
698     startup.dwFlags = STARTF_USESHOWWINDOW;
699     startup.wShowWindow = SW_SHOWNORMAL;
700
701     /* the basics */
702     get_file_name(resfile);
703     sprintf(buffer, "%s tests/process.c %s \"C:\\Program Files\\my nice app.exe\"", selfname, resfile);
704     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
705     /* wait for child to terminate */
706     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
707     /* child process has changed result file, so let profile functions know about it */
708     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
709
710     okChildInt("Arguments", "argcA", 4);
711     okChildString("Arguments", "argvA3", "C:\\Program Files\\my nice app.exe");
712     okChildString("Arguments", "argvA4", NULL);
713     okChildString("Arguments", "CommandLineA", buffer);
714     release_memory();
715     assert(DeleteFileA(resfile) != 0);
716
717     memset(&startup, 0, sizeof(startup));
718     startup.cb = sizeof(startup);
719     startup.dwFlags = STARTF_USESHOWWINDOW;
720     startup.wShowWindow = SW_SHOWNORMAL;
721
722     /* from Frangois */
723     get_file_name(resfile);
724     sprintf(buffer, "%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
725     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
726     /* wait for child to terminate */
727     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
728     /* child process has changed result file, so let profile functions know about it */
729     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
730
731     okChildInt("Arguments", "argcA", 6);
732     okChildString("Arguments", "argvA3", "a\"b\\");
733     okChildString("Arguments", "argvA4", "c\"");
734     okChildString("Arguments", "argvA5", "d");
735     okChildString("Arguments", "argvA6", NULL);
736     okChildString("Arguments", "CommandLineA", buffer);
737     release_memory();
738     assert(DeleteFileA(resfile) != 0);
739 }
740
741 static void test_Directory(void)
742 {
743     char                buffer[MAX_PATH];
744     PROCESS_INFORMATION info;
745     STARTUPINFOA        startup;
746     char windir[MAX_PATH];
747
748     memset(&startup, 0, sizeof(startup));
749     startup.cb = sizeof(startup);
750     startup.dwFlags = STARTF_USESHOWWINDOW;
751     startup.wShowWindow = SW_SHOWNORMAL;
752
753     /* the basics */
754     get_file_name(resfile);
755     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
756     GetWindowsDirectoryA( windir, sizeof(windir) );
757     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, windir, &startup, &info), "CreateProcess");
758     /* wait for child to terminate */
759     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
760     /* child process has changed result file, so let profile functions know about it */
761     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
762
763     okChildIString("Misc", "CurrDirA", windir);
764     release_memory();
765     assert(DeleteFileA(resfile) != 0);
766 }
767
768 static BOOL is_str_env_drive_dir(const char* str)
769 {
770     return str[0] == '=' && str[1] >= 'A' && str[1] <= 'Z' && str[2] == ':' &&
771         str[3] == '=' && str[4] == str[1];
772 }
773
774 /* compared expected child's environment (in gesA) from actual
775  * environment our child got
776  */
777 static void cmpEnvironment(const char* gesA)
778 {
779     int                 i, clen;
780     const char*         ptrA;
781     char*               res;
782     char                key[32];
783     BOOL                found;
784
785     clen = GetPrivateProfileIntA("EnvironmentA", "len", 0, resfile);
786     
787     /* now look each parent env in child */
788     if ((ptrA = gesA) != NULL)
789     {
790         while (*ptrA)
791         {
792             for (i = 0; i < clen; i++)
793             {
794                 sprintf(key, "env%d", i);
795                 res = getChildString("EnvironmentA", key);
796                 if (strncmp(ptrA, res, MAX_LISTED_ENV_VAR - 1) == 0)
797                     break;
798             }
799             found = i < clen;
800             ok(found, "Parent-env string %s isn't in child process", ptrA);
801             
802             ptrA += strlen(ptrA) + 1;
803             release_memory();
804         }
805     }
806     /* and each child env in parent */
807     for (i = 0; i < clen; i++)
808     {
809         sprintf(key, "env%d", i);
810         res = getChildString("EnvironmentA", key);
811         if ((ptrA = gesA) != NULL)
812         {
813             while (*ptrA)
814             {
815                 if (strncmp(res, ptrA, MAX_LISTED_ENV_VAR - 1) == 0)
816                     break;
817                 ptrA += strlen(ptrA) + 1;
818             }
819             if (!*ptrA) ptrA = NULL;
820         }
821
822         if (!is_str_env_drive_dir(res))
823         {
824             found = ptrA != NULL;
825             ok(found, "Child-env string %s isn't in parent process", res);
826         }
827         /* else => should also test we get the right per drive default directory here... */
828     }
829 }
830
831 static void test_Environment(void)
832 {
833     char                buffer[MAX_PATH];
834     PROCESS_INFORMATION info;
835     STARTUPINFOA        startup;
836     char                child_env[4096];
837     char*               ptr;
838     char*               env;
839
840     memset(&startup, 0, sizeof(startup));
841     startup.cb = sizeof(startup);
842     startup.dwFlags = STARTF_USESHOWWINDOW;
843     startup.wShowWindow = SW_SHOWNORMAL;
844
845     /* the basics */
846     get_file_name(resfile);
847     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
848     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess");
849     /* wait for child to terminate */
850     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
851     /* child process has changed result file, so let profile functions know about it */
852     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
853     
854     cmpEnvironment(GetEnvironmentStringsA());
855     release_memory();
856     assert(DeleteFileA(resfile) != 0);
857
858     memset(&startup, 0, sizeof(startup));
859     startup.cb = sizeof(startup);
860     startup.dwFlags = STARTF_USESHOWWINDOW;
861     startup.wShowWindow = SW_SHOWNORMAL;
862
863     /* the basics */
864     get_file_name(resfile);
865     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
866     ptr = child_env;
867     sprintf(ptr, "=%c:=%s", 'C', "C:\\FOO\\BAR");
868     ptr += strlen(ptr) + 1;
869     strcpy(ptr, "PATH=C:\\WINDOWS;C:\\WINDOWS\\SYSTEM;C:\\MY\\OWN\\DIR");
870     ptr += strlen(ptr) + 1;
871     strcpy(ptr, "FOO=BAR");
872     ptr += strlen(ptr) + 1;
873     strcpy(ptr, "BAR=FOOBAR");
874     ptr += strlen(ptr) + 1;
875     /* copy all existing variables except:
876      * - WINELOADER
877      * - PATH (already set above)
878      * - the directory definitions (=[A-Z]:=)
879      */
880     for (env = GetEnvironmentStringsA(); *env; env += strlen(env) + 1)
881     {
882         if (strncmp(env, "PATH=", 5) != 0 &&
883             strncmp(env, "WINELOADER=", 11) != 0 &&
884             !is_str_env_drive_dir(env))
885         {
886             strcpy(ptr, env);
887             ptr += strlen(ptr) + 1;
888         }
889     }
890     *ptr = '\0';
891     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, child_env, NULL, &startup, &info), "CreateProcess");
892     /* wait for child to terminate */
893     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
894     /* child process has changed result file, so let profile functions know about it */
895     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
896     
897     cmpEnvironment(child_env);
898
899     release_memory();
900     assert(DeleteFileA(resfile) != 0);
901 }
902
903 static  void    test_SuspendFlag(void)
904 {
905     char                buffer[MAX_PATH];
906     PROCESS_INFORMATION info;
907     STARTUPINFOA        startup;
908     DWORD               exit_status;
909
910     /* let's start simplistic */
911     memset(&startup, 0, sizeof(startup));
912     startup.cb = sizeof(startup);
913     startup.dwFlags = STARTF_USESHOWWINDOW;
914     startup.wShowWindow = SW_SHOWNORMAL;
915
916     get_file_name(resfile);
917     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
918     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup, &info), "CreateProcess");
919
920     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running");
921     Sleep(8000);
922     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running");
923     ok(ResumeThread(info.hThread) == 1, "Resuming thread");
924
925     /* wait for child to terminate */
926     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
927     /* child process has changed result file, so let profile functions know about it */
928     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
929
930     okChildInt("StartupInfoA", "cb", startup.cb);
931     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
932     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
933     okChildInt("StartupInfoA", "dwX", startup.dwX);
934     okChildInt("StartupInfoA", "dwY", startup.dwY);
935     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
936     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
937     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
938     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
939     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
940     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
941     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
942     release_memory();
943     assert(DeleteFileA(resfile) != 0);
944 }
945
946 static  void    test_DebuggingFlag(void)
947 {
948     char                buffer[MAX_PATH];
949     PROCESS_INFORMATION info;
950     STARTUPINFOA        startup;
951     DEBUG_EVENT         de;
952     unsigned            dbg = 0;
953
954     /* let's start simplistic */
955     memset(&startup, 0, sizeof(startup));
956     startup.cb = sizeof(startup);
957     startup.dwFlags = STARTF_USESHOWWINDOW;
958     startup.wShowWindow = SW_SHOWNORMAL;
959
960     get_file_name(resfile);
961     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
962     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &startup, &info), "CreateProcess");
963
964     /* get all startup events up to the entry point break exception */
965     do 
966     {
967         ok(WaitForDebugEvent(&de, INFINITE), "reading debug event");
968         ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
969         if (de.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) dbg++;
970     } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
971
972     ok(dbg, "I have seen a debug event");
973     /* wait for child to terminate */
974     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
975     /* child process has changed result file, so let profile functions know about it */
976     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
977
978     okChildInt("StartupInfoA", "cb", startup.cb);
979     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
980     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
981     okChildInt("StartupInfoA", "dwX", startup.dwX);
982     okChildInt("StartupInfoA", "dwY", startup.dwY);
983     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
984     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
985     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
986     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
987     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
988     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
989     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
990     release_memory();
991     assert(DeleteFileA(resfile) != 0);
992 }
993
994 static void test_Console(void)
995 {
996     char                buffer[MAX_PATH];
997     PROCESS_INFORMATION info;
998     STARTUPINFOA        startup;
999     SECURITY_ATTRIBUTES sa;
1000     CONSOLE_SCREEN_BUFFER_INFO  sbi, sbiC;
1001     DWORD               modeIn, modeOut, modeInC, modeOutC;
1002     DWORD               cpIn, cpOut, cpInC, cpOutC;
1003     DWORD               w;
1004     HANDLE              hChildIn, hChildInInh, hChildOut, hChildOutInh, hParentIn, hParentOut;
1005     const char*         msg = "This is a std-handle inheritance test.";
1006     unsigned            msg_len;
1007
1008     memset(&startup, 0, sizeof(startup));
1009     startup.cb = sizeof(startup);
1010     startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
1011     startup.wShowWindow = SW_SHOWNORMAL;
1012
1013     sa.nLength = sizeof(sa);
1014     sa.lpSecurityDescriptor = NULL;
1015     sa.bInheritHandle = TRUE;
1016
1017     startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1018     startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1019
1020     /* first, we need to be sure we're attached to a console */
1021     if (startup.hStdInput == INVALID_HANDLE_VALUE || startup.hStdOutput == INVALID_HANDLE_VALUE)
1022     {
1023         /* we're not attached to a console, let's do it */
1024         AllocConsole();
1025         startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1026         startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1027     }
1028     /* now verify everything's ok */
1029     ok(startup.hStdInput != INVALID_HANDLE_VALUE, "Opening ConIn");
1030     ok(startup.hStdOutput != INVALID_HANDLE_VALUE, "Opening ConOut");
1031     startup.hStdError = startup.hStdOutput;
1032
1033     ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbi), "Getting sb info");
1034     ok(GetConsoleMode(startup.hStdInput, &modeIn) && 
1035        GetConsoleMode(startup.hStdOutput, &modeOut), "Getting console modes");
1036     cpIn = GetConsoleCP();
1037     cpOut = GetConsoleOutputCP();
1038
1039     get_file_name(resfile);
1040     sprintf(buffer, "%s tests/process.c %s console", selfname, resfile);
1041     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess");
1042
1043     /* wait for child to terminate */
1044     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
1045     /* child process has changed result file, so let profile functions know about it */
1046     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1047
1048     /* now get the modification the child has made, and resets parents expected values */
1049     ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbiC), "Getting sb info");
1050     ok(GetConsoleMode(startup.hStdInput, &modeInC) && 
1051        GetConsoleMode(startup.hStdOutput, &modeOutC), "Getting console modes");
1052
1053     SetConsoleMode(startup.hStdInput, modeIn);
1054     SetConsoleMode(startup.hStdOutput, modeOut);
1055
1056     cpInC = GetConsoleCP();
1057     cpOutC = GetConsoleOutputCP();
1058     SetConsoleCP(cpIn);
1059     SetConsoleOutputCP(cpOut);
1060
1061     okChildInt("StartupInfoA", "cb", startup.cb);
1062     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
1063     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
1064     okChildInt("StartupInfoA", "dwX", startup.dwX);
1065     okChildInt("StartupInfoA", "dwY", startup.dwY);
1066     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
1067     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
1068     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
1069     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
1070     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
1071     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
1072     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
1073
1074     /* check child correctly inherited the console */
1075     okChildInt("StartupInfoA", "hStdInput", (DWORD)startup.hStdInput);
1076     okChildInt("StartupInfoA", "hStdOutput", (DWORD)startup.hStdOutput);
1077     okChildInt("StartupInfoA", "hStdError", (DWORD)startup.hStdError);
1078     okChildInt("Console", "SizeX", sbi.dwSize.X);
1079     okChildInt("Console", "SizeY", sbi.dwSize.Y);
1080     okChildInt("Console", "CursorX", sbi.dwCursorPosition.X);
1081     okChildInt("Console", "CursorY", sbi.dwCursorPosition.Y);
1082     okChildInt("Console", "Attributes", sbi.wAttributes);
1083     okChildInt("Console", "winLeft", sbi.srWindow.Left);
1084     okChildInt("Console", "winTop", sbi.srWindow.Top);
1085     okChildInt("Console", "winRight", sbi.srWindow.Right);
1086     okChildInt("Console", "winBottom", sbi.srWindow.Bottom);
1087     okChildInt("Console", "maxWinWidth", sbi.dwMaximumWindowSize.X);
1088     okChildInt("Console", "maxWinHeight", sbi.dwMaximumWindowSize.Y);
1089     okChildInt("Console", "InputCP", cpIn);
1090     okChildInt("Console", "OutputCP", cpOut);
1091     okChildInt("Console", "InputMode", modeIn);
1092     okChildInt("Console", "OutputMode", modeOut);
1093
1094     todo_wine ok(cpInC == 1252, "Wrong console CP (expected 1252 got %ld/%ld)", cpInC, cpIn);
1095     todo_wine ok(cpOutC == 1252, "Wrong console-SB CP (expected 1252 got %ld/%ld)", cpOutC, cpOut);
1096     ok(modeInC == (modeIn ^ 1), "Wrong console mode");
1097     ok(modeOutC == (modeOut ^ 1), "Wrong console-SB mode");
1098     ok(sbiC.dwCursorPosition.X == (sbi.dwCursorPosition.X ^ 1), "Wrong cursor position");
1099     ok(sbiC.dwCursorPosition.Y == (sbi.dwCursorPosition.Y ^ 1), "Wrong cursor position");
1100
1101     release_memory();
1102     assert(DeleteFileA(resfile) != 0);
1103
1104     ok(CreatePipe(&hParentIn, &hChildOut, NULL, 0), "Creating parent-input pipe");
1105     ok(DuplicateHandle(GetCurrentProcess(), hChildOut, GetCurrentProcess(), 
1106                        &hChildOutInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
1107        "Duplicating as inheritable child-output pipe");
1108     CloseHandle(hChildOut);
1109  
1110     ok(CreatePipe(&hChildIn, &hParentOut, NULL, 0), "Creating parent-output pipe");
1111     ok(DuplicateHandle(GetCurrentProcess(), hChildIn, GetCurrentProcess(), 
1112                        &hChildInInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
1113        "Duplicating as inheritable child-input pipe");
1114     CloseHandle(hChildIn); 
1115     
1116     memset(&startup, 0, sizeof(startup));
1117     startup.cb = sizeof(startup);
1118     startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
1119     startup.wShowWindow = SW_SHOWNORMAL;
1120     startup.hStdInput = hChildInInh;
1121     startup.hStdOutput = hChildOutInh;
1122     startup.hStdError = hChildOutInh;
1123
1124     get_file_name(resfile);
1125     sprintf(buffer, "%s tests/process.c %s stdhandle", selfname, resfile);
1126     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, &info), "CreateProcess");
1127     ok(CloseHandle(hChildInInh), "Closing handle");
1128     ok(CloseHandle(hChildOutInh), "Closing handle");
1129
1130     msg_len = strlen(msg) + 1;
1131     ok(WriteFile(hParentOut, msg, msg_len, &w, NULL), "Writting to child");
1132     ok(w == msg_len, "Should have written %u bytes, actually wrote %lu", msg_len, w);
1133     memset(buffer, 0, sizeof(buffer));
1134     ok(ReadFile(hParentIn, buffer, sizeof(buffer), &w, NULL), "Reading from child");
1135     ok(strcmp(buffer, msg) == 0, "Should have received '%s'", msg);
1136
1137     /* wait for child to terminate */
1138     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
1139     /* child process has changed result file, so let profile functions know about it */
1140     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1141
1142     okChildString("StdHandle", "msg", msg);
1143
1144     release_memory();
1145     assert(DeleteFileA(resfile) != 0);
1146 }
1147
1148 static  void    test_ExitCode(void)
1149 {
1150     char                buffer[MAX_PATH];
1151     PROCESS_INFORMATION info;
1152     STARTUPINFOA        startup;
1153     DWORD               code;
1154
1155     /* let's start simplistic */
1156     memset(&startup, 0, sizeof(startup));
1157     startup.cb = sizeof(startup);
1158     startup.dwFlags = STARTF_USESHOWWINDOW;
1159     startup.wShowWindow = SW_SHOWNORMAL;
1160
1161     get_file_name(resfile);
1162     sprintf(buffer, "%s tests/process.c %s exit_code", selfname, resfile);
1163     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess");
1164
1165     /* wait for child to terminate */
1166     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination");
1167     /* child process has changed result file, so let profile functions know about it */
1168     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1169
1170     ok(GetExitCodeProcess(info.hProcess, &code), "Getting exit code");
1171     okChildInt("ExitCode", "value", code);
1172
1173     release_memory();
1174     assert(DeleteFileA(resfile) != 0);
1175 }
1176
1177 START_TEST(process)
1178 {
1179     int b = init();
1180     ok(b, "Basic init of CreateProcess test");
1181     if (!b) return;
1182
1183     if (myARGC >= 3)
1184     {
1185         doChild(myARGV[2], (myARGC == 3) ? NULL : myARGV[3]);
1186         return;
1187     }
1188     test_Startup();
1189     test_CommandLine();
1190     test_Directory();
1191     test_Environment();
1192     test_SuspendFlag();
1193     test_DebuggingFlag();
1194     test_Console();
1195     test_ExitCode();
1196     /* things that can be tested:
1197      *  lookup:         check the way program to be executed is searched
1198      *  handles:        check the handle inheritance stuff (+sec options)
1199      *  console:        check if console creation parameters work
1200      */
1201 }