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