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