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