kernel32/tests: Test waiting on a disconnected client pipe.
[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 "ntstatus.h"
28 #define WIN32_NO_STATUS
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "wincon.h"
33 #include "winnls.h"
34 #include "winternl.h"
35
36 #include "wine/test.h"
37
38 #define expect_eq_d(expected, actual) \
39     do { \
40       int value = (actual); \
41       ok((expected) == value, "Expected " #actual " to be %d (" #expected ") is %d\n", \
42           (expected), value); \
43     } while (0)
44 #define expect_eq_ws_i(expected, actual) \
45     do { \
46       LPCWSTR value = (actual); \
47       ok(lstrcmpiW((expected), value) == 0, "Expected " #actual " to be L\"%s\" (" #expected ") is L\"%s\"\n", \
48           wine_dbgstr_w(expected), wine_dbgstr_w(value)); \
49     } while (0)
50
51 /* A simpler version of wine_dbgstr_w. Note that the returned buffer will be
52  * invalid after 16 calls to this funciton. */
53 static const char *wine_dbgstr_w(LPCWSTR wstr)
54 {
55   static char *buffers[16];
56   static int curr_buffer = 0;
57
58   int size;
59
60   curr_buffer = (curr_buffer + 1) % 16;
61   HeapFree(GetProcessHeap(), 0, buffers[curr_buffer]);
62   size = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
63   buffers[curr_buffer] = HeapAlloc(GetProcessHeap(), 0, size);
64   size = WideCharToMultiByte(CP_ACP, 0, wstr, -1, buffers[curr_buffer], size, NULL, NULL);
65   return buffers[curr_buffer];
66 }
67
68 static HINSTANCE hkernel32;
69 static LPVOID (WINAPI *pVirtualAllocEx)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
70 static BOOL   (WINAPI *pVirtualFreeEx)(HANDLE, LPVOID, SIZE_T, DWORD);
71 static BOOL   (WINAPI *pQueryFullProcessImageNameW)(HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD lpdwSize);
72
73 /* ############################### */
74 static char     base[MAX_PATH];
75 static char     selfname[MAX_PATH];
76 static char*    exename;
77 static char     resfile[MAX_PATH];
78
79 static int      myARGC;
80 static char**   myARGV;
81
82 /* As some environment variables get very long on Unix, we only test for
83  * the first 127 bytes.
84  * Note that increasing this value past 256 may exceed the buffer size
85  * limitations of the *Profile functions (at least on Wine).
86  */
87 #define MAX_LISTED_ENV_VAR      128
88
89 /* ---------------- portable memory allocation thingie */
90
91 static char     memory[1024*256];
92 static char*    memory_index = memory;
93
94 static char*    grab_memory(size_t len)
95 {
96     char*       ret = memory_index;
97     /* align on dword */
98     len = (len + 3) & ~3;
99     memory_index += len;
100     assert(memory_index <= memory + sizeof(memory));
101     return ret;
102 }
103
104 static void     release_memory(void)
105 {
106     memory_index = memory;
107 }
108
109 /* ---------------- simplistic tool to encode/decode strings (to hide \ " ' and such) */
110
111 static const char* encodeA(const char* str)
112 {
113     char*       ptr;
114     size_t      len,i;
115
116     if (!str) return "";
117     len = strlen(str) + 1;
118     ptr = grab_memory(len * 2 + 1);
119     for (i = 0; i < len; i++)
120         sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
121     ptr[2 * len] = '\0';
122     return ptr;
123 }
124
125 static const char* encodeW(const WCHAR* str)
126 {
127     char*       ptr;
128     size_t      len,i;
129
130     if (!str) return "";
131     len = lstrlenW(str) + 1;
132     ptr = grab_memory(len * 4 + 1);
133     assert(ptr);
134     for (i = 0; i < len; i++)
135         sprintf(&ptr[i * 4], "%04x", (unsigned int)(unsigned short)str[i]);
136     ptr[4 * len] = '\0';
137     return ptr;
138 }
139
140 static unsigned decode_char(char c)
141 {
142     if (c >= '0' && c <= '9') return c - '0';
143     if (c >= 'a' && c <= 'f') return c - 'a' + 10;
144     assert(c >= 'A' && c <= 'F');
145     return c - 'A' + 10;
146 }
147
148 static char*    decodeA(const char* str)
149 {
150     char*       ptr;
151     size_t      len,i;
152
153     len = strlen(str) / 2;
154     if (!len--) return NULL;
155     ptr = grab_memory(len + 1);
156     for (i = 0; i < len; i++)
157         ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
158     ptr[len] = '\0';
159     return ptr;
160 }
161
162 /* This will be needed to decode Unicode strings saved by the child process
163  * when we test Unicode functions.
164  */
165 static WCHAR*   decodeW(const char* str)
166 {
167     size_t      len;
168     WCHAR*      ptr;
169     int         i;
170
171     len = strlen(str) / 4;
172     if (!len--) return NULL;
173     ptr = (WCHAR*)grab_memory(len * 2 + 1);
174     for (i = 0; i < len; i++)
175         ptr[i] = (decode_char(str[4 * i]) << 12) |
176             (decode_char(str[4 * i + 1]) << 8) |
177             (decode_char(str[4 * i + 2]) << 4) |
178             (decode_char(str[4 * i + 3]) << 0);
179     ptr[len] = '\0';
180     return ptr;
181 }
182
183 /******************************************************************
184  *              init
185  *
186  * generates basic information like:
187  *      base:           absolute path to curr dir
188  *      selfname:       the way to reinvoke ourselves
189  *      exename:        executable without the path
190  * function-pointers, which are not implemented in all windows versions
191  */
192 static int     init(void)
193 {
194     char *p;
195
196     myARGC = winetest_get_mainargs( &myARGV );
197     if (!GetCurrentDirectoryA(sizeof(base), base)) return 0;
198     strcpy(selfname, myARGV[0]);
199
200     /* Strip the path of selfname */
201     if ((p = strrchr(selfname, '\\')) != NULL) exename = p + 1;
202     else exename = selfname;
203
204     if ((p = strrchr(exename, '/')) != NULL) exename = p + 1;
205
206     hkernel32 = GetModuleHandleA("kernel32");
207     pVirtualAllocEx = (void *) GetProcAddress(hkernel32, "VirtualAllocEx");
208     pVirtualFreeEx = (void *) GetProcAddress(hkernel32, "VirtualFreeEx");
209     pQueryFullProcessImageNameW = (void *) GetProcAddress(hkernel32, "QueryFullProcessImageNameW");
210     return 1;
211 }
212
213 /******************************************************************
214  *              get_file_name
215  *
216  * generates an absolute file_name for temporary file
217  *
218  */
219 static void     get_file_name(char* buf)
220 {
221     char        path[MAX_PATH];
222
223     buf[0] = '\0';
224     GetTempPathA(sizeof(path), path);
225     GetTempFileNameA(path, "wt", 0, buf);
226 }
227
228 /******************************************************************
229  *              static void     childPrintf
230  *
231  */
232 static void     childPrintf(HANDLE h, const char* fmt, ...)
233 {
234     va_list     valist;
235     char        buffer[1024+4*MAX_LISTED_ENV_VAR];
236     DWORD       w;
237
238     va_start(valist, fmt);
239     vsprintf(buffer, fmt, valist);
240     va_end(valist);
241     WriteFile(h, buffer, strlen(buffer), &w, NULL);
242 }
243
244
245 /******************************************************************
246  *              doChild
247  *
248  * output most of the information in the child process
249  */
250 static void     doChild(const char* file, const char* option)
251 {
252     STARTUPINFOA        siA;
253     STARTUPINFOW        siW;
254     int                 i;
255     char*               ptrA;
256     WCHAR*              ptrW;
257     char                bufA[MAX_PATH];
258     WCHAR               bufW[MAX_PATH];
259     HANDLE              hFile = CreateFileA(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
260     BOOL ret;
261
262     if (hFile == INVALID_HANDLE_VALUE) return;
263
264     /* output of startup info (Ansi) */
265     GetStartupInfoA(&siA);
266     childPrintf(hFile,
267                 "[StartupInfoA]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
268                 "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
269                 "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
270                 "dwFlags=%lu\nwShowWindow=%u\n"
271                 "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
272                 siA.cb, encodeA(siA.lpDesktop), encodeA(siA.lpTitle),
273                 siA.dwX, siA.dwY, siA.dwXSize, siA.dwYSize,
274                 siA.dwXCountChars, siA.dwYCountChars, siA.dwFillAttribute,
275                 siA.dwFlags, siA.wShowWindow,
276                 (DWORD_PTR)siA.hStdInput, (DWORD_PTR)siA.hStdOutput, (DWORD_PTR)siA.hStdError);
277
278     /* since GetStartupInfoW is only implemented in win2k,
279      * zero out before calling so we can notice the difference
280      */
281     memset(&siW, 0, sizeof(siW));
282     GetStartupInfoW(&siW);
283     childPrintf(hFile,
284                 "[StartupInfoW]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
285                 "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
286                 "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
287                 "dwFlags=%lu\nwShowWindow=%u\n"
288                 "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
289                 siW.cb, encodeW(siW.lpDesktop), encodeW(siW.lpTitle),
290                 siW.dwX, siW.dwY, siW.dwXSize, siW.dwYSize,
291                 siW.dwXCountChars, siW.dwYCountChars, siW.dwFillAttribute,
292                 siW.dwFlags, siW.wShowWindow,
293                 (DWORD_PTR)siW.hStdInput, (DWORD_PTR)siW.hStdOutput, (DWORD_PTR)siW.hStdError);
294
295     /* Arguments */
296     childPrintf(hFile, "[Arguments]\nargcA=%d\n", myARGC);
297     for (i = 0; i < myARGC; i++)
298     {
299         childPrintf(hFile, "argvA%d=%s\n", i, encodeA(myARGV[i]));
300     }
301     childPrintf(hFile, "CommandLineA=%s\n", encodeA(GetCommandLineA()));
302
303 #if 0
304     int                 argcW;
305     WCHAR**             argvW;
306
307     /* this is part of shell32... and should be tested there */
308     argvW = CommandLineToArgvW(GetCommandLineW(), &argcW);
309     for (i = 0; i < argcW; i++)
310     {
311         childPrintf(hFile, "argvW%d=%s\n", i, encodeW(argvW[i]));
312     }
313 #endif
314     childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW()));
315
316     /* output of environment (Ansi) */
317     ptrA = GetEnvironmentStringsA();
318     if (ptrA)
319     {
320         char    env_var[MAX_LISTED_ENV_VAR];
321
322         childPrintf(hFile, "[EnvironmentA]\n");
323         i = 0;
324         while (*ptrA)
325         {
326             lstrcpynA(env_var, ptrA, MAX_LISTED_ENV_VAR);
327             childPrintf(hFile, "env%d=%s\n", i, encodeA(env_var));
328             i++;
329             ptrA += strlen(ptrA) + 1;
330         }
331         childPrintf(hFile, "len=%d\n\n", i);
332     }
333
334     /* output of environment (Unicode) */
335     ptrW = GetEnvironmentStringsW();
336     if (ptrW)
337     {
338         WCHAR   env_var[MAX_LISTED_ENV_VAR];
339
340         childPrintf(hFile, "[EnvironmentW]\n");
341         i = 0;
342         while (*ptrW)
343         {
344             lstrcpynW(env_var, ptrW, MAX_LISTED_ENV_VAR - 1);
345             env_var[MAX_LISTED_ENV_VAR - 1] = '\0';
346             childPrintf(hFile, "env%d=%s\n", i, encodeW(env_var));
347             i++;
348             ptrW += lstrlenW(ptrW) + 1;
349         }
350         childPrintf(hFile, "len=%d\n\n", i);
351     }
352
353     childPrintf(hFile, "[Misc]\n");
354     if (GetCurrentDirectoryA(sizeof(bufA), bufA))
355         childPrintf(hFile, "CurrDirA=%s\n", encodeA(bufA));
356     if (GetCurrentDirectoryW(sizeof(bufW) / sizeof(bufW[0]), bufW))
357         childPrintf(hFile, "CurrDirW=%s\n", encodeW(bufW));
358     childPrintf(hFile, "\n");
359
360     if (option && strcmp(option, "console") == 0)
361     {
362         CONSOLE_SCREEN_BUFFER_INFO      sbi;
363         HANDLE hConIn  = GetStdHandle(STD_INPUT_HANDLE);
364         HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
365         DWORD modeIn, modeOut;
366
367         childPrintf(hFile, "[Console]\n");
368         if (GetConsoleScreenBufferInfo(hConOut, &sbi))
369         {
370             childPrintf(hFile, "SizeX=%d\nSizeY=%d\nCursorX=%d\nCursorY=%d\nAttributes=%d\n",
371                         sbi.dwSize.X, sbi.dwSize.Y, sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y, sbi.wAttributes);
372             childPrintf(hFile, "winLeft=%d\nwinTop=%d\nwinRight=%d\nwinBottom=%d\n",
373                         sbi.srWindow.Left, sbi.srWindow.Top, sbi.srWindow.Right, sbi.srWindow.Bottom);
374             childPrintf(hFile, "maxWinWidth=%d\nmaxWinHeight=%d\n",
375                         sbi.dwMaximumWindowSize.X, sbi.dwMaximumWindowSize.Y);
376         }
377         childPrintf(hFile, "InputCP=%d\nOutputCP=%d\n",
378                     GetConsoleCP(), GetConsoleOutputCP());
379         if (GetConsoleMode(hConIn, &modeIn))
380             childPrintf(hFile, "InputMode=%ld\n", modeIn);
381         if (GetConsoleMode(hConOut, &modeOut))
382             childPrintf(hFile, "OutputMode=%ld\n", modeOut);
383
384         /* now that we have written all relevant information, let's change it */
385         SetLastError(0xdeadbeef);
386         ret = SetConsoleCP(1252);
387         if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
388         {
389             win_skip("Setting the codepage is not implemented\n");
390         }
391         else
392         {
393             ok(ret, "Setting CP\n");
394             ok(SetConsoleOutputCP(1252), "Setting SB CP\n");
395         }
396
397         ret = SetConsoleMode(hConIn, modeIn ^ 1);
398         ok( ret, "Setting mode (%d)\n", GetLastError());
399         ret = SetConsoleMode(hConOut, modeOut ^ 1);
400         ok( ret, "Setting mode (%d)\n", GetLastError());
401         sbi.dwCursorPosition.X ^= 1;
402         sbi.dwCursorPosition.Y ^= 1;
403         ret = SetConsoleCursorPosition(hConOut, sbi.dwCursorPosition);
404         ok( ret, "Setting cursor position (%d)\n", GetLastError());
405     }
406     if (option && strcmp(option, "stdhandle") == 0)
407     {
408         HANDLE hStdIn  = GetStdHandle(STD_INPUT_HANDLE);
409         HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
410
411         if (hStdIn != INVALID_HANDLE_VALUE || hStdOut != INVALID_HANDLE_VALUE)
412         {
413             char buf[1024];
414             DWORD r, w;
415
416             ok(ReadFile(hStdIn, buf, sizeof(buf), &r, NULL) && r > 0, "Reading message from input pipe\n");
417             childPrintf(hFile, "[StdHandle]\nmsg=%s\n\n", encodeA(buf));
418             ok(WriteFile(hStdOut, buf, r, &w, NULL) && w == r, "Writing message to output pipe\n");
419         }
420     }
421
422     if (option && strcmp(option, "exit_code") == 0)
423     {
424         childPrintf(hFile, "[ExitCode]\nvalue=%d\n\n", 123);
425         CloseHandle(hFile);
426         ExitProcess(123);
427     }
428
429     CloseHandle(hFile);
430 }
431
432 static char* getChildString(const char* sect, const char* key)
433 {
434     char        buf[1024+4*MAX_LISTED_ENV_VAR];
435     char*       ret;
436
437     GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), resfile);
438     if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
439     assert(!(strlen(buf) & 1));
440     ret = decodeA(buf);
441     return ret;
442 }
443
444 static WCHAR* getChildStringW(const char* sect, const char* key)
445 {
446     char        buf[1024+4*MAX_LISTED_ENV_VAR];
447     WCHAR*       ret;
448
449     GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), resfile);
450     if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
451     assert(!(strlen(buf) & 1));
452     ret = decodeW(buf);
453     return ret;
454 }
455
456 /* FIXME: this may be moved to the wtmain.c file, because it may be needed by
457  * others... (windows uses stricmp while Un*x uses strcasecmp...)
458  */
459 static int wtstrcasecmp(const char* p1, const char* p2)
460 {
461     char c1, c2;
462
463     c1 = c2 = '@';
464     while (c1 == c2 && c1)
465     {
466         c1 = *p1++; c2 = *p2++;
467         if (c1 != c2)
468         {
469             c1 = toupper(c1); c2 = toupper(c2);
470         }
471     }
472     return c1 - c2;
473 }
474
475 static int strCmp(const char* s1, const char* s2, BOOL sensitive)
476 {
477     if (!s1 && !s2) return 0;
478     if (!s2) return -1;
479     if (!s1) return 1;
480     return (sensitive) ? strcmp(s1, s2) : wtstrcasecmp(s1, s2);
481 }
482
483 static void ok_child_string( int line, const char *sect, const char *key,
484                              const char *expect, int sensitive )
485 {
486     char* result = getChildString( sect, key );
487     ok_(__FILE__, line)( strCmp(result, expect, sensitive) == 0, "%s:%s expected '%s', got '%s'\n",
488                          sect, key, expect ? expect : "(null)", result );
489 }
490
491 static void ok_child_stringWA( int line, const char *sect, const char *key,
492                              const char *expect, int sensitive )
493 {
494     WCHAR* expectW;
495     CHAR* resultA;
496     DWORD len;
497     WCHAR* result = getChildStringW( sect, key );
498
499     len = MultiByteToWideChar( CP_ACP, 0, expect, -1, NULL, 0);
500     expectW = HeapAlloc(GetProcessHeap(),0,len*sizeof(WCHAR));
501     MultiByteToWideChar( CP_ACP, 0, expect, -1, expectW, len);
502
503     len = WideCharToMultiByte( CP_ACP, 0, result, -1, NULL, 0, NULL, NULL);
504     resultA = HeapAlloc(GetProcessHeap(),0,len*sizeof(CHAR));
505     WideCharToMultiByte( CP_ACP, 0, result, -1, resultA, len, NULL, NULL);
506
507     if (sensitive)
508         ok_(__FILE__, line)( lstrcmpW(result, expectW) == 0, "%s:%s expected '%s', got '%s'\n",
509                          sect, key, expect ? expect : "(null)", resultA );
510     else
511         ok_(__FILE__, line)( lstrcmpiW(result, expectW) == 0, "%s:%s expected '%s', got '%s'\n",
512                          sect, key, expect ? expect : "(null)", resultA );
513     HeapFree(GetProcessHeap(),0,expectW);
514     HeapFree(GetProcessHeap(),0,resultA);
515 }
516
517 #define okChildString(sect, key, expect) ok_child_string(__LINE__, (sect), (key), (expect), 1 )
518 #define okChildIString(sect, key, expect) ok_child_string(__LINE__, (sect), (key), (expect), 0 )
519 #define okChildStringWA(sect, key, expect) ok_child_stringWA(__LINE__, (sect), (key), (expect), 1 )
520
521 /* using !expect ensures that the test will fail if the sect/key isn't present
522  * in result file
523  */
524 #define okChildInt(sect, key, expect) \
525     do { \
526         UINT result = GetPrivateProfileIntA((sect), (key), !(expect), resfile); \
527         ok(result == expect, "%s:%s expected %u, but got %u\n", (sect), (key), (UINT)(expect), result); \
528    } while (0)
529
530 static void test_Startup(void)
531 {
532     char                buffer[MAX_PATH];
533     PROCESS_INFORMATION info;
534     STARTUPINFOA        startup,si;
535     static CHAR title[]   = "I'm the title string",
536                 desktop[] = "winsta0\\default",
537                 empty[]   = "";
538
539     /* let's start simplistic */
540     memset(&startup, 0, sizeof(startup));
541     startup.cb = sizeof(startup);
542     startup.dwFlags = STARTF_USESHOWWINDOW;
543     startup.wShowWindow = SW_SHOWNORMAL;
544
545     get_file_name(resfile);
546     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
547     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
548     /* wait for child to terminate */
549     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
550     /* child process has changed result file, so let profile functions know about it */
551     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
552
553     GetStartupInfoA(&si);
554     okChildInt("StartupInfoA", "cb", startup.cb);
555     okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
556     okChildInt("StartupInfoA", "dwX", startup.dwX);
557     okChildInt("StartupInfoA", "dwY", startup.dwY);
558     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
559     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
560     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
561     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
562     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
563     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
564     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
565     release_memory();
566     assert(DeleteFileA(resfile) != 0);
567
568     /* not so simplistic now */
569     memset(&startup, 0, sizeof(startup));
570     startup.cb = sizeof(startup);
571     startup.dwFlags = STARTF_USESHOWWINDOW;
572     startup.wShowWindow = SW_SHOWNORMAL;
573     startup.lpTitle = title;
574     startup.lpDesktop = desktop;
575     startup.dwXCountChars = 0x12121212;
576     startup.dwYCountChars = 0x23232323;
577     startup.dwX = 0x34343434;
578     startup.dwY = 0x45454545;
579     startup.dwXSize = 0x56565656;
580     startup.dwYSize = 0x67676767;
581     startup.dwFillAttribute = 0xA55A;
582
583     get_file_name(resfile);
584     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
585     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
586     /* wait for child to terminate */
587     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
588     /* child process has changed result file, so let profile functions know about it */
589     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
590
591     okChildInt("StartupInfoA", "cb", startup.cb);
592     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
593     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
594     okChildInt("StartupInfoA", "dwX", startup.dwX);
595     okChildInt("StartupInfoA", "dwY", startup.dwY);
596     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
597     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
598     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
599     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
600     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
601     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
602     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
603     release_memory();
604     assert(DeleteFileA(resfile) != 0);
605
606     /* not so simplistic now */
607     memset(&startup, 0, sizeof(startup));
608     startup.cb = sizeof(startup);
609     startup.dwFlags = STARTF_USESHOWWINDOW;
610     startup.wShowWindow = SW_SHOWNORMAL;
611     startup.lpTitle = title;
612     startup.lpDesktop = NULL;
613     startup.dwXCountChars = 0x12121212;
614     startup.dwYCountChars = 0x23232323;
615     startup.dwX = 0x34343434;
616     startup.dwY = 0x45454545;
617     startup.dwXSize = 0x56565656;
618     startup.dwYSize = 0x67676767;
619     startup.dwFillAttribute = 0xA55A;
620
621     get_file_name(resfile);
622     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
623     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
624     /* wait for child to terminate */
625     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
626     /* child process has changed result file, so let profile functions know about it */
627     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
628
629     okChildInt("StartupInfoA", "cb", startup.cb);
630     okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
631     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
632     okChildInt("StartupInfoA", "dwX", startup.dwX);
633     okChildInt("StartupInfoA", "dwY", startup.dwY);
634     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
635     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
636     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
637     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
638     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
639     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
640     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
641     release_memory();
642     assert(DeleteFileA(resfile) != 0);
643
644     /* not so simplistic now */
645     memset(&startup, 0, sizeof(startup));
646     startup.cb = sizeof(startup);
647     startup.dwFlags = STARTF_USESHOWWINDOW;
648     startup.wShowWindow = SW_SHOWNORMAL;
649     startup.lpTitle = title;
650     startup.lpDesktop = empty;
651     startup.dwXCountChars = 0x12121212;
652     startup.dwYCountChars = 0x23232323;
653     startup.dwX = 0x34343434;
654     startup.dwY = 0x45454545;
655     startup.dwXSize = 0x56565656;
656     startup.dwYSize = 0x67676767;
657     startup.dwFillAttribute = 0xA55A;
658
659     get_file_name(resfile);
660     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
661     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
662     /* wait for child to terminate */
663     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
664     /* child process has changed result file, so let profile functions know about it */
665     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
666
667     okChildInt("StartupInfoA", "cb", startup.cb);
668     todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
669     okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
670     okChildInt("StartupInfoA", "dwX", startup.dwX);
671     okChildInt("StartupInfoA", "dwY", startup.dwY);
672     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
673     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
674     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
675     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
676     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
677     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
678     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
679     release_memory();
680     assert(DeleteFileA(resfile) != 0);
681
682     /* not so simplistic now */
683     memset(&startup, 0, sizeof(startup));
684     startup.cb = sizeof(startup);
685     startup.dwFlags = STARTF_USESHOWWINDOW;
686     startup.wShowWindow = SW_SHOWNORMAL;
687     startup.lpTitle = NULL;
688     startup.lpDesktop = desktop;
689     startup.dwXCountChars = 0x12121212;
690     startup.dwYCountChars = 0x23232323;
691     startup.dwX = 0x34343434;
692     startup.dwY = 0x45454545;
693     startup.dwXSize = 0x56565656;
694     startup.dwYSize = 0x67676767;
695     startup.dwFillAttribute = 0xA55A;
696
697     get_file_name(resfile);
698     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
699     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
700     /* wait for child to terminate */
701     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
702     /* child process has changed result file, so let profile functions know about it */
703     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
704
705     okChildInt("StartupInfoA", "cb", startup.cb);
706     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
707     ok (startup.lpTitle == NULL || !strcmp(startup.lpTitle, selfname),
708         "StartupInfoA:lpTitle expected '%s' or null, got '%s'\n", selfname, startup.lpTitle);
709     okChildInt("StartupInfoA", "dwX", startup.dwX);
710     okChildInt("StartupInfoA", "dwY", startup.dwY);
711     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
712     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
713     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
714     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
715     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
716     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
717     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
718     release_memory();
719     assert(DeleteFileA(resfile) != 0);
720
721     /* not so simplistic now */
722     memset(&startup, 0, sizeof(startup));
723     startup.cb = sizeof(startup);
724     startup.dwFlags = STARTF_USESHOWWINDOW;
725     startup.wShowWindow = SW_SHOWNORMAL;
726     startup.lpTitle = empty;
727     startup.lpDesktop = desktop;
728     startup.dwXCountChars = 0x12121212;
729     startup.dwYCountChars = 0x23232323;
730     startup.dwX = 0x34343434;
731     startup.dwY = 0x45454545;
732     startup.dwXSize = 0x56565656;
733     startup.dwYSize = 0x67676767;
734     startup.dwFillAttribute = 0xA55A;
735
736     get_file_name(resfile);
737     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
738     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
739     /* wait for child to terminate */
740     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
741     /* child process has changed result file, so let profile functions know about it */
742     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
743
744     okChildInt("StartupInfoA", "cb", startup.cb);
745     okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
746     todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
747     okChildInt("StartupInfoA", "dwX", startup.dwX);
748     okChildInt("StartupInfoA", "dwY", startup.dwY);
749     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
750     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
751     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
752     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
753     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
754     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
755     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
756     release_memory();
757     assert(DeleteFileA(resfile) != 0);
758
759     /* not so simplistic now */
760     memset(&startup, 0, sizeof(startup));
761     startup.cb = sizeof(startup);
762     startup.dwFlags = STARTF_USESHOWWINDOW;
763     startup.wShowWindow = SW_SHOWNORMAL;
764     startup.lpTitle = empty;
765     startup.lpDesktop = empty;
766     startup.dwXCountChars = 0x12121212;
767     startup.dwYCountChars = 0x23232323;
768     startup.dwX = 0x34343434;
769     startup.dwY = 0x45454545;
770     startup.dwXSize = 0x56565656;
771     startup.dwYSize = 0x67676767;
772     startup.dwFillAttribute = 0xA55A;
773
774     get_file_name(resfile);
775     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
776     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
777     /* wait for child to terminate */
778     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
779     /* child process has changed result file, so let profile functions know about it */
780     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
781
782     okChildInt("StartupInfoA", "cb", startup.cb);
783     todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
784     todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
785     okChildInt("StartupInfoA", "dwX", startup.dwX);
786     okChildInt("StartupInfoA", "dwY", startup.dwY);
787     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
788     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
789     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
790     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
791     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
792     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
793     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
794     release_memory();
795     assert(DeleteFileA(resfile) != 0);
796
797     /* TODO: test for A/W and W/A and W/W */
798 }
799
800 static void test_CommandLine(void)
801 {
802     char                buffer[MAX_PATH], fullpath[MAX_PATH], *lpFilePart, *p;
803     char                buffer2[MAX_PATH];
804     PROCESS_INFORMATION info;
805     STARTUPINFOA        startup;
806     DWORD               len;
807     BOOL                ret;
808
809     memset(&startup, 0, sizeof(startup));
810     startup.cb = sizeof(startup);
811     startup.dwFlags = STARTF_USESHOWWINDOW;
812     startup.wShowWindow = SW_SHOWNORMAL;
813
814     /* the basics */
815     get_file_name(resfile);
816     sprintf(buffer, "%s tests/process.c %s \"C:\\Program Files\\my nice app.exe\"", selfname, resfile);
817     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
818     /* wait for child to terminate */
819     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
820     /* child process has changed result file, so let profile functions know about it */
821     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
822
823     okChildInt("Arguments", "argcA", 4);
824     okChildString("Arguments", "argvA3", "C:\\Program Files\\my nice app.exe");
825     okChildString("Arguments", "argvA4", NULL);
826     okChildString("Arguments", "CommandLineA", buffer);
827     release_memory();
828     assert(DeleteFileA(resfile) != 0);
829
830     memset(&startup, 0, sizeof(startup));
831     startup.cb = sizeof(startup);
832     startup.dwFlags = STARTF_USESHOWWINDOW;
833     startup.wShowWindow = SW_SHOWNORMAL;
834
835     /* from Frangois */
836     get_file_name(resfile);
837     sprintf(buffer, "%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
838     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
839     /* wait for child to terminate */
840     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
841     /* child process has changed result file, so let profile functions know about it */
842     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
843
844     okChildInt("Arguments", "argcA", 6);
845     okChildString("Arguments", "argvA3", "a\"b\\");
846     okChildString("Arguments", "argvA4", "c\"");
847     okChildString("Arguments", "argvA5", "d");
848     okChildString("Arguments", "argvA6", NULL);
849     okChildString("Arguments", "CommandLineA", buffer);
850     release_memory();
851     assert(DeleteFileA(resfile) != 0);
852
853     /* Test for Bug1330 to show that XP doesn't change '/' to '\\' in argv[0]*/
854     get_file_name(resfile);
855     /* Use exename to avoid buffer containing things like 'C:' */
856     sprintf(buffer, "./%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", exename, resfile);
857     SetLastError(0xdeadbeef);
858     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
859     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
860     /* wait for child to terminate */
861     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
862     /* child process has changed result file, so let profile functions know about it */
863     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
864     sprintf(buffer, "./%s", exename);
865     okChildString("Arguments", "argvA0", buffer);
866     release_memory();
867     assert(DeleteFileA(resfile) != 0);
868
869     get_file_name(resfile);
870     /* Use exename to avoid buffer containing things like 'C:' */
871     sprintf(buffer, ".\\%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", exename, resfile);
872     SetLastError(0xdeadbeef);
873     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
874     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
875     /* wait for child to terminate */
876     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
877     /* child process has changed result file, so let profile functions know about it */
878     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
879     sprintf(buffer, ".\\%s", exename);
880     okChildString("Arguments", "argvA0", buffer);
881     release_memory();
882     assert(DeleteFileA(resfile) != 0);
883     
884     get_file_name(resfile);
885     len = GetFullPathNameA(selfname, MAX_PATH, fullpath, &lpFilePart);
886     assert ( lpFilePart != 0);
887     *(lpFilePart -1 ) = 0;
888     p = strrchr(fullpath, '\\');
889     /* Use exename to avoid buffer containing things like 'C:' */
890     if (p) sprintf(buffer, "..%s/%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", p, exename, resfile);
891     else sprintf(buffer, "./%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", exename, resfile);
892     SetLastError(0xdeadbeef);
893     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
894     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
895     /* wait for child to terminate */
896     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
897     /* child process has changed result file, so let profile functions know about it */
898     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
899     if (p) sprintf(buffer, "..%s/%s", p, exename);
900     else sprintf(buffer, "./%s", exename);
901     okChildString("Arguments", "argvA0", buffer);
902     release_memory();
903     assert(DeleteFileA(resfile) != 0);
904
905     /* Using AppName */
906     get_file_name(resfile);
907     len = GetFullPathNameA(selfname, MAX_PATH, fullpath, &lpFilePart);
908     assert ( lpFilePart != 0);
909     *(lpFilePart -1 ) = 0;
910     p = strrchr(fullpath, '\\');
911     /* Use exename to avoid buffer containing things like 'C:' */
912     if (p) sprintf(buffer, "..%s/%s", p, exename);
913     else sprintf(buffer, "./%s", exename);
914     sprintf(buffer2, "dummy tests/process.c %s \"a\\\"b\\\\\" c\\\" d", resfile);
915     SetLastError(0xdeadbeef);
916     ret = CreateProcessA(buffer, buffer2, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
917     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
918     /* wait for child to terminate */
919     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
920     /* child process has changed result file, so let profile functions know about it */
921     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
922     sprintf(buffer, "tests/process.c %s", resfile);
923     okChildString("Arguments", "argvA0", "dummy");
924     okChildString("Arguments", "CommandLineA", buffer2);
925     okChildStringWA("Arguments", "CommandLineW", buffer2);
926     release_memory();
927     assert(DeleteFileA(resfile) != 0);
928
929     if (0) /* Test crashes on NT-based Windows. */
930     {
931         /* Test NULL application name and command line parameters. */
932         SetLastError(0xdeadbeef);
933         ret = CreateProcessA(NULL, NULL, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
934         ok(!ret, "CreateProcessA unexpectedly succeeded\n");
935         ok(GetLastError() == ERROR_INVALID_PARAMETER,
936            "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
937     }
938
939     buffer[0] = '\0';
940
941     /* Test empty application name parameter. */
942     SetLastError(0xdeadbeef);
943     ret = CreateProcessA(buffer, NULL, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
944     ok(!ret, "CreateProcessA unexpectedly succeeded\n");
945     ok(GetLastError() == ERROR_PATH_NOT_FOUND ||
946        broken(GetLastError() == ERROR_FILE_NOT_FOUND) /* Win9x/WinME */ ||
947        broken(GetLastError() == ERROR_ACCESS_DENIED) /* Win98 */,
948        "Expected ERROR_PATH_NOT_FOUND, got %d\n", GetLastError());
949
950     buffer2[0] = '\0';
951
952     /* Test empty application name and command line parameters. */
953     SetLastError(0xdeadbeef);
954     ret = CreateProcessA(buffer, buffer2, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
955     ok(!ret, "CreateProcessA unexpectedly succeeded\n");
956     ok(GetLastError() == ERROR_PATH_NOT_FOUND ||
957        broken(GetLastError() == ERROR_FILE_NOT_FOUND) /* Win9x/WinME */ ||
958        broken(GetLastError() == ERROR_ACCESS_DENIED) /* Win98 */,
959        "Expected ERROR_PATH_NOT_FOUND, got %d\n", GetLastError());
960
961     /* Test empty command line parameter. */
962     SetLastError(0xdeadbeef);
963     ret = CreateProcessA(NULL, buffer2, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
964     ok(!ret, "CreateProcessA unexpectedly succeeded\n");
965     ok(GetLastError() == ERROR_FILE_NOT_FOUND ||
966        GetLastError() == ERROR_PATH_NOT_FOUND /* NT4 */ ||
967        GetLastError() == ERROR_BAD_PATHNAME /* Win98 */,
968        "Expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError());
969
970     strcpy(buffer, "doesnotexist.exe");
971     strcpy(buffer2, "does not exist.exe");
972
973     /* Test nonexistent application name. */
974     SetLastError(0xdeadbeef);
975     ret = CreateProcessA(buffer, NULL, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
976     ok(!ret, "CreateProcessA unexpectedly succeeded\n");
977     ok(GetLastError() == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError());
978
979     SetLastError(0xdeadbeef);
980     ret = CreateProcessA(buffer2, NULL, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
981     ok(!ret, "CreateProcessA unexpectedly succeeded\n");
982     ok(GetLastError() == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError());
983
984     /* Test nonexistent command line parameter. */
985     SetLastError(0xdeadbeef);
986     ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
987     ok(!ret, "CreateProcessA unexpectedly succeeded\n");
988     ok(GetLastError() == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError());
989
990     SetLastError(0xdeadbeef);
991     ret = CreateProcessA(NULL, buffer2, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
992     ok(!ret, "CreateProcessA unexpectedly succeeded\n");
993     ok(GetLastError() == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError());
994 }
995
996 static void test_Directory(void)
997 {
998     char                buffer[MAX_PATH];
999     PROCESS_INFORMATION info;
1000     STARTUPINFOA        startup;
1001     char windir[MAX_PATH];
1002     static CHAR cmdline[] = "winver.exe";
1003
1004     memset(&startup, 0, sizeof(startup));
1005     startup.cb = sizeof(startup);
1006     startup.dwFlags = STARTF_USESHOWWINDOW;
1007     startup.wShowWindow = SW_SHOWNORMAL;
1008
1009     /* the basics */
1010     get_file_name(resfile);
1011     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
1012     GetWindowsDirectoryA( windir, sizeof(windir) );
1013     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, windir, &startup, &info), "CreateProcess\n");
1014     /* wait for child to terminate */
1015     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1016     /* child process has changed result file, so let profile functions know about it */
1017     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1018
1019     okChildIString("Misc", "CurrDirA", windir);
1020     release_memory();
1021     assert(DeleteFileA(resfile) != 0);
1022
1023     /* search PATH for the exe if directory is NULL */
1024     ok(CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
1025     ok(TerminateProcess(info.hProcess, 0), "Child process termination\n");
1026
1027     /* if any directory is provided, don't search PATH, error on bad directory */
1028     SetLastError(0xdeadbeef);
1029     memset(&info, 0, sizeof(info));
1030     ok(!CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0L,
1031                        NULL, "non\\existent\\directory", &startup, &info), "CreateProcess\n");
1032     ok(GetLastError() == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", GetLastError());
1033     ok(!TerminateProcess(info.hProcess, 0), "Child process should not exist\n");
1034 }
1035
1036 static BOOL is_str_env_drive_dir(const char* str)
1037 {
1038     return str[0] == '=' && str[1] >= 'A' && str[1] <= 'Z' && str[2] == ':' &&
1039         str[3] == '=' && str[4] == str[1];
1040 }
1041
1042 /* compared expected child's environment (in gesA) from actual
1043  * environment our child got
1044  */
1045 static void cmpEnvironment(const char* gesA)
1046 {
1047     int                 i, clen;
1048     const char*         ptrA;
1049     char*               res;
1050     char                key[32];
1051     BOOL                found;
1052
1053     clen = GetPrivateProfileIntA("EnvironmentA", "len", 0, resfile);
1054     
1055     /* now look each parent env in child */
1056     if ((ptrA = gesA) != NULL)
1057     {
1058         while (*ptrA)
1059         {
1060             for (i = 0; i < clen; i++)
1061             {
1062                 sprintf(key, "env%d", i);
1063                 res = getChildString("EnvironmentA", key);
1064                 if (strncmp(ptrA, res, MAX_LISTED_ENV_VAR - 1) == 0)
1065                     break;
1066             }
1067             found = i < clen;
1068             ok(found, "Parent-env string %s isn't in child process\n", ptrA);
1069             
1070             ptrA += strlen(ptrA) + 1;
1071             release_memory();
1072         }
1073     }
1074     /* and each child env in parent */
1075     for (i = 0; i < clen; i++)
1076     {
1077         sprintf(key, "env%d", i);
1078         res = getChildString("EnvironmentA", key);
1079         if ((ptrA = gesA) != NULL)
1080         {
1081             while (*ptrA)
1082             {
1083                 if (strncmp(res, ptrA, MAX_LISTED_ENV_VAR - 1) == 0)
1084                     break;
1085                 ptrA += strlen(ptrA) + 1;
1086             }
1087             if (!*ptrA) ptrA = NULL;
1088         }
1089
1090         if (!is_str_env_drive_dir(res))
1091         {
1092             found = ptrA != NULL;
1093             ok(found, "Child-env string %s isn't in parent process\n", res);
1094         }
1095         /* else => should also test we get the right per drive default directory here... */
1096     }
1097 }
1098
1099 static void test_Environment(void)
1100 {
1101     char                buffer[MAX_PATH];
1102     PROCESS_INFORMATION info;
1103     STARTUPINFOA        startup;
1104     char*               child_env;
1105     int                 child_env_len;
1106     char*               ptr;
1107     char*               env;
1108     int                 slen;
1109
1110     memset(&startup, 0, sizeof(startup));
1111     startup.cb = sizeof(startup);
1112     startup.dwFlags = STARTF_USESHOWWINDOW;
1113     startup.wShowWindow = SW_SHOWNORMAL;
1114
1115     /* the basics */
1116     get_file_name(resfile);
1117     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
1118     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
1119     /* wait for child to terminate */
1120     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1121     /* child process has changed result file, so let profile functions know about it */
1122     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1123     
1124     cmpEnvironment(GetEnvironmentStringsA());
1125     release_memory();
1126     assert(DeleteFileA(resfile) != 0);
1127
1128     memset(&startup, 0, sizeof(startup));
1129     startup.cb = sizeof(startup);
1130     startup.dwFlags = STARTF_USESHOWWINDOW;
1131     startup.wShowWindow = SW_SHOWNORMAL;
1132
1133     /* the basics */
1134     get_file_name(resfile);
1135     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
1136     
1137     child_env_len = 0;
1138     ptr = GetEnvironmentStringsA();
1139     while(*ptr)
1140     {
1141         slen = strlen(ptr)+1;
1142         child_env_len += slen;
1143         ptr += slen;
1144     }
1145     /* Add space for additional environment variables */
1146     child_env_len += 256;
1147     child_env = HeapAlloc(GetProcessHeap(), 0, child_env_len);
1148     
1149     ptr = child_env;
1150     sprintf(ptr, "=%c:=%s", 'C', "C:\\FOO\\BAR");
1151     ptr += strlen(ptr) + 1;
1152     strcpy(ptr, "PATH=C:\\WINDOWS;C:\\WINDOWS\\SYSTEM;C:\\MY\\OWN\\DIR");
1153     ptr += strlen(ptr) + 1;
1154     strcpy(ptr, "FOO=BAR");
1155     ptr += strlen(ptr) + 1;
1156     strcpy(ptr, "BAR=FOOBAR");
1157     ptr += strlen(ptr) + 1;
1158     /* copy all existing variables except:
1159      * - WINELOADER
1160      * - PATH (already set above)
1161      * - the directory definitions (=[A-Z]:=)
1162      */
1163     for (env = GetEnvironmentStringsA(); *env; env += strlen(env) + 1)
1164     {
1165         if (strncmp(env, "PATH=", 5) != 0 &&
1166             strncmp(env, "WINELOADER=", 11) != 0 &&
1167             !is_str_env_drive_dir(env))
1168         {
1169             strcpy(ptr, env);
1170             ptr += strlen(ptr) + 1;
1171         }
1172     }
1173     *ptr = '\0';
1174     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, child_env, NULL, &startup, &info), "CreateProcess\n");
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     cmpEnvironment(child_env);
1181
1182     HeapFree(GetProcessHeap(), 0, child_env);
1183     release_memory();
1184     assert(DeleteFileA(resfile) != 0);
1185 }
1186
1187 static  void    test_SuspendFlag(void)
1188 {
1189     char                buffer[MAX_PATH];
1190     PROCESS_INFORMATION info;
1191     STARTUPINFOA       startup, us;
1192     DWORD               exit_status;
1193
1194     /* let's start simplistic */
1195     memset(&startup, 0, sizeof(startup));
1196     startup.cb = sizeof(startup);
1197     startup.dwFlags = STARTF_USESHOWWINDOW;
1198     startup.wShowWindow = SW_SHOWNORMAL;
1199
1200     get_file_name(resfile);
1201     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
1202     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup, &info), "CreateProcess\n");
1203
1204     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
1205     Sleep(8000);
1206     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
1207     ok(ResumeThread(info.hThread) == 1, "Resuming thread\n");
1208
1209     /* wait for child to terminate */
1210     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1211     /* child process has changed result file, so let profile functions know about it */
1212     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1213
1214     GetStartupInfoA(&us);
1215
1216     okChildInt("StartupInfoA", "cb", startup.cb);
1217     okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
1218     ok (startup.lpTitle == NULL || !strcmp(startup.lpTitle, selfname),
1219         "StartupInfoA:lpTitle expected '%s' or null, got '%s'\n", selfname, startup.lpTitle);
1220     okChildInt("StartupInfoA", "dwX", startup.dwX);
1221     okChildInt("StartupInfoA", "dwY", startup.dwY);
1222     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
1223     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
1224     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
1225     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
1226     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
1227     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
1228     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
1229     release_memory();
1230     assert(DeleteFileA(resfile) != 0);
1231 }
1232
1233 static  void    test_DebuggingFlag(void)
1234 {
1235     char                buffer[MAX_PATH];
1236     PROCESS_INFORMATION info;
1237     STARTUPINFOA       startup, us;
1238     DEBUG_EVENT         de;
1239     unsigned            dbg = 0;
1240
1241     /* let's start simplistic */
1242     memset(&startup, 0, sizeof(startup));
1243     startup.cb = sizeof(startup);
1244     startup.dwFlags = STARTF_USESHOWWINDOW;
1245     startup.wShowWindow = SW_SHOWNORMAL;
1246
1247     get_file_name(resfile);
1248     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
1249     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
1250
1251     /* get all startup events up to the entry point break exception */
1252     do 
1253     {
1254         ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
1255         ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
1256         if (de.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) dbg++;
1257     } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
1258
1259     ok(dbg, "I have seen a debug event\n");
1260     /* wait for child to terminate */
1261     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1262     /* child process has changed result file, so let profile functions know about it */
1263     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1264
1265     GetStartupInfoA(&us);
1266
1267     okChildInt("StartupInfoA", "cb", startup.cb);
1268     okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
1269     ok (startup.lpTitle == NULL || !strcmp(startup.lpTitle, selfname),
1270         "StartupInfoA:lpTitle expected '%s' or null, got '%s'\n", selfname, startup.lpTitle);
1271     okChildInt("StartupInfoA", "dwX", startup.dwX);
1272     okChildInt("StartupInfoA", "dwY", startup.dwY);
1273     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
1274     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
1275     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
1276     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
1277     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
1278     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
1279     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
1280     release_memory();
1281     assert(DeleteFileA(resfile) != 0);
1282 }
1283
1284 static BOOL is_console(HANDLE h)
1285 {
1286     return h != INVALID_HANDLE_VALUE && ((ULONG_PTR)h & 3) == 3;
1287 }
1288
1289 static void test_Console(void)
1290 {
1291     char                buffer[MAX_PATH];
1292     PROCESS_INFORMATION info;
1293     STARTUPINFOA       startup, us;
1294     SECURITY_ATTRIBUTES sa;
1295     CONSOLE_SCREEN_BUFFER_INFO  sbi, sbiC;
1296     DWORD               modeIn, modeOut, modeInC, modeOutC;
1297     DWORD               cpIn, cpOut, cpInC, cpOutC;
1298     DWORD               w;
1299     HANDLE              hChildIn, hChildInInh, hChildOut, hChildOutInh, hParentIn, hParentOut;
1300     const char*         msg = "This is a std-handle inheritance test.";
1301     unsigned            msg_len;
1302     BOOL                run_tests = TRUE;
1303
1304     memset(&startup, 0, sizeof(startup));
1305     startup.cb = sizeof(startup);
1306     startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
1307     startup.wShowWindow = SW_SHOWNORMAL;
1308
1309     sa.nLength = sizeof(sa);
1310     sa.lpSecurityDescriptor = NULL;
1311     sa.bInheritHandle = TRUE;
1312
1313     startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1314     startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1315
1316     /* first, we need to be sure we're attached to a console */
1317     if (!is_console(startup.hStdInput) || !is_console(startup.hStdOutput))
1318     {
1319         /* we're not attached to a console, let's do it */
1320         AllocConsole();
1321         startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1322         startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1323     }
1324     /* now verify everything's ok */
1325     ok(startup.hStdInput != INVALID_HANDLE_VALUE, "Opening ConIn\n");
1326     ok(startup.hStdOutput != INVALID_HANDLE_VALUE, "Opening ConOut\n");
1327     startup.hStdError = startup.hStdOutput;
1328
1329     ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbi), "Getting sb info\n");
1330     ok(GetConsoleMode(startup.hStdInput, &modeIn) && 
1331        GetConsoleMode(startup.hStdOutput, &modeOut), "Getting console modes\n");
1332     cpIn = GetConsoleCP();
1333     cpOut = GetConsoleOutputCP();
1334
1335     get_file_name(resfile);
1336     sprintf(buffer, "%s tests/process.c %s console", selfname, resfile);
1337     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
1338
1339     /* wait for child to terminate */
1340     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1341     /* child process has changed result file, so let profile functions know about it */
1342     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1343
1344     /* now get the modification the child has made, and resets parents expected values */
1345     ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbiC), "Getting sb info\n");
1346     ok(GetConsoleMode(startup.hStdInput, &modeInC) && 
1347        GetConsoleMode(startup.hStdOutput, &modeOutC), "Getting console modes\n");
1348
1349     SetConsoleMode(startup.hStdInput, modeIn);
1350     SetConsoleMode(startup.hStdOutput, modeOut);
1351
1352     cpInC = GetConsoleCP();
1353     cpOutC = GetConsoleOutputCP();
1354
1355     /* Try to set invalid CP */
1356     SetLastError(0xdeadbeef);
1357     ok(!SetConsoleCP(0), "Shouldn't succeed\n");
1358     ok(GetLastError()==ERROR_INVALID_PARAMETER ||
1359        broken(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED), /* win9x */
1360        "GetLastError: expecting %u got %u\n",
1361        ERROR_INVALID_PARAMETER, GetLastError());
1362     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1363         run_tests = FALSE;
1364
1365
1366     SetLastError(0xdeadbeef);
1367     ok(!SetConsoleOutputCP(0), "Shouldn't succeed\n");
1368     ok(GetLastError()==ERROR_INVALID_PARAMETER ||
1369        broken(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED), /* win9x */
1370        "GetLastError: expecting %u got %u\n",
1371        ERROR_INVALID_PARAMETER, GetLastError());
1372
1373     SetConsoleCP(cpIn);
1374     SetConsoleOutputCP(cpOut);
1375
1376     GetStartupInfoA(&us);
1377
1378     okChildInt("StartupInfoA", "cb", startup.cb);
1379     okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
1380     ok (startup.lpTitle == NULL || !strcmp(startup.lpTitle, selfname),
1381         "StartupInfoA:lpTitle expected '%s' or null, got '%s'\n", selfname, startup.lpTitle);
1382     okChildInt("StartupInfoA", "dwX", startup.dwX);
1383     okChildInt("StartupInfoA", "dwY", startup.dwY);
1384     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
1385     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
1386     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
1387     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
1388     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
1389     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
1390     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
1391
1392     /* check child correctly inherited the console */
1393     okChildInt("StartupInfoA", "hStdInput", (DWORD_PTR)startup.hStdInput);
1394     okChildInt("StartupInfoA", "hStdOutput", (DWORD_PTR)startup.hStdOutput);
1395     okChildInt("StartupInfoA", "hStdError", (DWORD_PTR)startup.hStdError);
1396     okChildInt("Console", "SizeX", (DWORD)sbi.dwSize.X);
1397     okChildInt("Console", "SizeY", (DWORD)sbi.dwSize.Y);
1398     okChildInt("Console", "CursorX", (DWORD)sbi.dwCursorPosition.X);
1399     okChildInt("Console", "CursorY", (DWORD)sbi.dwCursorPosition.Y);
1400     okChildInt("Console", "Attributes", sbi.wAttributes);
1401     okChildInt("Console", "winLeft", (DWORD)sbi.srWindow.Left);
1402     okChildInt("Console", "winTop", (DWORD)sbi.srWindow.Top);
1403     okChildInt("Console", "winRight", (DWORD)sbi.srWindow.Right);
1404     okChildInt("Console", "winBottom", (DWORD)sbi.srWindow.Bottom);
1405     okChildInt("Console", "maxWinWidth", (DWORD)sbi.dwMaximumWindowSize.X);
1406     okChildInt("Console", "maxWinHeight", (DWORD)sbi.dwMaximumWindowSize.Y);
1407     okChildInt("Console", "InputCP", cpIn);
1408     okChildInt("Console", "OutputCP", cpOut);
1409     okChildInt("Console", "InputMode", modeIn);
1410     okChildInt("Console", "OutputMode", modeOut);
1411
1412     if (run_tests)
1413     {
1414         ok(cpInC == 1252, "Wrong console CP (expected 1252 got %d/%d)\n", cpInC, cpIn);
1415         ok(cpOutC == 1252, "Wrong console-SB CP (expected 1252 got %d/%d)\n", cpOutC, cpOut);
1416     }
1417     else
1418         win_skip("Setting the codepage is not implemented\n");
1419
1420     ok(modeInC == (modeIn ^ 1), "Wrong console mode\n");
1421     ok(modeOutC == (modeOut ^ 1), "Wrong console-SB mode\n");
1422     trace("cursor position(X): %d/%d\n",sbi.dwCursorPosition.X, sbiC.dwCursorPosition.X);
1423     ok(sbiC.dwCursorPosition.Y == (sbi.dwCursorPosition.Y ^ 1), "Wrong cursor position\n");
1424
1425     release_memory();
1426     assert(DeleteFileA(resfile) != 0);
1427
1428     ok(CreatePipe(&hParentIn, &hChildOut, NULL, 0), "Creating parent-input pipe\n");
1429     ok(DuplicateHandle(GetCurrentProcess(), hChildOut, GetCurrentProcess(), 
1430                        &hChildOutInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
1431        "Duplicating as inheritable child-output pipe\n");
1432     CloseHandle(hChildOut);
1433  
1434     ok(CreatePipe(&hChildIn, &hParentOut, NULL, 0), "Creating parent-output pipe\n");
1435     ok(DuplicateHandle(GetCurrentProcess(), hChildIn, GetCurrentProcess(), 
1436                        &hChildInInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
1437        "Duplicating as inheritable child-input pipe\n");
1438     CloseHandle(hChildIn); 
1439     
1440     memset(&startup, 0, sizeof(startup));
1441     startup.cb = sizeof(startup);
1442     startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
1443     startup.wShowWindow = SW_SHOWNORMAL;
1444     startup.hStdInput = hChildInInh;
1445     startup.hStdOutput = hChildOutInh;
1446     startup.hStdError = hChildOutInh;
1447
1448     get_file_name(resfile);
1449     sprintf(buffer, "%s tests/process.c %s stdhandle", selfname, resfile);
1450     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
1451     ok(CloseHandle(hChildInInh), "Closing handle\n");
1452     ok(CloseHandle(hChildOutInh), "Closing handle\n");
1453
1454     msg_len = strlen(msg) + 1;
1455     ok(WriteFile(hParentOut, msg, msg_len, &w, NULL), "Writing to child\n");
1456     ok(w == msg_len, "Should have written %u bytes, actually wrote %u\n", msg_len, w);
1457     memset(buffer, 0, sizeof(buffer));
1458     ok(ReadFile(hParentIn, buffer, sizeof(buffer), &w, NULL), "Reading from child\n");
1459     ok(strcmp(buffer, msg) == 0, "Should have received '%s'\n", msg);
1460
1461     /* wait for child to terminate */
1462     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1463     /* child process has changed result file, so let profile functions know about it */
1464     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1465
1466     okChildString("StdHandle", "msg", msg);
1467
1468     release_memory();
1469     assert(DeleteFileA(resfile) != 0);
1470 }
1471
1472 static  void    test_ExitCode(void)
1473 {
1474     char                buffer[MAX_PATH];
1475     PROCESS_INFORMATION info;
1476     STARTUPINFOA        startup;
1477     DWORD               code;
1478
1479     /* let's start simplistic */
1480     memset(&startup, 0, sizeof(startup));
1481     startup.cb = sizeof(startup);
1482     startup.dwFlags = STARTF_USESHOWWINDOW;
1483     startup.wShowWindow = SW_SHOWNORMAL;
1484
1485     get_file_name(resfile);
1486     sprintf(buffer, "%s tests/process.c %s exit_code", selfname, resfile);
1487     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
1488
1489     /* wait for child to terminate */
1490     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1491     /* child process has changed result file, so let profile functions know about it */
1492     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1493
1494     ok(GetExitCodeProcess(info.hProcess, &code), "Getting exit code\n");
1495     okChildInt("ExitCode", "value", code);
1496
1497     release_memory();
1498     assert(DeleteFileA(resfile) != 0);
1499 }
1500
1501 static void test_OpenProcess(void)
1502 {
1503     HANDLE hproc;
1504     void *addr1;
1505     MEMORY_BASIC_INFORMATION info;
1506     SIZE_T dummy, read_bytes;
1507
1508     /* not exported in all windows versions */
1509     if ((!pVirtualAllocEx) || (!pVirtualFreeEx)) {
1510         win_skip("VirtualAllocEx not found\n");
1511         return;
1512     }
1513
1514     /* without PROCESS_VM_OPERATION */
1515     hproc = OpenProcess(PROCESS_ALL_ACCESS & ~PROCESS_VM_OPERATION, FALSE, GetCurrentProcessId());
1516     ok(hproc != NULL, "OpenProcess error %d\n", GetLastError());
1517
1518     SetLastError(0xdeadbeef);
1519     addr1 = pVirtualAllocEx(hproc, 0, 0xFFFC, MEM_RESERVE, PAGE_NOACCESS);
1520     ok(!addr1, "VirtualAllocEx should fail\n");
1521     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1522     {   /* Win9x */
1523         CloseHandle(hproc);
1524         win_skip("VirtualAllocEx not implemented\n");
1525         return;
1526     }
1527     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1528
1529     read_bytes = 0xdeadbeef;
1530     SetLastError(0xdeadbeef);
1531     ok(ReadProcessMemory(hproc, test_OpenProcess, &dummy, sizeof(dummy), &read_bytes),
1532        "ReadProcessMemory error %d\n", GetLastError());
1533     ok(read_bytes == sizeof(dummy), "wrong read bytes %ld\n", read_bytes);
1534
1535     CloseHandle(hproc);
1536
1537     hproc = OpenProcess(PROCESS_VM_OPERATION, FALSE, GetCurrentProcessId());
1538     ok(hproc != NULL, "OpenProcess error %d\n", GetLastError());
1539
1540     addr1 = pVirtualAllocEx(hproc, 0, 0xFFFC, MEM_RESERVE, PAGE_NOACCESS);
1541     ok(addr1 != NULL, "VirtualAllocEx error %d\n", GetLastError());
1542
1543     /* without PROCESS_QUERY_INFORMATION */
1544     SetLastError(0xdeadbeef);
1545     ok(!VirtualQueryEx(hproc, addr1, &info, sizeof(info)),
1546        "VirtualQueryEx without PROCESS_QUERY_INFORMATION rights should fail\n");
1547     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1548
1549     /* without PROCESS_VM_READ */
1550     read_bytes = 0xdeadbeef;
1551     SetLastError(0xdeadbeef);
1552     ok(!ReadProcessMemory(hproc, addr1, &dummy, sizeof(dummy), &read_bytes),
1553        "ReadProcessMemory without PROCESS_VM_READ rights should fail\n");
1554     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1555     ok(read_bytes == 0, "wrong read bytes %ld\n", read_bytes);
1556
1557     CloseHandle(hproc);
1558
1559     hproc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
1560
1561     memset(&info, 0xcc, sizeof(info));
1562     ok(VirtualQueryEx(hproc, addr1, &info, sizeof(info)) == sizeof(info),
1563        "VirtualQueryEx error %d\n", GetLastError());
1564
1565     ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress, addr1);
1566     ok(info.AllocationBase == addr1, "%p != %p\n", info.AllocationBase, addr1);
1567     ok(info.AllocationProtect == PAGE_NOACCESS, "%x != PAGE_NOACCESS\n", info.AllocationProtect);
1568     ok(info.RegionSize == 0x10000, "%lx != 0x10000\n", info.RegionSize);
1569     ok(info.State == MEM_RESERVE, "%x != MEM_RESERVE\n", info.State);
1570     /* NT reports Protect == 0 for a not committed memory block */
1571     ok(info.Protect == 0 /* NT */ ||
1572        info.Protect == PAGE_NOACCESS, /* Win9x */
1573         "%x != PAGE_NOACCESS\n", info.Protect);
1574     ok(info.Type == MEM_PRIVATE, "%x != MEM_PRIVATE\n", info.Type);
1575
1576     SetLastError(0xdeadbeef);
1577     ok(!pVirtualFreeEx(hproc, addr1, 0, MEM_RELEASE),
1578        "VirtualFreeEx without PROCESS_VM_OPERATION rights should fail\n");
1579     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1580
1581     CloseHandle(hproc);
1582
1583     ok(VirtualFree(addr1, 0, MEM_RELEASE), "VirtualFree failed\n");
1584 }
1585
1586 static void test_GetProcessVersion(void)
1587 {
1588     static char cmdline[] = "winver.exe";
1589     PROCESS_INFORMATION pi;
1590     STARTUPINFOA si;
1591     DWORD ret;
1592
1593     SetLastError(0xdeadbeef);
1594     ret = GetProcessVersion(0);
1595     ok(ret, "GetProcessVersion error %u\n", GetLastError());
1596
1597     SetLastError(0xdeadbeef);
1598     ret = GetProcessVersion(GetCurrentProcessId());
1599     ok(ret, "GetProcessVersion error %u\n", GetLastError());
1600
1601     memset(&si, 0, sizeof(si));
1602     si.cb = sizeof(si);
1603     si.dwFlags = STARTF_USESHOWWINDOW;
1604     si.wShowWindow = SW_HIDE;
1605     ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
1606     SetLastError(0xdeadbeef);
1607     ok(ret, "CreateProcess error %u\n", GetLastError());
1608
1609     SetLastError(0xdeadbeef);
1610     ret = GetProcessVersion(pi.dwProcessId);
1611     ok(ret, "GetProcessVersion error %u\n", GetLastError());
1612
1613     SetLastError(0xdeadbeef);
1614     ret = TerminateProcess(pi.hProcess, 0);
1615     ok(ret, "TerminateProcess error %u\n", GetLastError());
1616
1617     CloseHandle(pi.hProcess);
1618     CloseHandle(pi.hThread);
1619 }
1620
1621 static void test_ProcessName(void)
1622 {
1623     HANDLE hSelf;
1624     WCHAR module_name[1024];
1625     WCHAR deviceW[] = {'\\','D', 'e','v','i','c','e',0};
1626     WCHAR buf[1024];
1627     DWORD size;
1628
1629     if (!pQueryFullProcessImageNameW)
1630     {
1631         win_skip("QueryFullProcessImageNameW unavailable (added in Windows Vista)\n");
1632         return;
1633     }
1634
1635     ok(GetModuleFileNameW(NULL, module_name, 1024), "GetModuleFileNameW(NULL, ...) failed\n");
1636
1637     /* GetCurrentProcess pseudo-handle */
1638     size = sizeof(buf) / sizeof(buf[0]);
1639     expect_eq_d(TRUE, pQueryFullProcessImageNameW(GetCurrentProcess(), 0, buf, &size));
1640     expect_eq_d(lstrlenW(buf), size);
1641     expect_eq_ws_i(buf, module_name);
1642
1643     hSelf = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
1644     /* Real handle */
1645     size = sizeof(buf) / sizeof(buf[0]);
1646     expect_eq_d(TRUE, pQueryFullProcessImageNameW(hSelf, 0, buf, &size));
1647     expect_eq_d(lstrlenW(buf), size);
1648     expect_eq_ws_i(buf, module_name);
1649
1650     /* Buffer too small */
1651     size = lstrlenW(module_name)/2;
1652     lstrcpyW(buf, deviceW);
1653     SetLastError(0xdeadbeef);
1654     expect_eq_d(FALSE, pQueryFullProcessImageNameW(hSelf, 0, buf, &size));
1655     expect_eq_d(lstrlenW(module_name)/2, size);  /* size not changed(!) */
1656     expect_eq_d(ERROR_INSUFFICIENT_BUFFER, GetLastError());
1657     expect_eq_ws_i(deviceW, buf);  /* buffer not changed */
1658
1659     /* Too small - not space for NUL terminator */
1660     size = lstrlenW(module_name);
1661     SetLastError(0xdeadbeef);
1662     expect_eq_d(FALSE, pQueryFullProcessImageNameW(hSelf, 0, buf, &size));
1663     expect_eq_d(lstrlenW(module_name), size);  /* size not changed(!) */
1664     expect_eq_d(ERROR_INSUFFICIENT_BUFFER, GetLastError());
1665
1666     /* NULL buffer */
1667     size = 0;
1668     expect_eq_d(FALSE, pQueryFullProcessImageNameW(hSelf, 0, NULL, &size));
1669     expect_eq_d(0, size);
1670     expect_eq_d(ERROR_INSUFFICIENT_BUFFER, GetLastError());
1671
1672     /* native path */
1673     size = sizeof(buf) / sizeof(buf[0]);
1674     expect_eq_d(TRUE, pQueryFullProcessImageNameW(hSelf, PROCESS_NAME_NATIVE, buf, &size));
1675     expect_eq_d(lstrlenW(buf), size);
1676     ok(buf[0] == '\\', "NT path should begin with '\\'\n");
1677     todo_wine ok(memcmp(buf, deviceW, sizeof(WCHAR)*lstrlenW(deviceW)) == 0, "NT path should begin with \\Device\n");
1678
1679     /* Buffer too small */
1680     size = lstrlenW(module_name)/2;
1681     SetLastError(0xdeadbeef);
1682     lstrcpyW(buf, module_name);
1683     expect_eq_d(FALSE, pQueryFullProcessImageNameW(hSelf, 0, buf, &size));
1684     expect_eq_d(lstrlenW(module_name)/2, size);  /* size not changed(!) */
1685     expect_eq_d(ERROR_INSUFFICIENT_BUFFER, GetLastError());
1686     expect_eq_ws_i(module_name, buf);  /* buffer not changed */
1687
1688     CloseHandle(hSelf);
1689 }
1690
1691 static void test_Handles(void)
1692 {
1693     HANDLE handle = GetCurrentProcess();
1694     BOOL ret;
1695     DWORD code;
1696
1697     ok( handle == (HANDLE)~(ULONG_PTR)0 ||
1698         handle == (HANDLE)(ULONG_PTR)0x7fffffff /* win9x */,
1699         "invalid current process handle %p\n", handle );
1700     ret = GetExitCodeProcess( handle, &code );
1701     ok( ret, "GetExitCodeProcess failed err %u\n", GetLastError() );
1702 #ifdef _WIN64
1703     /* truncated handle */
1704     SetLastError( 0xdeadbeef );
1705     handle = (HANDLE)((ULONG_PTR)handle & ~0u);
1706     ret = GetExitCodeProcess( handle, &code );
1707     ok( !ret, "GetExitCodeProcess succeeded for %p\n", handle );
1708     ok( GetLastError() == ERROR_INVALID_HANDLE, "wrong error %u\n", GetLastError() );
1709     /* sign-extended handle */
1710     SetLastError( 0xdeadbeef );
1711     handle = (HANDLE)((LONG_PTR)(int)(ULONG_PTR)handle);
1712     ret = GetExitCodeProcess( handle, &code );
1713     ok( ret, "GetExitCodeProcess failed err %u\n", GetLastError() );
1714     /* invalid high-word */
1715     SetLastError( 0xdeadbeef );
1716     handle = (HANDLE)(((ULONG_PTR)handle & ~0u) + ((ULONG_PTR)1 << 32));
1717     ret = GetExitCodeProcess( handle, &code );
1718     ok( !ret, "GetExitCodeProcess succeeded for %p\n", handle );
1719     ok( GetLastError() == ERROR_INVALID_HANDLE, "wrong error %u\n", GetLastError() );
1720 #endif
1721 }
1722
1723 START_TEST(process)
1724 {
1725     int b = init();
1726     ok(b, "Basic init of CreateProcess test\n");
1727     if (!b) return;
1728
1729     if (myARGC >= 3)
1730     {
1731         doChild(myARGV[2], (myARGC == 3) ? NULL : myARGV[3]);
1732         return;
1733     }
1734     test_Startup();
1735     test_CommandLine();
1736     test_Directory();
1737     test_Environment();
1738     test_SuspendFlag();
1739     test_DebuggingFlag();
1740     test_Console();
1741     test_ExitCode();
1742     test_OpenProcess();
1743     test_GetProcessVersion();
1744     test_ProcessName();
1745     test_Handles();
1746     /* things that can be tested:
1747      *  lookup:         check the way program to be executed is searched
1748      *  handles:        check the handle inheritance stuff (+sec options)
1749      *  console:        check if console creation parameters work
1750      */
1751 }