kernel32/tests: Fix the thread test to cope with the Win95 InterlockedIncrement behavior.
[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[] = "I'm the desktop string",
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     assert (p);
858     /* Use exename to avoid buffer containing things like 'C:' */
859     sprintf(buffer, "..%s/%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", p, 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     sprintf(buffer, "..%s/%s", p, exename);
868     okChildString("Arguments", "argvA0", buffer);
869     release_memory();
870     assert(DeleteFileA(resfile) != 0);
871
872     /* Using AppName */
873     get_file_name(resfile);
874     len = GetFullPathNameA(selfname, MAX_PATH, fullpath, &lpFilePart);
875     assert ( lpFilePart != 0);
876     *(lpFilePart -1 ) = 0;
877     p = strrchr(fullpath, '\\');
878     assert (p);
879     /* Use exename to avoid buffer containing things like 'C:' */
880     sprintf(buffer, "..%s/%s", p, exename);
881     sprintf(buffer2, "dummy tests/process.c %s \"a\\\"b\\\\\" c\\\" d", resfile);
882     SetLastError(0xdeadbeef);
883     ret = CreateProcessA(buffer, buffer2, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info);
884     ok(ret, "CreateProcess (%s) failed : %d\n", buffer, GetLastError());
885     /* wait for child to terminate */
886     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
887     /* child process has changed result file, so let profile functions know about it */
888     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
889     sprintf(buffer, "tests/process.c %s", resfile);
890     okChildString("Arguments", "argvA0", "dummy");
891     okChildString("Arguments", "CommandLineA", buffer2);
892     okChildStringWA("Arguments", "CommandLineW", buffer2);
893     release_memory();
894     assert(DeleteFileA(resfile) != 0);
895 }
896
897 static void test_Directory(void)
898 {
899     char                buffer[MAX_PATH];
900     PROCESS_INFORMATION info;
901     STARTUPINFOA        startup;
902     char windir[MAX_PATH];
903     static CHAR cmdline[] = "winver.exe";
904
905     memset(&startup, 0, sizeof(startup));
906     startup.cb = sizeof(startup);
907     startup.dwFlags = STARTF_USESHOWWINDOW;
908     startup.wShowWindow = SW_SHOWNORMAL;
909
910     /* the basics */
911     get_file_name(resfile);
912     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
913     GetWindowsDirectoryA( windir, sizeof(windir) );
914     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, windir, &startup, &info), "CreateProcess\n");
915     /* wait for child to terminate */
916     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
917     /* child process has changed result file, so let profile functions know about it */
918     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
919
920     okChildIString("Misc", "CurrDirA", windir);
921     release_memory();
922     assert(DeleteFileA(resfile) != 0);
923
924     /* search PATH for the exe if directory is NULL */
925     ok(CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
926     ok(TerminateProcess(info.hProcess, 0), "Child process termination\n");
927
928     /* if any directory is provided, don't search PATH, error on bad directory */
929     SetLastError(0xdeadbeef);
930     memset(&info, 0, sizeof(info));
931     ok(!CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0L,
932                        NULL, "non\\existent\\directory", &startup, &info), "CreateProcess\n");
933     ok(GetLastError() == ERROR_DIRECTORY, "Expected ERROR_DIRECTORY, got %d\n", GetLastError());
934     ok(!TerminateProcess(info.hProcess, 0), "Child process should not exist\n");
935 }
936
937 static BOOL is_str_env_drive_dir(const char* str)
938 {
939     return str[0] == '=' && str[1] >= 'A' && str[1] <= 'Z' && str[2] == ':' &&
940         str[3] == '=' && str[4] == str[1];
941 }
942
943 /* compared expected child's environment (in gesA) from actual
944  * environment our child got
945  */
946 static void cmpEnvironment(const char* gesA)
947 {
948     int                 i, clen;
949     const char*         ptrA;
950     char*               res;
951     char                key[32];
952     BOOL                found;
953
954     clen = GetPrivateProfileIntA("EnvironmentA", "len", 0, resfile);
955     
956     /* now look each parent env in child */
957     if ((ptrA = gesA) != NULL)
958     {
959         while (*ptrA)
960         {
961             for (i = 0; i < clen; i++)
962             {
963                 sprintf(key, "env%d", i);
964                 res = getChildString("EnvironmentA", key);
965                 if (strncmp(ptrA, res, MAX_LISTED_ENV_VAR - 1) == 0)
966                     break;
967             }
968             found = i < clen;
969             ok(found, "Parent-env string %s isn't in child process\n", ptrA);
970             
971             ptrA += strlen(ptrA) + 1;
972             release_memory();
973         }
974     }
975     /* and each child env in parent */
976     for (i = 0; i < clen; i++)
977     {
978         sprintf(key, "env%d", i);
979         res = getChildString("EnvironmentA", key);
980         if ((ptrA = gesA) != NULL)
981         {
982             while (*ptrA)
983             {
984                 if (strncmp(res, ptrA, MAX_LISTED_ENV_VAR - 1) == 0)
985                     break;
986                 ptrA += strlen(ptrA) + 1;
987             }
988             if (!*ptrA) ptrA = NULL;
989         }
990
991         if (!is_str_env_drive_dir(res))
992         {
993             found = ptrA != NULL;
994             ok(found, "Child-env string %s isn't in parent process\n", res);
995         }
996         /* else => should also test we get the right per drive default directory here... */
997     }
998 }
999
1000 static void test_Environment(void)
1001 {
1002     char                buffer[MAX_PATH];
1003     PROCESS_INFORMATION info;
1004     STARTUPINFOA        startup;
1005     char*               child_env;
1006     int                 child_env_len;
1007     char*               ptr;
1008     char*               env;
1009     int                 slen;
1010
1011     memset(&startup, 0, sizeof(startup));
1012     startup.cb = sizeof(startup);
1013     startup.dwFlags = STARTF_USESHOWWINDOW;
1014     startup.wShowWindow = SW_SHOWNORMAL;
1015
1016     /* the basics */
1017     get_file_name(resfile);
1018     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
1019     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
1020     /* wait for child to terminate */
1021     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1022     /* child process has changed result file, so let profile functions know about it */
1023     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1024     
1025     cmpEnvironment(GetEnvironmentStringsA());
1026     release_memory();
1027     assert(DeleteFileA(resfile) != 0);
1028
1029     memset(&startup, 0, sizeof(startup));
1030     startup.cb = sizeof(startup);
1031     startup.dwFlags = STARTF_USESHOWWINDOW;
1032     startup.wShowWindow = SW_SHOWNORMAL;
1033
1034     /* the basics */
1035     get_file_name(resfile);
1036     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
1037     
1038     child_env_len = 0;
1039     ptr = GetEnvironmentStringsA();
1040     while(*ptr)
1041     {
1042         slen = strlen(ptr)+1;
1043         child_env_len += slen;
1044         ptr += slen;
1045     }
1046     /* Add space for additional environment variables */
1047     child_env_len += 256;
1048     child_env = HeapAlloc(GetProcessHeap(), 0, child_env_len);
1049     
1050     ptr = child_env;
1051     sprintf(ptr, "=%c:=%s", 'C', "C:\\FOO\\BAR");
1052     ptr += strlen(ptr) + 1;
1053     strcpy(ptr, "PATH=C:\\WINDOWS;C:\\WINDOWS\\SYSTEM;C:\\MY\\OWN\\DIR");
1054     ptr += strlen(ptr) + 1;
1055     strcpy(ptr, "FOO=BAR");
1056     ptr += strlen(ptr) + 1;
1057     strcpy(ptr, "BAR=FOOBAR");
1058     ptr += strlen(ptr) + 1;
1059     /* copy all existing variables except:
1060      * - WINELOADER
1061      * - PATH (already set above)
1062      * - the directory definitions (=[A-Z]:=)
1063      */
1064     for (env = GetEnvironmentStringsA(); *env; env += strlen(env) + 1)
1065     {
1066         if (strncmp(env, "PATH=", 5) != 0 &&
1067             strncmp(env, "WINELOADER=", 11) != 0 &&
1068             !is_str_env_drive_dir(env))
1069         {
1070             strcpy(ptr, env);
1071             ptr += strlen(ptr) + 1;
1072         }
1073     }
1074     *ptr = '\0';
1075     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, child_env, NULL, &startup, &info), "CreateProcess\n");
1076     /* wait for child to terminate */
1077     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1078     /* child process has changed result file, so let profile functions know about it */
1079     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1080     
1081     cmpEnvironment(child_env);
1082
1083     HeapFree(GetProcessHeap(), 0, child_env);
1084     release_memory();
1085     assert(DeleteFileA(resfile) != 0);
1086 }
1087
1088 static  void    test_SuspendFlag(void)
1089 {
1090     char                buffer[MAX_PATH];
1091     PROCESS_INFORMATION info;
1092     STARTUPINFOA       startup, us;
1093     DWORD               exit_status;
1094
1095     /* let's start simplistic */
1096     memset(&startup, 0, sizeof(startup));
1097     startup.cb = sizeof(startup);
1098     startup.dwFlags = STARTF_USESHOWWINDOW;
1099     startup.wShowWindow = SW_SHOWNORMAL;
1100
1101     get_file_name(resfile);
1102     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
1103     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup, &info), "CreateProcess\n");
1104
1105     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
1106     Sleep(8000);
1107     ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
1108     ok(ResumeThread(info.hThread) == 1, "Resuming thread\n");
1109
1110     /* wait for child to terminate */
1111     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1112     /* child process has changed result file, so let profile functions know about it */
1113     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1114
1115     GetStartupInfoA(&us);
1116
1117     okChildInt("StartupInfoA", "cb", startup.cb);
1118     okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
1119     ok (startup.lpTitle == NULL || !strcmp(startup.lpTitle, selfname),
1120         "StartupInfoA:lpTitle expected '%s' or null, got '%s'\n", selfname, startup.lpTitle);
1121     okChildInt("StartupInfoA", "dwX", startup.dwX);
1122     okChildInt("StartupInfoA", "dwY", startup.dwY);
1123     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
1124     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
1125     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
1126     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
1127     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
1128     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
1129     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
1130     release_memory();
1131     assert(DeleteFileA(resfile) != 0);
1132 }
1133
1134 static  void    test_DebuggingFlag(void)
1135 {
1136     char                buffer[MAX_PATH];
1137     PROCESS_INFORMATION info;
1138     STARTUPINFOA       startup, us;
1139     DEBUG_EVENT         de;
1140     unsigned            dbg = 0;
1141
1142     /* let's start simplistic */
1143     memset(&startup, 0, sizeof(startup));
1144     startup.cb = sizeof(startup);
1145     startup.dwFlags = STARTF_USESHOWWINDOW;
1146     startup.wShowWindow = SW_SHOWNORMAL;
1147
1148     get_file_name(resfile);
1149     sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
1150     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
1151
1152     /* get all startup events up to the entry point break exception */
1153     do 
1154     {
1155         ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
1156         ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
1157         if (de.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) dbg++;
1158     } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
1159
1160     ok(dbg, "I have seen a debug event\n");
1161     /* wait for child to terminate */
1162     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1163     /* child process has changed result file, so let profile functions know about it */
1164     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1165
1166     GetStartupInfoA(&us);
1167
1168     okChildInt("StartupInfoA", "cb", startup.cb);
1169     okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
1170     ok (startup.lpTitle == NULL || !strcmp(startup.lpTitle, selfname),
1171         "StartupInfoA:lpTitle expected '%s' or null, got '%s'\n", selfname, startup.lpTitle);
1172     okChildInt("StartupInfoA", "dwX", startup.dwX);
1173     okChildInt("StartupInfoA", "dwY", startup.dwY);
1174     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
1175     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
1176     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
1177     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
1178     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
1179     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
1180     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
1181     release_memory();
1182     assert(DeleteFileA(resfile) != 0);
1183 }
1184
1185 static BOOL is_console(HANDLE h)
1186 {
1187     return h != INVALID_HANDLE_VALUE && ((ULONG_PTR)h & 3) == 3;
1188 }
1189
1190 static void test_Console(void)
1191 {
1192     char                buffer[MAX_PATH];
1193     PROCESS_INFORMATION info;
1194     STARTUPINFOA       startup, us;
1195     SECURITY_ATTRIBUTES sa;
1196     CONSOLE_SCREEN_BUFFER_INFO  sbi, sbiC;
1197     DWORD               modeIn, modeOut, modeInC, modeOutC;
1198     DWORD               cpIn, cpOut, cpInC, cpOutC;
1199     DWORD               w;
1200     HANDLE              hChildIn, hChildInInh, hChildOut, hChildOutInh, hParentIn, hParentOut;
1201     const char*         msg = "This is a std-handle inheritance test.";
1202     unsigned            msg_len;
1203     BOOL                run_tests = TRUE;
1204
1205     memset(&startup, 0, sizeof(startup));
1206     startup.cb = sizeof(startup);
1207     startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
1208     startup.wShowWindow = SW_SHOWNORMAL;
1209
1210     sa.nLength = sizeof(sa);
1211     sa.lpSecurityDescriptor = NULL;
1212     sa.bInheritHandle = TRUE;
1213
1214     startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1215     startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1216
1217     /* first, we need to be sure we're attached to a console */
1218     if (!is_console(startup.hStdInput) || !is_console(startup.hStdOutput))
1219     {
1220         /* we're not attached to a console, let's do it */
1221         AllocConsole();
1222         startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1223         startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
1224     }
1225     /* now verify everything's ok */
1226     ok(startup.hStdInput != INVALID_HANDLE_VALUE, "Opening ConIn\n");
1227     ok(startup.hStdOutput != INVALID_HANDLE_VALUE, "Opening ConOut\n");
1228     startup.hStdError = startup.hStdOutput;
1229
1230     ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbi), "Getting sb info\n");
1231     ok(GetConsoleMode(startup.hStdInput, &modeIn) && 
1232        GetConsoleMode(startup.hStdOutput, &modeOut), "Getting console modes\n");
1233     cpIn = GetConsoleCP();
1234     cpOut = GetConsoleOutputCP();
1235
1236     get_file_name(resfile);
1237     sprintf(buffer, "%s tests/process.c %s console", selfname, resfile);
1238     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
1239
1240     /* wait for child to terminate */
1241     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1242     /* child process has changed result file, so let profile functions know about it */
1243     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1244
1245     /* now get the modification the child has made, and resets parents expected values */
1246     ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbiC), "Getting sb info\n");
1247     ok(GetConsoleMode(startup.hStdInput, &modeInC) && 
1248        GetConsoleMode(startup.hStdOutput, &modeOutC), "Getting console modes\n");
1249
1250     SetConsoleMode(startup.hStdInput, modeIn);
1251     SetConsoleMode(startup.hStdOutput, modeOut);
1252
1253     cpInC = GetConsoleCP();
1254     cpOutC = GetConsoleOutputCP();
1255
1256     /* Try to set invalid CP */
1257     SetLastError(0xdeadbeef);
1258     ok(!SetConsoleCP(0), "Shouldn't succeed\n");
1259     ok(GetLastError()==ERROR_INVALID_PARAMETER ||
1260        broken(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED), /* win9x */
1261        "GetLastError: expecting %u got %u\n",
1262        ERROR_INVALID_PARAMETER, GetLastError());
1263     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1264         run_tests = FALSE;
1265
1266
1267     SetLastError(0xdeadbeef);
1268     ok(!SetConsoleOutputCP(0), "Shouldn't succeed\n");
1269     ok(GetLastError()==ERROR_INVALID_PARAMETER ||
1270        broken(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED), /* win9x */
1271        "GetLastError: expecting %u got %u\n",
1272        ERROR_INVALID_PARAMETER, GetLastError());
1273
1274     SetConsoleCP(cpIn);
1275     SetConsoleOutputCP(cpOut);
1276
1277     GetStartupInfoA(&us);
1278
1279     okChildInt("StartupInfoA", "cb", startup.cb);
1280     okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
1281     ok (startup.lpTitle == NULL || !strcmp(startup.lpTitle, selfname),
1282         "StartupInfoA:lpTitle expected '%s' or null, got '%s'\n", selfname, startup.lpTitle);
1283     okChildInt("StartupInfoA", "dwX", startup.dwX);
1284     okChildInt("StartupInfoA", "dwY", startup.dwY);
1285     okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
1286     okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
1287     okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
1288     okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
1289     okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
1290     okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
1291     okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
1292
1293     /* check child correctly inherited the console */
1294     okChildInt("StartupInfoA", "hStdInput", (DWORD)startup.hStdInput);
1295     okChildInt("StartupInfoA", "hStdOutput", (DWORD)startup.hStdOutput);
1296     okChildInt("StartupInfoA", "hStdError", (DWORD)startup.hStdError);
1297     okChildInt("Console", "SizeX", (DWORD)sbi.dwSize.X);
1298     okChildInt("Console", "SizeY", (DWORD)sbi.dwSize.Y);
1299     okChildInt("Console", "CursorX", (DWORD)sbi.dwCursorPosition.X);
1300     okChildInt("Console", "CursorY", (DWORD)sbi.dwCursorPosition.Y);
1301     okChildInt("Console", "Attributes", sbi.wAttributes);
1302     okChildInt("Console", "winLeft", (DWORD)sbi.srWindow.Left);
1303     okChildInt("Console", "winTop", (DWORD)sbi.srWindow.Top);
1304     okChildInt("Console", "winRight", (DWORD)sbi.srWindow.Right);
1305     okChildInt("Console", "winBottom", (DWORD)sbi.srWindow.Bottom);
1306     okChildInt("Console", "maxWinWidth", (DWORD)sbi.dwMaximumWindowSize.X);
1307     okChildInt("Console", "maxWinHeight", (DWORD)sbi.dwMaximumWindowSize.Y);
1308     okChildInt("Console", "InputCP", cpIn);
1309     okChildInt("Console", "OutputCP", cpOut);
1310     okChildInt("Console", "InputMode", modeIn);
1311     okChildInt("Console", "OutputMode", modeOut);
1312
1313     if (run_tests)
1314     {
1315         ok(cpInC == 1252, "Wrong console CP (expected 1252 got %d/%d)\n", cpInC, cpIn);
1316         ok(cpOutC == 1252, "Wrong console-SB CP (expected 1252 got %d/%d)\n", cpOutC, cpOut);
1317     }
1318     else
1319         win_skip("Setting the codepage is not implemented\n");
1320
1321     ok(modeInC == (modeIn ^ 1), "Wrong console mode\n");
1322     ok(modeOutC == (modeOut ^ 1), "Wrong console-SB mode\n");
1323     trace("cursor position(X): %d/%d\n",sbi.dwCursorPosition.X, sbiC.dwCursorPosition.X);
1324     ok(sbiC.dwCursorPosition.Y == (sbi.dwCursorPosition.Y ^ 1), "Wrong cursor position\n");
1325
1326     release_memory();
1327     assert(DeleteFileA(resfile) != 0);
1328
1329     ok(CreatePipe(&hParentIn, &hChildOut, NULL, 0), "Creating parent-input pipe\n");
1330     ok(DuplicateHandle(GetCurrentProcess(), hChildOut, GetCurrentProcess(), 
1331                        &hChildOutInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
1332        "Duplicating as inheritable child-output pipe\n");
1333     CloseHandle(hChildOut);
1334  
1335     ok(CreatePipe(&hChildIn, &hParentOut, NULL, 0), "Creating parent-output pipe\n");
1336     ok(DuplicateHandle(GetCurrentProcess(), hChildIn, GetCurrentProcess(), 
1337                        &hChildInInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
1338        "Duplicating as inheritable child-input pipe\n");
1339     CloseHandle(hChildIn); 
1340     
1341     memset(&startup, 0, sizeof(startup));
1342     startup.cb = sizeof(startup);
1343     startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
1344     startup.wShowWindow = SW_SHOWNORMAL;
1345     startup.hStdInput = hChildInInh;
1346     startup.hStdOutput = hChildOutInh;
1347     startup.hStdError = hChildOutInh;
1348
1349     get_file_name(resfile);
1350     sprintf(buffer, "%s tests/process.c %s stdhandle", selfname, resfile);
1351     ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
1352     ok(CloseHandle(hChildInInh), "Closing handle\n");
1353     ok(CloseHandle(hChildOutInh), "Closing handle\n");
1354
1355     msg_len = strlen(msg) + 1;
1356     ok(WriteFile(hParentOut, msg, msg_len, &w, NULL), "Writing to child\n");
1357     ok(w == msg_len, "Should have written %u bytes, actually wrote %u\n", msg_len, w);
1358     memset(buffer, 0, sizeof(buffer));
1359     ok(ReadFile(hParentIn, buffer, sizeof(buffer), &w, NULL), "Reading from child\n");
1360     ok(strcmp(buffer, msg) == 0, "Should have received '%s'\n", msg);
1361
1362     /* wait for child to terminate */
1363     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1364     /* child process has changed result file, so let profile functions know about it */
1365     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1366
1367     okChildString("StdHandle", "msg", msg);
1368
1369     release_memory();
1370     assert(DeleteFileA(resfile) != 0);
1371 }
1372
1373 static  void    test_ExitCode(void)
1374 {
1375     char                buffer[MAX_PATH];
1376     PROCESS_INFORMATION info;
1377     STARTUPINFOA        startup;
1378     DWORD               code;
1379
1380     /* let's start simplistic */
1381     memset(&startup, 0, sizeof(startup));
1382     startup.cb = sizeof(startup);
1383     startup.dwFlags = STARTF_USESHOWWINDOW;
1384     startup.wShowWindow = SW_SHOWNORMAL;
1385
1386     get_file_name(resfile);
1387     sprintf(buffer, "%s tests/process.c %s exit_code", selfname, resfile);
1388     ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
1389
1390     /* wait for child to terminate */
1391     ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
1392     /* child process has changed result file, so let profile functions know about it */
1393     WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
1394
1395     ok(GetExitCodeProcess(info.hProcess, &code), "Getting exit code\n");
1396     okChildInt("ExitCode", "value", code);
1397
1398     release_memory();
1399     assert(DeleteFileA(resfile) != 0);
1400 }
1401
1402 static void test_OpenProcess(void)
1403 {
1404     HANDLE hproc;
1405     void *addr1;
1406     MEMORY_BASIC_INFORMATION info;
1407     SIZE_T dummy, read_bytes;
1408
1409     /* not exported in all windows versions */
1410     if ((!pVirtualAllocEx) || (!pVirtualFreeEx)) {
1411         skip("VirtualAllocEx not found\n");
1412         return;
1413     }
1414
1415     /* without PROCESS_VM_OPERATION */
1416     hproc = OpenProcess(PROCESS_ALL_ACCESS & ~PROCESS_VM_OPERATION, FALSE, GetCurrentProcessId());
1417     ok(hproc != NULL, "OpenProcess error %d\n", GetLastError());
1418
1419     SetLastError(0xdeadbeef);
1420     addr1 = pVirtualAllocEx(hproc, 0, 0xFFFC, MEM_RESERVE, PAGE_NOACCESS);
1421     ok(!addr1, "VirtualAllocEx should fail\n");
1422     if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1423     {   /* Win9x */
1424         CloseHandle(hproc);
1425         skip("VirtualAllocEx not implemented\n");
1426         return;
1427     }
1428     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1429
1430     read_bytes = 0xdeadbeef;
1431     SetLastError(0xdeadbeef);
1432     ok(ReadProcessMemory(hproc, test_OpenProcess, &dummy, sizeof(dummy), &read_bytes),
1433        "ReadProcessMemory error %d\n", GetLastError());
1434     ok(read_bytes == sizeof(dummy), "wrong read bytes %ld\n", read_bytes);
1435
1436     CloseHandle(hproc);
1437
1438     hproc = OpenProcess(PROCESS_VM_OPERATION, FALSE, GetCurrentProcessId());
1439     ok(hproc != NULL, "OpenProcess error %d\n", GetLastError());
1440
1441     addr1 = pVirtualAllocEx(hproc, 0, 0xFFFC, MEM_RESERVE, PAGE_NOACCESS);
1442     ok(addr1 != NULL, "VirtualAllocEx error %d\n", GetLastError());
1443
1444     /* without PROCESS_QUERY_INFORMATION */
1445     SetLastError(0xdeadbeef);
1446     ok(!VirtualQueryEx(hproc, addr1, &info, sizeof(info)),
1447        "VirtualQueryEx without PROCESS_QUERY_INFORMATION rights should fail\n");
1448     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1449
1450     /* without PROCESS_VM_READ */
1451     read_bytes = 0xdeadbeef;
1452     SetLastError(0xdeadbeef);
1453     ok(!ReadProcessMemory(hproc, addr1, &dummy, sizeof(dummy), &read_bytes),
1454        "ReadProcessMemory without PROCESS_VM_READ rights should fail\n");
1455     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1456     ok(read_bytes == 0, "wrong read bytes %ld\n", read_bytes);
1457
1458     CloseHandle(hproc);
1459
1460     hproc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
1461
1462     memset(&info, 0xcc, sizeof(info));
1463     ok(VirtualQueryEx(hproc, addr1, &info, sizeof(info)) == sizeof(info),
1464        "VirtualQueryEx error %d\n", GetLastError());
1465
1466     ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress, addr1);
1467     ok(info.AllocationBase == addr1, "%p != %p\n", info.AllocationBase, addr1);
1468     ok(info.AllocationProtect == PAGE_NOACCESS, "%x != PAGE_NOACCESS\n", info.AllocationProtect);
1469     ok(info.RegionSize == 0x10000, "%lx != 0x10000\n", info.RegionSize);
1470     ok(info.State == MEM_RESERVE, "%x != MEM_RESERVE\n", info.State);
1471     /* NT reports Protect == 0 for a not committed memory block */
1472     ok(info.Protect == 0 /* NT */ ||
1473        info.Protect == PAGE_NOACCESS, /* Win9x */
1474         "%x != PAGE_NOACCESS\n", info.Protect);
1475     ok(info.Type == MEM_PRIVATE, "%x != MEM_PRIVATE\n", info.Type);
1476
1477     SetLastError(0xdeadbeef);
1478     ok(!pVirtualFreeEx(hproc, addr1, 0, MEM_RELEASE),
1479        "VirtualFreeEx without PROCESS_VM_OPERATION rights should fail\n");
1480     ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error %d\n", GetLastError());
1481
1482     CloseHandle(hproc);
1483
1484     ok(VirtualFree(addr1, 0, MEM_RELEASE), "VirtualFree failed\n");
1485 }
1486
1487 static void test_GetProcessVersion(void)
1488 {
1489     static char cmdline[] = "winver.exe";
1490     PROCESS_INFORMATION pi;
1491     STARTUPINFOA si;
1492     DWORD ret;
1493
1494     SetLastError(0xdeadbeef);
1495     ret = GetProcessVersion(0);
1496     ok(ret, "GetProcessVersion error %u\n", GetLastError());
1497
1498     SetLastError(0xdeadbeef);
1499     ret = GetProcessVersion(GetCurrentProcessId());
1500     ok(ret, "GetProcessVersion error %u\n", GetLastError());
1501
1502     memset(&si, 0, sizeof(si));
1503     si.cb = sizeof(si);
1504     si.dwFlags = STARTF_USESHOWWINDOW;
1505     si.wShowWindow = SW_HIDE;
1506     ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
1507     SetLastError(0xdeadbeef);
1508     ok(ret, "CreateProcess error %u\n", GetLastError());
1509
1510     SetLastError(0xdeadbeef);
1511     ret = GetProcessVersion(pi.dwProcessId);
1512     ok(ret, "GetProcessVersion error %u\n", GetLastError());
1513
1514     SetLastError(0xdeadbeef);
1515     ret = TerminateProcess(pi.hProcess, 0);
1516     ok(ret, "TerminateProcess error %u\n", GetLastError());
1517
1518     CloseHandle(pi.hProcess);
1519     CloseHandle(pi.hThread);
1520 }
1521
1522 START_TEST(process)
1523 {
1524     int b = init();
1525     ok(b, "Basic init of CreateProcess test\n");
1526     if (!b) return;
1527
1528     if (myARGC >= 3)
1529     {
1530         doChild(myARGV[2], (myARGC == 3) ? NULL : myARGV[3]);
1531         return;
1532     }
1533     test_Startup();
1534     test_CommandLine();
1535     test_Directory();
1536     test_Environment();
1537     test_SuspendFlag();
1538     test_DebuggingFlag();
1539     test_Console();
1540     test_ExitCode();
1541     test_OpenProcess();
1542     test_GetProcessVersion();
1543     /* things that can be tested:
1544      *  lookup:         check the way program to be executed is searched
1545      *  handles:        check the handle inheritance stuff (+sec options)
1546      *  console:        check if console creation parameters work
1547      */
1548 }