kernel32/tests: Skip some tests on win9x.
[wine] / dlls / kernel32 / tests / process.c
1 /*
2  * Unit test suite for CreateProcess function.
3  *
4  * Copyright 2002 Eric Pouech
5  * Copyright 2006 Dmitry Timoshkov
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include <assert.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #include "wine/test.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "wincon.h"
32 #include "winnls.h"
33
34 static HINSTANCE hkernel32;
35 static LPVOID (WINAPI *pVirtualAllocEx)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
36 static BOOL   (WINAPI *pVirtualFreeEx)(HANDLE, LPVOID, SIZE_T, DWORD);
37
38 /* ############################### */
39 static char     base[MAX_PATH];
40 static char     selfname[MAX_PATH];
41 static char*    exename;
42 static char     resfile[MAX_PATH];
43
44 static int      myARGC;
45 static char**   myARGV;
46
47 /* As some environment variables get very long on Unix, we only test for
48  * the first 127 bytes.
49  * Note that increasing this value past 256 may exceed the buffer size
50  * limitations of the *Profile functions (at least on Wine).
51  */
52 #define MAX_LISTED_ENV_VAR      128
53
54 /* ---------------- portable memory allocation thingie */
55
56 static char     memory[1024*256];
57 static char*    memory_index = memory;
58
59 static char*    grab_memory(size_t len)
60 {
61     char*       ret = memory_index;
62     /* align on dword */
63     len = (len + 3) & ~3;
64     memory_index += len;
65     assert(memory_index <= memory + sizeof(memory));
66     return ret;
67 }
68
69 static void     release_memory(void)
70 {
71     memory_index = memory;
72 }
73
74 /* ---------------- simplistic tool to encode/decode strings (to hide \ " ' and such) */
75
76 static const char* encodeA(const char* str)
77 {
78     char*       ptr;
79     size_t      len,i;
80
81     if (!str) return "";
82     len = strlen(str) + 1;
83     ptr = grab_memory(len * 2 + 1);
84     for (i = 0; i < len; i++)
85         sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
86     ptr[2 * len] = '\0';
87     return ptr;
88 }
89
90 static const char* encodeW(const WCHAR* str)
91 {
92     char*       ptr;
93     size_t      len,i;
94
95     if (!str) return "";
96     len = lstrlenW(str) + 1;
97     ptr = grab_memory(len * 4 + 1);
98     assert(ptr);
99     for (i = 0; i < len; i++)
100         sprintf(&ptr[i * 4], "%04x", (unsigned int)(unsigned short)str[i]);
101     ptr[4 * len] = '\0';
102     return ptr;
103 }
104
105 static unsigned decode_char(char c)
106 {
107     if (c >= '0' && c <= '9') return c - '0';
108     if (c >= 'a' && c <= 'f') return c - 'a' + 10;
109     assert(c >= 'A' && c <= 'F');
110     return c - 'A' + 10;
111 }
112
113 static char*    decodeA(const char* str)
114 {
115     char*       ptr;
116     size_t      len,i;
117
118     len = strlen(str) / 2;
119     if (!len--) return NULL;
120     ptr = grab_memory(len + 1);
121     for (i = 0; i < len; i++)
122         ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
123     ptr[len] = '\0';
124     return ptr;
125 }
126
127 #if 0
128 /* This will be needed to decode Unicode strings saved by the child process
129  * when we test Unicode functions.
130  */
131 static WCHAR*   decodeW(const char* str)
132 {
133     size_t      len;
134     WCHAR*      ptr;
135     int         i;
136
137     len = strlen(str) / 4;
138     if (!len--) return NULL;
139     ptr = (WCHAR*)grab_memory(len * 2 + 1);
140     for (i = 0; i < len; i++)
141         ptr[i] = (decode_char(str[4 * i]) << 12) |
142             (decode_char(str[4 * i + 1]) << 8) |
143             (decode_char(str[4 * i + 2]) << 4) |
144             (decode_char(str[4 * i + 3]) << 0);
145     ptr[len] = '\0';
146     return ptr;
147 }
148 #endif
149
150 /******************************************************************
151  *              init
152  *
153  * generates basic information like:
154  *      base:           absolute path to curr dir
155  *      selfname:       the way to reinvoke ourselves
156  *      exename:        executable without the path
157  * function-pointers, which are not implemented in all windows versions
158  */
159 static int     init(void)
160 {
161     char *p;
162
163     myARGC = winetest_get_mainargs( &myARGV );
164     if (!GetCurrentDirectoryA(sizeof(base), base)) return 0;
165     strcpy(selfname, myARGV[0]);
166
167     /* Strip the path of selfname */
168     if ((p = strrchr(selfname, '\\')) != NULL) exename = p + 1;
169     else exename = selfname;
170
171     if ((p = strrchr(exename, '/')) != NULL) exename = p + 1;
172
173     hkernel32 = GetModuleHandleA("kernel32");
174     pVirtualAllocEx = (void *) GetProcAddress(hkernel32, "VirtualAllocEx");
175     pVirtualFreeEx = (void *) GetProcAddress(hkernel32, "VirtualFreeEx");
176     return 1;
177 }
178
179 /******************************************************************
180  *              get_file_name
181  *
182  * generates an absolute file_name for temporary file
183  *
184  */
185 static void     get_file_name(char* buf)
186 {
187     char        path[MAX_PATH];
188
189     buf[0] = '\0';
190     GetTempPathA(sizeof(path), path);
191     GetTempFileNameA(path, "wt", 0, buf);
192 }
193
194 /******************************************************************
195  *              static void     childPrintf
196  *
197  */
198 static void     childPrintf(HANDLE h, const char* fmt, ...)
199 {
200     va_list     valist;
201     char        buffer[1024+4*MAX_LISTED_ENV_VAR];
202     DWORD       w;
203
204     va_start(valist, fmt);
205     vsprintf(buffer, fmt, valist);
206     va_end(valist);
207     WriteFile(h, buffer, strlen(buffer), &w, NULL);
208 }
209
210
211 /******************************************************************
212  *              doChild
213  *
214  * output most of the information in the child process
215  */
216 static void     doChild(const char* file, const char* option)
217 {
218     STARTUPINFOA        siA;
219     STARTUPINFOW        siW;
220     int                 i;
221     char*               ptrA;
222     WCHAR*              ptrW;
223     char                bufA[MAX_PATH];
224     WCHAR               bufW[MAX_PATH];
225     HANDLE              hFile = CreateFileA(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
226     BOOL ret;
227
228     if (hFile == INVALID_HANDLE_VALUE) return;
229
230     /* output of startup info (Ansi) */
231     GetStartupInfoA(&siA);
232     childPrintf(hFile,
233                 "[StartupInfoA]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
234                 "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
235                 "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
236                 "dwFlags=%lu\nwShowWindow=%u\n"
237                 "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
238                 siA.cb, encodeA(siA.lpDesktop), encodeA(siA.lpTitle),
239                 siA.dwX, siA.dwY, siA.dwXSize, siA.dwYSize,
240                 siA.dwXCountChars, siA.dwYCountChars, siA.dwFillAttribute,
241                 siA.dwFlags, siA.wShowWindow,
242                 (DWORD)siA.hStdInput, (DWORD)siA.hStdOutput, (DWORD)siA.hStdError);
243
244     /* since GetStartupInfoW is only implemented in win2k,
245      * zero out before calling so we can notice the difference
246      */
247     memset(&siW, 0, sizeof(siW));
248     GetStartupInfoW(&siW);
249     childPrintf(hFile,
250                 "[StartupInfoW]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
251                 "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
252                 "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
253                 "dwFlags=%lu\nwShowWindow=%u\n"
254                 "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
255                 siW.cb, encodeW(siW.lpDesktop), encodeW(siW.lpTitle),
256                 siW.dwX, siW.dwY, siW.dwXSize, siW.dwYSize,
257                 siW.dwXCountChars, siW.dwYCountChars, siW.dwFillAttribute,
258                 siW.dwFlags, siW.wShowWindow,
259                 (DWORD)siW.hStdInput, (DWORD)siW.hStdOutput, (DWORD)siW.hStdError);
260
261     /* Arguments */
262     childPrintf(hFile, "[Arguments]\nargcA=%d\n", myARGC);
263     for (i = 0; i < myARGC; i++)
264     {
265         childPrintf(hFile, "argvA%d=%s\n", i, encodeA(myARGV[i]));
266     }
267     childPrintf(hFile, "CommandLineA=%s\n", encodeA(GetCommandLineA()));
268
269 #if 0
270     int                 argcW;
271     WCHAR**             argvW;
272
273     /* this is part of shell32... and should be tested there */
274     argvW = CommandLineToArgvW(GetCommandLineW(), &argcW);
275     for (i = 0; i < argcW; i++)
276     {
277         childPrintf(hFile, "argvW%d=%s\n", i, encodeW(argvW[i]));
278     }
279 #endif
280     childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW()));
281
282     /* output of environment (Ansi) */
283     ptrA = GetEnvironmentStringsA();
284     if (ptrA)
285     {
286         char    env_var[MAX_LISTED_ENV_VAR];
287
288         childPrintf(hFile, "[EnvironmentA]\n");
289         i = 0;
290         while (*ptrA)
291         {
292             lstrcpynA(env_var, ptrA, MAX_LISTED_ENV_VAR);
293             childPrintf(hFile, "env%d=%s\n", i, encodeA(env_var));
294             i++;
295             ptrA += strlen(ptrA) + 1;
296         }
297         childPrintf(hFile, "len=%d\n\n", i);
298     }
299
300     /* output of environment (Unicode) */
301     ptrW = GetEnvironmentStringsW();
302     if (ptrW)
303     {
304         WCHAR   env_var[MAX_LISTED_ENV_VAR];
305
306         childPrintf(hFile, "[EnvironmentW]\n");
307         i = 0;
308         while (*ptrW)
309         {
310             lstrcpynW(env_var, ptrW, MAX_LISTED_ENV_VAR - 1);
311             env_var[MAX_LISTED_ENV_VAR - 1] = '\0';
312             childPrintf(hFile, "env%d=%s\n", i, encodeW(env_var));
313             i++;
314             ptrW += lstrlenW(ptrW) + 1;
315         }
316         childPrintf(hFile, "len=%d\n\n", i);
317     }
318
319     childPrintf(hFile, "[Misc]\n");
320     if (GetCurrentDirectoryA(sizeof(bufA), bufA))
321         childPrintf(hFile, "CurrDirA=%s\n", encodeA(bufA));
322     if (GetCurrentDirectoryW(sizeof(bufW) / sizeof(bufW[0]), bufW))
323         childPrintf(hFile, "CurrDirW=%s\n", encodeW(bufW));
324     childPrintf(hFile, "\n");
325
326     if (option && strcmp(option, "console") == 0)
327     {
328         CONSOLE_SCREEN_BUFFER_INFO      sbi;
329         HANDLE hConIn  = GetStdHandle(STD_INPUT_HANDLE);
330         HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
331         DWORD modeIn, modeOut;
332
333         childPrintf(hFile, "[Console]\n");
334         if (GetConsoleScreenBufferInfo(hConOut, &sbi))
335         {
336             childPrintf(hFile, "SizeX=%d\nSizeY=%d\nCursorX=%d\nCursorY=%d\nAttributes=%d\n",
337                         sbi.dwSize.X, sbi.dwSize.Y, sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y, sbi.wAttributes);
338             childPrintf(hFile, "winLeft=%d\nwinTop=%d\nwinRight=%d\nwinBottom=%d\n",
339                         sbi.srWindow.Left, sbi.srWindow.Top, sbi.srWindow.Right, sbi.srWindow.Bottom);
340             childPrintf(hFile, "maxWinWidth=%d\nmaxWinHeight=%d\n",
341                         sbi.dwMaximumWindowSize.X, sbi.dwMaximumWindowSize.Y);
342         }
343         childPrintf(hFile, "InputCP=%d\nOutputCP=%d\n",
344                     GetConsoleCP(), GetConsoleOutputCP());
345         if (GetConsoleMode(hConIn, &modeIn))
346             childPrintf(hFile, "InputMode=%ld\n", modeIn);
347         if (GetConsoleMode(hConOut, &modeOut))
348             childPrintf(hFile, "OutputMode=%ld\n", modeOut);
349
350         /* now that we have written all relevant information, let's change it */
351         SetLastError(0xdeadbeef);
352         ret = SetConsoleCP(1252);
353         if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
354         {
355             win_skip("Setting the codepage is not implemented");
356         }
357         else
358         {
359             ok(ret, "Setting CP\n");
360             ok(SetConsoleOutputCP(1252), "Setting SB CP\n");
361         }
362
363         ret = SetConsoleMode(hConIn, modeIn ^ 1);
364         ok( ret, "Setting mode (%d)\n", GetLastError());
365         ret = SetConsoleMode(hConOut, modeOut ^ 1);
366         ok( ret, "Setting mode (%d)\n", GetLastError());
367         sbi.dwCursorPosition.X ^= 1;
368         sbi.dwCursorPosition.Y ^= 1;
369         ret = SetConsoleCursorPosition(hConOut, sbi.dwCursorPosition);
370         ok( ret, "Setting cursor position (%d)\n", GetLastError());
371     }
372     if (option && strcmp(option, "stdhandle") == 0)
373     {
374         HANDLE hStdIn  = GetStdHandle(STD_INPUT_HANDLE);
375         HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
376
377         if (hStdIn != INVALID_HANDLE_VALUE || hStdOut != INVALID_HANDLE_VALUE)
378         {
379             char buf[1024];
380             DWORD r, w;
381
382             ok(ReadFile(hStdIn, buf, sizeof(buf), &r, NULL) && r > 0, "Reading message from input pipe\n");
383             childPrintf(hFile, "[StdHandle]\nmsg=%s\n\n", encodeA(buf));
384             ok(WriteFile(hStdOut, buf, r, &w, NULL) && w == r, "Writing message to output pipe\n");
385         }
386     }
387
388     if (option && strcmp(option, "exit_code") == 0)
389     {
390         childPrintf(hFile, "[ExitCode]\nvalue=%d\n\n", 123);
391         CloseHandle(hFile);
392         ExitProcess(123);
393     }
394
395     CloseHandle(hFile);
396 }
397
398 static char* getChildString(const char* sect, const char* key)
399 {
400     char        buf[1024+4*MAX_LISTED_ENV_VAR];
401     char*       ret;
402
403     GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), resfile);
404     if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
405     assert(!(strlen(buf) & 1));
406     ret = decodeA(buf);
407     return ret;
408 }
409
410 /* FIXME: this may be moved to the wtmain.c file, because it may be needed by
411  * others... (windows uses stricmp while Un*x uses strcasecmp...)
412  */
413 static int wtstrcasecmp(const char* p1, const char* p2)
414 {
415     char c1, c2;
416
417     c1 = c2 = '@';
418     while (c1 == c2 && c1)
419     {
420         c1 = *p1++; c2 = *p2++;
421         if (c1 != c2)
422         {
423             c1 = toupper(c1); c2 = toupper(c2);
424         }
425     }
426     return c1 - c2;
427 }
428
429 static int strCmp(const char* s1, const char* s2, BOOL sensitive)
430 {
431     if (!s1 && !s2) return 0;
432     if (!s2) return -1;
433     if (!s1) return 1;
434     return (sensitive) ? strcmp(s1, s2) : wtstrcasecmp(s1, s2);
435 }
436
437 static void ok_child_string( int line, const char *sect, const char *key,
438                              const char *expect, int sensitive )
439 {
440     char* result = getChildString( sect, key );
441     ok_(__FILE__, line)( strCmp(result, expect, sensitive) == 0, "%s:%s expected '%s', got '%s'\n",
442                          sect, key, expect ? expect : "(null)", result );
443 }
444
445 #define okChildString(sect, key, expect) ok_child_string(__LINE__, (sect), (key), (expect), 1 )
446 #define okChildIString(sect, key, expect) ok_child_string(__LINE__, (sect), (key), (expect), 0 )
447
448 /* using !expect ensures that the test will fail if the sect/key isn't present
449  * in result file
450  */
451 #define okChildInt(sect, key, expect) \
452     do { \
453         UINT result = GetPrivateProfileIntA((sect), (key), !(expect), resfile); \
454         ok(result == expect, "%s:%s expected %d, but got %d\n", (sect), (key), (int)(expect), result); \
455    } while (0)
456
457 static void test_Startup(void)
458 {
459     char                buffer[MAX_PATH];
460     PROCESS_INFORMATION info;
461     STARTUPINFOA        startup,si;
462     static CHAR title[]   = "I'm the title string",
463                 desktop[] = "I'm the desktop string",
464                 empty[]   = "";
465
466     /* let's start simplistic */
467     memset(&startup, 0, sizeof(startup));
468     startup.cb = sizeof(startup);
469     startup.dwFlags = STARTF_USESHOWWINDOW;
470     startup.wShowWindow = SW_SHOWNORMAL;
471
472     get_file_name(resfile);
473     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
474     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
475     /* wait for child to terminate */
476     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
477     /* child process has changed result file, so let profile functions know about it */
478     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
479
480     GetStartupInfoA(&si);
481     okChildInt("StartupInfoA", "cb", startup.cb);
482     okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
483     okChildInt("StartupInfoA", "dwX", startup.dwX);
484     okChildInt("StartupInfoA", "dwY", startup.dwY);
485     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
486     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
487     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
488     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
489     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
490     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
491     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
492     release_memory();
493     assert(DeleteFileA(resfile) != 0);
494
495     /* not so simplistic now */
496     memset(&startup, 0, sizeof(startup));
497     startup.cb = sizeof(startup);
498     startup.dwFlags = STARTF_USESHOWWINDOW;
499     startup.wShowWindow = SW_SHOWNORMAL;
500     startup.lpTitle = title;
501     startup.lpDesktop = desktop;
502     startup.dwXCountChars = 0x12121212;
503     startup.dwYCountChars = 0x23232323;
504     startup.dwX = 0x34343434;
505     startup.dwY = 0x45454545;
506     startup.dwXSize = 0x56565656;
507     startup.dwYSize = 0x67676767;
508     startup.dwFillAttribute = 0xA55A;
509
510     get_file_name(resfile);
511     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
512     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
513     /* wait for child to terminate */
514     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
515     /* child process has changed result file, so let profile functions know about it */
516     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
517
518     okChildInt("StartupInfoA", "cb", startup.cb);
519     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
520     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
521     okChildInt("StartupInfoA", "dwX", startup.dwX);
522     okChildInt("StartupInfoA", "dwY", startup.dwY);
523     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
524     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
525     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
526     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
527     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
528     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
529     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
530     release_memory();
531     assert(DeleteFileA(resfile) != 0);
532
533     /* not so simplistic now */
534     memset(&startup, 0, sizeof(startup));
535     startup.cb = sizeof(startup);
536     startup.dwFlags = STARTF_USESHOWWINDOW;
537     startup.wShowWindow = SW_SHOWNORMAL;
538     startup.lpTitle = title;
539     startup.lpDesktop = NULL;
540     startup.dwXCountChars = 0x12121212;
541     startup.dwYCountChars = 0x23232323;
542     startup.dwX = 0x34343434;
543     startup.dwY = 0x45454545;
544     startup.dwXSize = 0x56565656;
545     startup.dwYSize = 0x67676767;
546     startup.dwFillAttribute = 0xA55A;
547
548     get_file_name(resfile);
549     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
550     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
551     /* wait for child to terminate */
552     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
553     /* child process has changed result file, so let profile functions know about it */
554     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
555
556     okChildInt("StartupInfoA", "cb", startup.cb);
557     okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
558     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
559     okChildInt("StartupInfoA", "dwX", startup.dwX);
560     okChildInt("StartupInfoA", "dwY", startup.dwY);
561     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
562     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
563     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
564     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
565     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
566     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
567     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
568     release_memory();
569     assert(DeleteFileA(resfile) != 0);
570
571     /* not so simplistic now */
572     memset(&startup, 0, sizeof(startup));
573     startup.cb = sizeof(startup);
574     startup.dwFlags = STARTF_USESHOWWINDOW;
575     startup.wShowWindow = SW_SHOWNORMAL;
576     startup.lpTitle = title;
577     startup.lpDesktop = empty;
578     startup.dwXCountChars = 0x12121212;
579     startup.dwYCountChars = 0x23232323;
580     startup.dwX = 0x34343434;
581     startup.dwY = 0x45454545;
582     startup.dwXSize = 0x56565656;
583     startup.dwYSize = 0x67676767;
584     startup.dwFillAttribute = 0xA55A;
585
586     get_file_name(resfile);
587     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
588     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
589     /* wait for child to terminate */
590     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
591     /* child process has changed result file, so let profile functions know about it */
592     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
593
594     okChildInt("StartupInfoA", "cb", startup.cb);
595     todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
596     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
597     okChildInt("StartupInfoA", "dwX", startup.dwX);
598     okChildInt("StartupInfoA", "dwY", startup.dwY);
599     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
600     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
601     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
602     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
603     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
604     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
605     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
606     release_memory();
607     assert(DeleteFileA(resfile) != 0);
608
609     /* not so simplistic now */
610     memset(&startup, 0, sizeof(startup));
611     startup.cb = sizeof(startup);
612     startup.dwFlags = STARTF_USESHOWWINDOW;
613     startup.wShowWindow = SW_SHOWNORMAL;
614     startup.lpTitle = NULL;
615     startup.lpDesktop = desktop;
616     startup.dwXCountChars = 0x12121212;
617     startup.dwYCountChars = 0x23232323;
618     startup.dwX = 0x34343434;
619     startup.dwY = 0x45454545;
620     startup.dwXSize = 0x56565656;
621     startup.dwYSize = 0x67676767;
622     startup.dwFillAttribute = 0xA55A;
623
624     get_file_name(resfile);
625     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
626     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
627     /* wait for child to terminate */
628     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
629     /* child process has changed result file, so let profile functions know about it */
630     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
631
632     okChildInt("StartupInfoA", "cb", startup.cb);
633     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
634     ok (startup.lpTitle == NULL || !strcmp(startup.lpTitle, selfname),
635         "StartupInfoA:lpTitle expected '%s' or null, got '%s'\n", selfname, 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 = empty;
654     startup.lpDesktop = desktop;
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\n");
666     /* wait for child to terminate */
667     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
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     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     /* not so simplistic now */
687     memset(&startup, 0, sizeof(startup));
688     startup.cb = sizeof(startup);
689     startup.dwFlags = STARTF_USESHOWWINDOW;
690     startup.wShowWindow = SW_SHOWNORMAL;
691     startup.lpTitle = empty;
692     startup.lpDesktop = empty;
693     startup.dwXCountChars = 0x12121212;
694     startup.dwYCountChars = 0x23232323;
695     startup.dwX = 0x34343434;
696     startup.dwY = 0x45454545;
697     startup.dwXSize = 0x56565656;
698     startup.dwYSize = 0x67676767;
699     startup.dwFillAttribute = 0xA55A;
700
701     get_file_name(resfile);
702     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
703     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
704     /* wait for child to terminate */
705     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
706     /* child process has changed result file, so let profile functions know about it */
707     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
708
709     okChildInt("StartupInfoA", "cb", startup.cb);
710     todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
711     todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
712     okChildInt("StartupInfoA", "dwX", startup.dwX);
713     okChildInt("StartupInfoA", "dwY", startup.dwY);
714     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
715     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
716     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
717     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
718     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
719     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
720     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
721     release_memory();
722     assert(DeleteFileA(resfile) != 0);
723
724     /* TODO: test for A/W and W/A and W/W */
725 }
726
727 static void test_CommandLine(void)
728 {
729     char                buffer[MAX_PATH], fullpath[MAX_PATH], *lpFilePart, *p;
730     PROCESS_INFORMATION info;
731     STARTUPINFOA        startup;
732     DWORD               len;
733     BOOL                ret;
734
735     memset(&startup, 0, sizeof(startup));
736     startup.cb = sizeof(startup);
737     startup.dwFlags = STARTF_USESHOWWINDOW;
738     startup.wShowWindow = SW_SHOWNORMAL;
739
740     /* the basics */
741     get_file_name(resfile);
742     sprintf(buffer, "%s tests/process.c %s \"C:\\Program Files\\my nice app.exe\"", selfname, resfile);
743     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
744     /* wait for child to terminate */
745     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
746     /* child process has changed result file, so let profile functions know about it */
747     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
748
749     okChildInt("Arguments", "argcA", 4);
750     okChildString("Arguments", "argvA3", "C:\\Program Files\\my nice app.exe");
751     okChildString("Arguments", "argvA4", NULL);
752     okChildString("Arguments", "CommandLineA", buffer);
753     release_memory();
754     assert(DeleteFileA(resfile) != 0);
755
756     memset(&startup, 0, sizeof(startup));
757     startup.cb = sizeof(startup);
758     startup.dwFlags = STARTF_USESHOWWINDOW;
759     startup.wShowWindow = SW_SHOWNORMAL;
760
761     /* from Frangois */
762     get_file_name(resfile);
763     sprintf(buffer, "%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
764     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
765     /* wait for child to terminate */
766     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
767     /* child process has changed result file, so let profile functions know about it */
768     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
769
770     okChildInt("Arguments", "argcA", 6);
771     okChildString("Arguments", "argvA3", "a\"b\\");
772     okChildString("Arguments", "argvA4", "c\"");
773     okChildString("Arguments", "argvA5", "d");
774     okChildString("Arguments", "argvA6", NULL);
775     okChildString("Arguments", "CommandLineA", buffer);
776     release_memory();
777     assert(DeleteFileA(resfile) != 0);
778
779     /* Test for Bug1330 to show that XP doesn't change '/' to '\\' in argv[0]*/
780     get_file_name(resfile);
781     /* Use exename to avoid buffer containing things like 'C:' */
782     sprintf(buffer, "./%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", exename, resfile);
783     SetLastError(0xdeadbeef);
784     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
785     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
786     /* wait for child to terminate */
787     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
788     /* child process has changed result file, so let profile functions know about it */
789     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
790     sprintf(buffer, "./%s", exename);
791     okChildString("Arguments", "argvA0", buffer);
792     release_memory();
793     assert(DeleteFileA(resfile) != 0);
794
795     get_file_name(resfile);
796     /* Use exename to avoid buffer containing things like 'C:' */
797     sprintf(buffer, ".\\%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", exename, resfile);
798     SetLastError(0xdeadbeef);
799     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
800     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
801     /* wait for child to terminate */
802     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
803     /* child process has changed result file, so let profile functions know about it */
804     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
805     sprintf(buffer, ".\\%s", exename);
806     okChildString("Arguments", "argvA0", buffer);
807     release_memory();
808     assert(DeleteFileA(resfile) != 0);
809     
810     get_file_name(resfile);
811     len = GetFullPathNameA(selfname, MAX_PATH, fullpath, &lpFilePart);
812     assert ( lpFilePart != 0);
813     *(lpFilePart -1 ) = 0;
814     p = strrchr(fullpath, '\\');
815     assert (p);
816     /* Use exename to avoid buffer containing things like 'C:' */
817     sprintf(buffer, "..%s/%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", p, exename, resfile);
818     SetLastError(0xdeadbeef);
819     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
820     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
821     /* wait for child to terminate */
822     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
823     /* child process has changed result file, so let profile functions know about it */
824     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
825     sprintf(buffer, "..%s/%s", p, exename);
826     okChildString("Arguments", "argvA0", buffer);
827     release_memory();
828     assert(DeleteFileA(resfile) != 0);
829     
830 }
831
832 static void test_Directory(void)
833 {
834     char                buffer[MAX_PATH];
835     PROCESS_INFORMATION info;
836     STARTUPINFOA        startup;
837     char windir[MAX_PATH];
838     static CHAR cmdline[] = "winver.exe";
839
840     memset(&startup, 0, sizeof(startup));
841     startup.cb = sizeof(startup);
842     startup.dwFlags = STARTF_USESHOWWINDOW;
843     startup.wShowWindow = SW_SHOWNORMAL;
844
845     /* the basics */
846     get_file_name(resfile);
847     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
848     GetWindowsDirectoryA( windir, sizeof(windir) );
849     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, windir, &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     okChildIString("Misc", "CurrDirA", windir);
856     release_memory();
857     assert(DeleteFileA(resfile) != 0);
858
859     /* search PATH for the exe if directory is NULL */
860     ok(CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
861     ok(TerminateProcess(info.hProcess, 0), "Child process termination\n");
862
863     /* if any directory is provided, don't search PATH, error on bad directory */
864     SetLastError(0xdeadbeef);
865     memset(&info, 0, sizeof(info));
866     ok(!CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0L,
867                        NULL, "non\\existent\\directory", &startup, &info), "CreateProcess\n");
868     ok(GetLastError() == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", GetLastError());
869     ok(!TerminateProcess(info.hProcess, 0), "Child process should not exist\n");
870 }
871
872 static BOOL is_str_env_drive_dir(const char* str)
873 {
874     return str[0] == '=' && str[1] >= 'A' && str[1] <= 'Z' && str[2] == ':' &&
875         str[3] == '=' && str[4] == str[1];
876 }
877
878 /* compared expected child's environment (in gesA) from actual
879  * environment our child got
880  */
881 static void cmpEnvironment(const char* gesA)
882 {
883     int                 i, clen;
884     const char*         ptrA;
885     char*               res;
886     char                key[32];
887     BOOL                found;
888
889     clen = GetPrivateProfileIntA("EnvironmentA", "len", 0, resfile);
890     
891     /* now look each parent env in child */
892     if ((ptrA = gesA) != NULL)
893     {
894         while (*ptrA)
895         {
896             for (i = 0; i < clen; i++)
897             {
898                 sprintf(key, "env%d", i);
899                 res = getChildString("EnvironmentA", key);
900                 if (strncmp(ptrA, res, MAX_LISTED_ENV_VAR - 1) == 0)
901                     break;
902             }
903             found = i < clen;
904             ok(found, "Parent-env string %s isn't in child process\n", ptrA);
905             
906             ptrA += strlen(ptrA) + 1;
907             release_memory();
908         }
909     }
910     /* and each child env in parent */
911     for (i = 0; i < clen; i++)
912     {
913         sprintf(key, "env%d", i);
914         res = getChildString("EnvironmentA", key);
915         if ((ptrA = gesA) != NULL)
916         {
917             while (*ptrA)
918             {
919                 if (strncmp(res, ptrA, MAX_LISTED_ENV_VAR - 1) == 0)
920                     break;
921                 ptrA += strlen(ptrA) + 1;
922             }
923             if (!*ptrA) ptrA = NULL;
924         }
925
926         if (!is_str_env_drive_dir(res))
927         {
928             found = ptrA != NULL;
929             ok(found, "Child-env string %s isn't in parent process\n", res);
930         }
931         /* else => should also test we get the right per drive default directory here... */
932     }
933 }
934
935 static void test_Environment(void)
936 {
937     char                buffer[MAX_PATH];
938     PROCESS_INFORMATION info;
939     STARTUPINFOA        startup;
940     char*               child_env;
941     int                 child_env_len;
942     char*               ptr;
943     char*               env;
944     int                 slen;
945
946     memset(&startup, 0, sizeof(startup));
947     startup.cb = sizeof(startup);
948     startup.dwFlags = STARTF_USESHOWWINDOW;
949     startup.wShowWindow = SW_SHOWNORMAL;
950
951     /* the basics */
952     get_file_name(resfile);
953     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
954     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
955     /* wait for child to terminate */
956     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
957     /* child process has changed result file, so let profile functions know about it */
958     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
959     
960     cmpEnvironment(GetEnvironmentStringsA());
961     release_memory();
962     assert(DeleteFileA(resfile) != 0);
963
964     memset(&startup, 0, sizeof(startup));
965     startup.cb = sizeof(startup);
966     startup.dwFlags = STARTF_USESHOWWINDOW;
967     startup.wShowWindow = SW_SHOWNORMAL;
968
969     /* the basics */
970     get_file_name(resfile);
971     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
972     
973     child_env_len = 0;
974     ptr = GetEnvironmentStringsA();
975     while(*ptr)
976     {
977         slen = strlen(ptr)+1;
978         child_env_len += slen;
979         ptr += slen;
980     }
981     /* Add space for additional environment variables */
982     child_env_len += 256;
983     child_env = HeapAlloc(GetProcessHeap(), 0, child_env_len);
984     
985     ptr = child_env;
986     sprintf(ptr, "=%c:=%s", 'C', "C:\\FOO\\BAR");
987     ptr += strlen(ptr) + 1;
988     strcpy(ptr, "PATH=C:\\WINDOWS;C:\\WINDOWS\\SYSTEM;C:\\MY\\OWN\\DIR");
989     ptr += strlen(ptr) + 1;
990     strcpy(ptr, "FOO=BAR");
991     ptr += strlen(ptr) + 1;
992     strcpy(ptr, "BAR=FOOBAR");
993     ptr += strlen(ptr) + 1;
994     /* copy all existing variables except:
995      * - WINELOADER
996      * - PATH (already set above)
997      * - the directory definitions (=[A-Z]:=)
998      */
999     for (env = GetEnvironmentStringsA(); *env; env += strlen(env) + 1)
1000     {
1001         if (strncmp(env, "PATH=", 5) != 0 &&
1002             strncmp(env, "WINELOADER=", 11) != 0 &&
1003             !is_str_env_drive_dir(env))
1004         {
1005             strcpy(ptr, env);
1006             ptr += strlen(ptr) + 1;
1007         }
1008     }
1009     *ptr = '\0';
1010     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, child_env, NULL, &startup, &info), "CreateProcess\n");
1011     /* wait for child to terminate */
1012     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1013     /* child process has changed result file, so let profile functions know about it */
1014     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1015     
1016     cmpEnvironment(child_env);
1017
1018     HeapFree(GetProcessHeap(), 0, child_env);
1019     release_memory();
1020     assert(DeleteFileA(resfile) != 0);
1021 }
1022
1023 static  void    test_SuspendFlag(void)
1024 {
1025     char                buffer[MAX_PATH];
1026     PROCESS_INFORMATION info;
1027     STARTUPINFOA       startup, us;
1028     DWORD               exit_status;
1029
1030     /* let's start simplistic */
1031     memset(&startup, 0, sizeof(startup));
1032     startup.cb = sizeof(startup);
1033     startup.dwFlags = STARTF_USESHOWWINDOW;
1034     startup.wShowWindow = SW_SHOWNORMAL;
1035
1036     get_file_name(resfile);
1037     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
1038     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup, &info), "CreateProcess\n");
1039
1040     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
1041     Sleep(8000);
1042     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
1043     ok(ResumeThread(info.hThread) == 1, "Resuming thread\n");
1044
1045     /* wait for child to terminate */
1046     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1047     /* child process has changed result file, so let profile functions know about it */
1048     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1049
1050     GetStartupInfoA(&us);
1051
1052     okChildInt("StartupInfoA", "cb", startup.cb);
1053     okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
1054     ok (startup.lpTitle == NULL || !strcmp(startup.lpTitle, selfname),
1055         "StartupInfoA:lpTitle expected '%s' or null, got '%s'\n", selfname, startup.lpTitle);
1056     okChildInt("StartupInfoA", "dwX", startup.dwX);
1057     okChildInt("StartupInfoA", "dwY", startup.dwY);
1058     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
1059     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
1060     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
1061     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
1062     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
1063     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
1064     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
1065     release_memory();
1066     assert(DeleteFileA(resfile) != 0);
1067 }
1068
1069 static  void    test_DebuggingFlag(void)
1070 {
1071     char                buffer[MAX_PATH];
1072     PROCESS_INFORMATION info;
1073     STARTUPINFOA       startup, us;
1074     DEBUG_EVENT         de;
1075     unsigned            dbg = 0;
1076
1077     /* let's start simplistic */
1078     memset(&startup, 0, sizeof(startup));
1079     startup.cb = sizeof(startup);
1080     startup.dwFlags = STARTF_USESHOWWINDOW;
1081     startup.wShowWindow = SW_SHOWNORMAL;
1082
1083     get_file_name(resfile);
1084     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
1085     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
1086
1087     /* get all startup events up to the entry point break exception */
1088     do 
1089     {
1090         ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
1091         ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
1092         if (de.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) dbg++;
1093     } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
1094
1095     ok(dbg, "I have seen a debug event\n");
1096     /* wait for child to terminate */
1097     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1098     /* child process has changed result file, so let profile functions know about it */
1099     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1100
1101     GetStartupInfoA(&us);
1102
1103     okChildInt("StartupInfoA", "cb", startup.cb);
1104     okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
1105     ok (startup.lpTitle == NULL || !strcmp(startup.lpTitle, selfname),
1106         "StartupInfoA:lpTitle expected '%s' or null, got '%s'\n", selfname, startup.lpTitle);
1107     okChildInt("StartupInfoA", "dwX", startup.dwX);
1108     okChildInt("StartupInfoA", "dwY", startup.dwY);
1109     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
1110     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
1111     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
1112     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
1113     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
1114     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
1115     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
1116     release_memory();
1117     assert(DeleteFileA(resfile) != 0);
1118 }
1119
1120 static BOOL is_console(HANDLE h)
1121 {
1122     return h != INVALID_HANDLE_VALUE && ((ULONG_PTR)h & 3) == 3;
1123 }
1124
1125 static void test_Console(void)
1126 {
1127     char                buffer[MAX_PATH];
1128     PROCESS_INFORMATION info;
1129     STARTUPINFOA       startup, us;
1130     SECURITY_ATTRIBUTES sa;
1131     CONSOLE_SCREEN_BUFFER_INFO  sbi, sbiC;
1132     DWORD               modeIn, modeOut, modeInC, modeOutC;
1133     DWORD               cpIn, cpOut, cpInC, cpOutC;
1134     DWORD               w;
1135     HANDLE              hChildIn, hChildInInh, hChildOut, hChildOutInh, hParentIn, hParentOut;
1136     const char*         msg = "This is a std-handle inheritance test.";
1137     unsigned            msg_len;
1138     BOOL                run_tests = TRUE;
1139
1140     memset(&startup, 0, sizeof(startup));
1141     startup.cb = sizeof(startup);
1142     startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
1143     startup.wShowWindow = SW_SHOWNORMAL;
1144
1145     sa.nLength = sizeof(sa);
1146     sa.lpSecurityDescriptor = NULL;
1147     sa.bInheritHandle = TRUE;
1148
1149     startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1150     startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1151
1152     /* first, we need to be sure we're attached to a console */
1153     if (!is_console(startup.hStdInput) || !is_console(startup.hStdOutput))
1154     {
1155         /* we're not attached to a console, let's do it */
1156         AllocConsole();
1157         startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1158         startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1159     }
1160     /* now verify everything's ok */
1161     ok(startup.hStdInput != INVALID_HANDLE_VALUE, "Opening ConIn\n");
1162     ok(startup.hStdOutput != INVALID_HANDLE_VALUE, "Opening ConOut\n");
1163     startup.hStdError = startup.hStdOutput;
1164
1165     ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbi), "Getting sb info\n");
1166     ok(GetConsoleMode(startup.hStdInput, &modeIn) && 
1167        GetConsoleMode(startup.hStdOutput, &modeOut), "Getting console modes\n");
1168     cpIn = GetConsoleCP();
1169     cpOut = GetConsoleOutputCP();
1170
1171     get_file_name(resfile);
1172     sprintf(buffer, "%s tests/process.c %s console", selfname, resfile);
1173     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
1174
1175     /* wait for child to terminate */
1176     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1177     /* child process has changed result file, so let profile functions know about it */
1178     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1179
1180     /* now get the modification the child has made, and resets parents expected values */
1181     ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbiC), "Getting sb info\n");
1182     ok(GetConsoleMode(startup.hStdInput, &modeInC) && 
1183        GetConsoleMode(startup.hStdOutput, &modeOutC), "Getting console modes\n");
1184
1185     SetConsoleMode(startup.hStdInput, modeIn);
1186     SetConsoleMode(startup.hStdOutput, modeOut);
1187
1188     cpInC = GetConsoleCP();
1189     cpOutC = GetConsoleOutputCP();
1190
1191     /* Try to set invalid CP */
1192     SetLastError(0xdeadbeef);
1193     ok(!SetConsoleCP(0), "Shouldn't succeed\n");
1194     ok(GetLastError()==ERROR_INVALID_PARAMETER ||
1195        broken(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED), /* win9x */
1196        "GetLastError: expecting %u got %u\n",
1197        ERROR_INVALID_PARAMETER, GetLastError());
1198     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1199         run_tests = FALSE;
1200
1201
1202     SetLastError(0xdeadbeef);
1203     ok(!SetConsoleOutputCP(0), "Shouldn't succeed\n");
1204     ok(GetLastError()==ERROR_INVALID_PARAMETER ||
1205        broken(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED), /* win9x */
1206        "GetLastError: expecting %u got %u\n",
1207        ERROR_INVALID_PARAMETER, GetLastError());
1208
1209     SetConsoleCP(cpIn);
1210     SetConsoleOutputCP(cpOut);
1211
1212     GetStartupInfoA(&us);
1213
1214     okChildInt("StartupInfoA", "cb", startup.cb);
1215     okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
1216     ok (startup.lpTitle == NULL || !strcmp(startup.lpTitle, selfname),
1217         "StartupInfoA:lpTitle expected '%s' or null, got '%s'\n", selfname, startup.lpTitle);
1218     okChildInt("StartupInfoA", "dwX", startup.dwX);
1219     okChildInt("StartupInfoA", "dwY", startup.dwY);
1220     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
1221     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
1222     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
1223     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
1224     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
1225     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
1226     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
1227
1228     /* check child correctly inherited the console */
1229     okChildInt("StartupInfoA", "hStdInput", (DWORD)startup.hStdInput);
1230     okChildInt("StartupInfoA", "hStdOutput", (DWORD)startup.hStdOutput);
1231     okChildInt("StartupInfoA", "hStdError", (DWORD)startup.hStdError);
1232     okChildInt("Console", "SizeX", (DWORD)sbi.dwSize.X);
1233     okChildInt("Console", "SizeY", (DWORD)sbi.dwSize.Y);
1234     okChildInt("Console", "CursorX", (DWORD)sbi.dwCursorPosition.X);
1235     okChildInt("Console", "CursorY", (DWORD)sbi.dwCursorPosition.Y);
1236     okChildInt("Console", "Attributes", sbi.wAttributes);
1237     okChildInt("Console", "winLeft", (DWORD)sbi.srWindow.Left);
1238     okChildInt("Console", "winTop", (DWORD)sbi.srWindow.Top);
1239     okChildInt("Console", "winRight", (DWORD)sbi.srWindow.Right);
1240     okChildInt("Console", "winBottom", (DWORD)sbi.srWindow.Bottom);
1241     okChildInt("Console", "maxWinWidth", (DWORD)sbi.dwMaximumWindowSize.X);
1242     okChildInt("Console", "maxWinHeight", (DWORD)sbi.dwMaximumWindowSize.Y);
1243     okChildInt("Console", "InputCP", cpIn);
1244     okChildInt("Console", "OutputCP", cpOut);
1245     okChildInt("Console", "InputMode", modeIn);
1246     okChildInt("Console", "OutputMode", modeOut);
1247
1248     if (run_tests)
1249     {
1250         ok(cpInC == 1252, "Wrong console CP (expected 1252 got %d/%d)\n", cpInC, cpIn);
1251         ok(cpOutC == 1252, "Wrong console-SB CP (expected 1252 got %d/%d)\n", cpOutC, cpOut);
1252     }
1253     else
1254         win_skip("Setting the codepage is not implemented");
1255
1256     ok(modeInC == (modeIn ^ 1), "Wrong console mode\n");
1257     ok(modeOutC == (modeOut ^ 1), "Wrong console-SB mode\n");
1258     trace("cursor position(X): %d/%d\n",sbi.dwCursorPosition.X, sbiC.dwCursorPosition.X);
1259     ok(sbiC.dwCursorPosition.Y == (sbi.dwCursorPosition.Y ^ 1), "Wrong cursor position\n");
1260
1261     release_memory();
1262     assert(DeleteFileA(resfile) != 0);
1263
1264     ok(CreatePipe(&hParentIn, &hChildOut, NULL, 0), "Creating parent-input pipe\n");
1265     ok(DuplicateHandle(GetCurrentProcess(), hChildOut, GetCurrentProcess(), 
1266                        &hChildOutInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
1267        "Duplicating as inheritable child-output pipe\n");
1268     CloseHandle(hChildOut);
1269  
1270     ok(CreatePipe(&hChildIn, &hParentOut, NULL, 0), "Creating parent-output pipe\n");
1271     ok(DuplicateHandle(GetCurrentProcess(), hChildIn, GetCurrentProcess(), 
1272                        &hChildInInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
1273        "Duplicating as inheritable child-input pipe\n");
1274     CloseHandle(hChildIn); 
1275     
1276     memset(&startup, 0, sizeof(startup));
1277     startup.cb = sizeof(startup);
1278     startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
1279     startup.wShowWindow = SW_SHOWNORMAL;
1280     startup.hStdInput = hChildInInh;
1281     startup.hStdOutput = hChildOutInh;
1282     startup.hStdError = hChildOutInh;
1283
1284     get_file_name(resfile);
1285     sprintf(buffer, "%s tests/process.c %s stdhandle", selfname, resfile);
1286     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
1287     ok(CloseHandle(hChildInInh), "Closing handle\n");
1288     ok(CloseHandle(hChildOutInh), "Closing handle\n");
1289
1290     msg_len = strlen(msg) + 1;
1291     ok(WriteFile(hParentOut, msg, msg_len, &w, NULL), "Writing to child\n");
1292     ok(w == msg_len, "Should have written %u bytes, actually wrote %u\n", msg_len, w);
1293     memset(buffer, 0, sizeof(buffer));
1294     ok(ReadFile(hParentIn, buffer, sizeof(buffer), &w, NULL), "Reading from child\n");
1295     ok(strcmp(buffer, msg) == 0, "Should have received '%s'\n", msg);
1296
1297     /* wait for child to terminate */
1298     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1299     /* child process has changed result file, so let profile functions know about it */
1300     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1301
1302     okChildString("StdHandle", "msg", msg);
1303
1304     release_memory();
1305     assert(DeleteFileA(resfile) != 0);
1306 }
1307
1308 static  void    test_ExitCode(void)
1309 {
1310     char                buffer[MAX_PATH];
1311     PROCESS_INFORMATION info;
1312     STARTUPINFOA        startup;
1313     DWORD               code;
1314
1315     /* let's start simplistic */
1316     memset(&startup, 0, sizeof(startup));
1317     startup.cb = sizeof(startup);
1318     startup.dwFlags = STARTF_USESHOWWINDOW;
1319     startup.wShowWindow = SW_SHOWNORMAL;
1320
1321     get_file_name(resfile);
1322     sprintf(buffer, "%s tests/process.c %s exit_code", selfname, resfile);
1323     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
1324
1325     /* wait for child to terminate */
1326     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1327     /* child process has changed result file, so let profile functions know about it */
1328     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1329
1330     ok(GetExitCodeProcess(info.hProcess, &code), "Getting exit code\n");
1331     okChildInt("ExitCode", "value", code);
1332
1333     release_memory();
1334     assert(DeleteFileA(resfile) != 0);
1335 }
1336
1337 static void test_OpenProcess(void)
1338 {
1339     HANDLE hproc;
1340     void *addr1;
1341     MEMORY_BASIC_INFORMATION info;
1342     SIZE_T dummy, read_bytes;
1343
1344     /* not exported in all windows versions */
1345     if ((!pVirtualAllocEx) || (!pVirtualFreeEx)) {
1346         skip("VirtualAllocEx not found\n");
1347         return;
1348     }
1349
1350     /* without PROCESS_VM_OPERATION */
1351     hproc = OpenProcess(PROCESS_ALL_ACCESS & ~PROCESS_VM_OPERATION, FALSE, GetCurrentProcessId());
1352     ok(hproc != NULL, "OpenProcess error %d\n", GetLastError());
1353
1354     SetLastError(0xdeadbeef);
1355     addr1 = pVirtualAllocEx(hproc, 0, 0xFFFC, MEM_RESERVE, PAGE_NOACCESS);
1356     ok(!addr1, "VirtualAllocEx should fail\n");
1357     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1358     {   /* Win9x */
1359         CloseHandle(hproc);
1360         skip("VirtualAllocEx not implemented\n");
1361         return;
1362     }
1363     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1364
1365     read_bytes = 0xdeadbeef;
1366     SetLastError(0xdeadbeef);
1367     ok(ReadProcessMemory(hproc, test_OpenProcess, &dummy, sizeof(dummy), &read_bytes),
1368        "ReadProcessMemory error %d\n", GetLastError());
1369     ok(read_bytes == sizeof(dummy), "wrong read bytes %ld\n", read_bytes);
1370
1371     CloseHandle(hproc);
1372
1373     hproc = OpenProcess(PROCESS_VM_OPERATION, FALSE, GetCurrentProcessId());
1374     ok(hproc != NULL, "OpenProcess error %d\n", GetLastError());
1375
1376     addr1 = pVirtualAllocEx(hproc, 0, 0xFFFC, MEM_RESERVE, PAGE_NOACCESS);
1377     ok(addr1 != NULL, "VirtualAllocEx error %d\n", GetLastError());
1378
1379     /* without PROCESS_QUERY_INFORMATION */
1380     SetLastError(0xdeadbeef);
1381     ok(!VirtualQueryEx(hproc, addr1, &info, sizeof(info)),
1382        "VirtualQueryEx without PROCESS_QUERY_INFORMATION rights should fail\n");
1383     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1384
1385     /* without PROCESS_VM_READ */
1386     read_bytes = 0xdeadbeef;
1387     SetLastError(0xdeadbeef);
1388     ok(!ReadProcessMemory(hproc, addr1, &dummy, sizeof(dummy), &read_bytes),
1389        "ReadProcessMemory without PROCESS_VM_READ rights should fail\n");
1390     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1391     ok(read_bytes == 0, "wrong read bytes %ld\n", read_bytes);
1392
1393     CloseHandle(hproc);
1394
1395     hproc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
1396
1397     memset(&info, 0xcc, sizeof(info));
1398     ok(VirtualQueryEx(hproc, addr1, &info, sizeof(info)) == sizeof(info),
1399        "VirtualQueryEx error %d\n", GetLastError());
1400
1401     ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress, addr1);
1402     ok(info.AllocationBase == addr1, "%p != %p\n", info.AllocationBase, addr1);
1403     ok(info.AllocationProtect == PAGE_NOACCESS, "%x != PAGE_NOACCESS\n", info.AllocationProtect);
1404     ok(info.RegionSize == 0x10000, "%lx != 0x10000\n", info.RegionSize);
1405     ok(info.State == MEM_RESERVE, "%x != MEM_RESERVE\n", info.State);
1406     /* NT reports Protect == 0 for a not committed memory block */
1407     ok(info.Protect == 0 /* NT */ ||
1408        info.Protect == PAGE_NOACCESS, /* Win9x */
1409         "%x != PAGE_NOACCESS\n", info.Protect);
1410     ok(info.Type == MEM_PRIVATE, "%x != MEM_PRIVATE\n", info.Type);
1411
1412     SetLastError(0xdeadbeef);
1413     ok(!pVirtualFreeEx(hproc, addr1, 0, MEM_RELEASE),
1414        "VirtualFreeEx without PROCESS_VM_OPERATION rights should fail\n");
1415     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1416
1417     CloseHandle(hproc);
1418
1419     ok(VirtualFree(addr1, 0, MEM_RELEASE), "VirtualFree failed\n");
1420 }
1421
1422 START_TEST(process)
1423 {
1424     int b = init();
1425     ok(b, "Basic init of CreateProcess test\n");
1426     if (!b) return;
1427
1428     if (myARGC >= 3)
1429     {
1430         doChild(myARGV[2], (myARGC == 3) ? NULL : myARGV[3]);
1431         return;
1432     }
1433     test_Startup();
1434     test_CommandLine();
1435     test_Directory();
1436     test_Environment();
1437     test_SuspendFlag();
1438     test_DebuggingFlag();
1439     test_Console();
1440     test_ExitCode();
1441     test_OpenProcess();
1442     /* things that can be tested:
1443      *  lookup:         check the way program to be executed is searched
1444      *  handles:        check the handle inheritance stuff (+sec options)
1445      *  console:        check if console creation parameters work
1446      */
1447 }