1*c2c66affSColin Finck /*
2*c2c66affSColin Finck  * Wine Conformance Test EXE
3*c2c66affSColin Finck  *
4*c2c66affSColin Finck  * Copyright 2003, 2004 Jakob Eriksson   (for Solid Form Sweden AB)
5*c2c66affSColin Finck  * Copyright 2003 Dimitrie O. Paun
6*c2c66affSColin Finck  * Copyright 2003 Ferenc Wagner
7*c2c66affSColin Finck  *
8*c2c66affSColin Finck  * This library is free software; you can redistribute it and/or
9*c2c66affSColin Finck  * modify it under the terms of the GNU Lesser General Public
10*c2c66affSColin Finck  * License as published by the Free Software Foundation; either
11*c2c66affSColin Finck  * version 2.1 of the License, or (at your option) any later version.
12*c2c66affSColin Finck  *
13*c2c66affSColin Finck  * This library is distributed in the hope that it will be useful,
14*c2c66affSColin Finck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15*c2c66affSColin Finck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16*c2c66affSColin Finck  * Lesser General Public License for more details.
17*c2c66affSColin Finck  *
18*c2c66affSColin Finck  * You should have received a copy of the GNU Lesser General Public
19*c2c66affSColin Finck  * License along with this library; if not, write to the Free Software
20*c2c66affSColin Finck  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21*c2c66affSColin Finck  *
22*c2c66affSColin Finck  * This program is dedicated to Anna Lindh,
23*c2c66affSColin Finck  * Swedish Minister of Foreign Affairs.
24*c2c66affSColin Finck  * Anna was murdered September 11, 2003.
25*c2c66affSColin Finck  *
26*c2c66affSColin Finck  */
27*c2c66affSColin Finck 
28*c2c66affSColin Finck #include "config.h"
29*c2c66affSColin Finck #include "wine/port.h"
30*c2c66affSColin Finck 
31*c2c66affSColin Finck #include <stdio.h>
32*c2c66affSColin Finck #include <stdlib.h>
33*c2c66affSColin Finck #include <assert.h>
34*c2c66affSColin Finck #include <errno.h>
35*c2c66affSColin Finck #ifdef HAVE_UNISTD_H
36*c2c66affSColin Finck #  include <unistd.h>
37*c2c66affSColin Finck #endif
38*c2c66affSColin Finck #include <windows.h>
39*c2c66affSColin Finck 
40*c2c66affSColin Finck #include "winetest.h"
41*c2c66affSColin Finck #include "resource.h"
42*c2c66affSColin Finck #include <reason.h>
43*c2c66affSColin Finck 
44*c2c66affSColin Finck struct wine_test
45*c2c66affSColin Finck {
46*c2c66affSColin Finck     char *name;
47*c2c66affSColin Finck     int resource;
48*c2c66affSColin Finck     int subtest_count;
49*c2c66affSColin Finck     char **subtests;
50*c2c66affSColin Finck     char *exename;
51*c2c66affSColin Finck };
52*c2c66affSColin Finck 
53*c2c66affSColin Finck struct rev_info
54*c2c66affSColin Finck {
55*c2c66affSColin Finck     const char* file;
56*c2c66affSColin Finck     const char* rev;
57*c2c66affSColin Finck };
58*c2c66affSColin Finck 
59*c2c66affSColin Finck char *tag = NULL;
60*c2c66affSColin Finck static struct wine_test *wine_tests;
61*c2c66affSColin Finck static int nr_of_files, nr_of_tests;
62*c2c66affSColin Finck static struct rev_info *rev_infos = NULL;
63*c2c66affSColin Finck static const char whitespace[] = " \t\r\n";
64*c2c66affSColin Finck static const char testexe[] = "_test.exe";
65*c2c66affSColin Finck 
get_file_version(char * file_name)66*c2c66affSColin Finck static char * get_file_version(char * file_name)
67*c2c66affSColin Finck {
68*c2c66affSColin Finck     static char version[32];
69*c2c66affSColin Finck     DWORD size;
70*c2c66affSColin Finck     DWORD handle;
71*c2c66affSColin Finck 
72*c2c66affSColin Finck     size = GetFileVersionInfoSizeA(file_name, &handle);
73*c2c66affSColin Finck     if (size) {
74*c2c66affSColin Finck         char * data = xmalloc(size);
75*c2c66affSColin Finck         if (data) {
76*c2c66affSColin Finck             if (GetFileVersionInfoA(file_name, handle, size, data)) {
77*c2c66affSColin Finck                 static char backslash[] = "\\";
78*c2c66affSColin Finck                 VS_FIXEDFILEINFO *pFixedVersionInfo;
79*c2c66affSColin Finck                 UINT len;
80*c2c66affSColin Finck                 if (VerQueryValueA(data, backslash, (LPVOID *)&pFixedVersionInfo, &len)) {
81*c2c66affSColin Finck                     sprintf(version, "%d.%d.%d.%d",
82*c2c66affSColin Finck                             pFixedVersionInfo->dwFileVersionMS >> 16,
83*c2c66affSColin Finck                             pFixedVersionInfo->dwFileVersionMS & 0xffff,
84*c2c66affSColin Finck                             pFixedVersionInfo->dwFileVersionLS >> 16,
85*c2c66affSColin Finck                             pFixedVersionInfo->dwFileVersionLS & 0xffff);
86*c2c66affSColin Finck                 } else
87*c2c66affSColin Finck                     sprintf(version, "version not available");
88*c2c66affSColin Finck             } else
89*c2c66affSColin Finck                 sprintf(version, "unknown");
90*c2c66affSColin Finck             free(data);
91*c2c66affSColin Finck         } else
92*c2c66affSColin Finck             sprintf(version, "failed");
93*c2c66affSColin Finck     } else
94*c2c66affSColin Finck         sprintf(version, "version not available");
95*c2c66affSColin Finck 
96*c2c66affSColin Finck     return version;
97*c2c66affSColin Finck }
98*c2c66affSColin Finck 
running_under_wine(void)99*c2c66affSColin Finck static int running_under_wine (void)
100*c2c66affSColin Finck {
101*c2c66affSColin Finck     HMODULE module = GetModuleHandleA("ntdll.dll");
102*c2c66affSColin Finck 
103*c2c66affSColin Finck     if (!module) return 0;
104*c2c66affSColin Finck     return (GetProcAddress(module, "wine_server_call") != NULL);
105*c2c66affSColin Finck }
106*c2c66affSColin Finck 
running_on_visible_desktop(void)107*c2c66affSColin Finck static int running_on_visible_desktop (void)
108*c2c66affSColin Finck {
109*c2c66affSColin Finck     HWND desktop;
110*c2c66affSColin Finck     HMODULE huser32 = GetModuleHandle("user32.dll");
111*c2c66affSColin Finck     FARPROC pGetProcessWindowStation = GetProcAddress(huser32, "GetProcessWindowStation");
112*c2c66affSColin Finck     FARPROC pGetUserObjectInformationA = GetProcAddress(huser32, "GetUserObjectInformationA");
113*c2c66affSColin Finck 
114*c2c66affSColin Finck     desktop = GetDesktopWindow();
115*c2c66affSColin Finck     if (!GetWindowLongPtrW(desktop, GWLP_WNDPROC)) /* Win9x */
116*c2c66affSColin Finck         return IsWindowVisible(desktop);
117*c2c66affSColin Finck 
118*c2c66affSColin Finck     if (pGetProcessWindowStation && pGetUserObjectInformationA)
119*c2c66affSColin Finck     {
120*c2c66affSColin Finck         DWORD len;
121*c2c66affSColin Finck         HWINSTA wstation;
122*c2c66affSColin Finck         USEROBJECTFLAGS uoflags;
123*c2c66affSColin Finck 
124*c2c66affSColin Finck         wstation = (HWINSTA)pGetProcessWindowStation();
125*c2c66affSColin Finck         assert(pGetUserObjectInformationA(wstation, UOI_FLAGS, &uoflags, sizeof(uoflags), &len));
126*c2c66affSColin Finck         return (uoflags.dwFlags & WSF_VISIBLE) != 0;
127*c2c66affSColin Finck     }
128*c2c66affSColin Finck     return IsWindowVisible(desktop);
129*c2c66affSColin Finck }
130*c2c66affSColin Finck 
print_version(void)131*c2c66affSColin Finck static void print_version (void)
132*c2c66affSColin Finck {
133*c2c66affSColin Finck     OSVERSIONINFOEX ver;
134*c2c66affSColin Finck     BOOL ext;
135*c2c66affSColin Finck     int is_win2k3_r2;
136*c2c66affSColin Finck     const char *(*wine_get_build_id)(void);
137*c2c66affSColin Finck 
138*c2c66affSColin Finck     ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
139*c2c66affSColin Finck     if (!(ext = GetVersionEx ((OSVERSIONINFO *) &ver)))
140*c2c66affSColin Finck     {
141*c2c66affSColin Finck 	ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
142*c2c66affSColin Finck 	if (!GetVersionEx ((OSVERSIONINFO *) &ver))
143*c2c66affSColin Finck 	    report (R_FATAL, "Can't get OS version.");
144*c2c66affSColin Finck     }
145*c2c66affSColin Finck 
146*c2c66affSColin Finck     xprintf ("    bRunningUnderWine=%d\n", running_under_wine ());
147*c2c66affSColin Finck     xprintf ("    bRunningOnVisibleDesktop=%d\n", running_on_visible_desktop ());
148*c2c66affSColin Finck     xprintf ("    dwMajorVersion=%ld\n    dwMinorVersion=%ld\n"
149*c2c66affSColin Finck              "    dwBuildNumber=%ld\n    PlatformId=%ld\n    szCSDVersion=%s\n",
150*c2c66affSColin Finck              ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
151*c2c66affSColin Finck              ver.dwPlatformId, ver.szCSDVersion);
152*c2c66affSColin Finck 
153*c2c66affSColin Finck     wine_get_build_id = (void *)GetProcAddress(GetModuleHandleA("ntdll.dll"), "wine_get_build_id");
154*c2c66affSColin Finck     if (wine_get_build_id) xprintf( "    WineBuild=%s\n", wine_get_build_id() );
155*c2c66affSColin Finck 
156*c2c66affSColin Finck     is_win2k3_r2 = GetSystemMetrics(SM_SERVERR2);
157*c2c66affSColin Finck     if(is_win2k3_r2)
158*c2c66affSColin Finck         xprintf("    R2 build number=%d\n", is_win2k3_r2);
159*c2c66affSColin Finck 
160*c2c66affSColin Finck     if (!ext) return;
161*c2c66affSColin Finck 
162*c2c66affSColin Finck     xprintf ("    wServicePackMajor=%d\n    wServicePackMinor=%d\n"
163*c2c66affSColin Finck              "    wSuiteMask=%d\n    wProductType=%d\n    wReserved=%d\n",
164*c2c66affSColin Finck              ver.wServicePackMajor, ver.wServicePackMinor, ver.wSuiteMask,
165*c2c66affSColin Finck              ver.wProductType, ver.wReserved);
166*c2c66affSColin Finck }
167*c2c66affSColin Finck 
is_dot_dir(const char * x)168*c2c66affSColin Finck static inline int is_dot_dir(const char* x)
169*c2c66affSColin Finck {
170*c2c66affSColin Finck     return ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))));
171*c2c66affSColin Finck }
172*c2c66affSColin Finck 
remove_dir(const char * dir)173*c2c66affSColin Finck static void remove_dir (const char *dir)
174*c2c66affSColin Finck {
175*c2c66affSColin Finck     HANDLE  hFind;
176*c2c66affSColin Finck     WIN32_FIND_DATA wfd;
177*c2c66affSColin Finck     char path[MAX_PATH];
178*c2c66affSColin Finck     size_t dirlen = strlen (dir);
179*c2c66affSColin Finck 
180*c2c66affSColin Finck     /* Make sure the directory exists before going further */
181*c2c66affSColin Finck     memcpy (path, dir, dirlen);
182*c2c66affSColin Finck     strcpy (path + dirlen++, "\\*");
183*c2c66affSColin Finck     hFind = FindFirstFile (path, &wfd);
184*c2c66affSColin Finck     if (hFind == INVALID_HANDLE_VALUE) return;
185*c2c66affSColin Finck 
186*c2c66affSColin Finck     do {
187*c2c66affSColin Finck         char *lp = wfd.cFileName;
188*c2c66affSColin Finck 
189*c2c66affSColin Finck         if (!lp[0]) lp = wfd.cAlternateFileName; /* ? FIXME not (!lp) ? */
190*c2c66affSColin Finck         if (is_dot_dir (lp)) continue;
191*c2c66affSColin Finck         strcpy (path + dirlen, lp);
192*c2c66affSColin Finck         if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
193*c2c66affSColin Finck             remove_dir(path);
194*c2c66affSColin Finck         else if (!DeleteFile (path))
195*c2c66affSColin Finck             report (R_WARNING, "Can't delete file %s: error %d",
196*c2c66affSColin Finck                     path, GetLastError ());
197*c2c66affSColin Finck     } while (FindNextFile (hFind, &wfd));
198*c2c66affSColin Finck     FindClose (hFind);
199*c2c66affSColin Finck     if (!RemoveDirectory (dir))
200*c2c66affSColin Finck         report (R_WARNING, "Can't remove directory %s: error %d",
201*c2c66affSColin Finck                 dir, GetLastError ());
202*c2c66affSColin Finck }
203*c2c66affSColin Finck 
get_test_source_file(const char * test,const char * subtest)204*c2c66affSColin Finck static const char* get_test_source_file(const char* test, const char* subtest)
205*c2c66affSColin Finck {
206*c2c66affSColin Finck     static const char* special_dirs[][2] = {
207*c2c66affSColin Finck 	{ 0, 0 }
208*c2c66affSColin Finck     };
209*c2c66affSColin Finck     static char buffer[MAX_PATH];
210*c2c66affSColin Finck     int i;
211*c2c66affSColin Finck 
212*c2c66affSColin Finck     for (i = 0; special_dirs[i][0]; i++) {
213*c2c66affSColin Finck 	if (strcmp(test, special_dirs[i][0]) == 0) {
214*c2c66affSColin Finck 	    test = special_dirs[i][1];
215*c2c66affSColin Finck 	    break;
216*c2c66affSColin Finck 	}
217*c2c66affSColin Finck     }
218*c2c66affSColin Finck 
219*c2c66affSColin Finck     snprintf(buffer, sizeof(buffer), "dlls/%s/tests/%s.c", test, subtest);
220*c2c66affSColin Finck     return buffer;
221*c2c66affSColin Finck }
222*c2c66affSColin Finck 
get_file_rev(const char * file)223*c2c66affSColin Finck static const char* get_file_rev(const char* file)
224*c2c66affSColin Finck {
225*c2c66affSColin Finck     const struct rev_info* rev;
226*c2c66affSColin Finck 
227*c2c66affSColin Finck     for(rev = rev_infos; rev->file; rev++) {
228*c2c66affSColin Finck 	if (strcmp(rev->file, file) == 0) return rev->rev;
229*c2c66affSColin Finck     }
230*c2c66affSColin Finck 
231*c2c66affSColin Finck     return "-";
232*c2c66affSColin Finck }
233*c2c66affSColin Finck 
extract_rev_infos(void)234*c2c66affSColin Finck static void extract_rev_infos (void)
235*c2c66affSColin Finck {
236*c2c66affSColin Finck     char revinfo[256], *p;
237*c2c66affSColin Finck     int size = 0, i;
238*c2c66affSColin Finck     unsigned int len;
239*c2c66affSColin Finck     HMODULE module = GetModuleHandle (NULL);
240*c2c66affSColin Finck 
241*c2c66affSColin Finck     for (i = 0; TRUE; i++) {
242*c2c66affSColin Finck 	if (i >= size) {
243*c2c66affSColin Finck 	    size += 100;
244*c2c66affSColin Finck 	    rev_infos = xrealloc (rev_infos, size * sizeof (*rev_infos));
245*c2c66affSColin Finck 	}
246*c2c66affSColin Finck 	memset(rev_infos + i, 0, sizeof(rev_infos[i]));
247*c2c66affSColin Finck 
248*c2c66affSColin Finck         len = LoadStringA (module, REV_INFO+i, revinfo, sizeof(revinfo));
249*c2c66affSColin Finck         if (len == 0) break; /* end of revision info */
250*c2c66affSColin Finck 	if (len >= sizeof(revinfo) - 1)
251*c2c66affSColin Finck 	    report (R_FATAL, "Revision info too long.");
252*c2c66affSColin Finck 	if(!(p = strrchr(revinfo, ':')))
253*c2c66affSColin Finck 	    report (R_FATAL, "Revision info malformed (i=%d)", i);
254*c2c66affSColin Finck 	*p = 0;
255*c2c66affSColin Finck 	rev_infos[i].file = strdup(revinfo);
256*c2c66affSColin Finck 	rev_infos[i].rev = strdup(p + 1);
257*c2c66affSColin Finck     }
258*c2c66affSColin Finck }
259*c2c66affSColin Finck 
extract_rcdata(LPTSTR name,int type,DWORD * size)260*c2c66affSColin Finck static void* extract_rcdata (LPTSTR name, int type, DWORD* size)
261*c2c66affSColin Finck {
262*c2c66affSColin Finck     HRSRC rsrc;
263*c2c66affSColin Finck     HGLOBAL hdl;
264*c2c66affSColin Finck     LPVOID addr;
265*c2c66affSColin Finck 
266*c2c66affSColin Finck     if (!(rsrc = FindResource (NULL, name, MAKEINTRESOURCE(type))) ||
267*c2c66affSColin Finck         !(*size = SizeofResource (0, rsrc)) ||
268*c2c66affSColin Finck         !(hdl = LoadResource (0, rsrc)) ||
269*c2c66affSColin Finck         !(addr = LockResource (hdl)))
270*c2c66affSColin Finck         return NULL;
271*c2c66affSColin Finck     return addr;
272*c2c66affSColin Finck }
273*c2c66affSColin Finck 
274*c2c66affSColin Finck /* Fills in the name and exename fields */
275*c2c66affSColin Finck static void
extract_test(struct wine_test * test,const char * dir,LPTSTR res_name)276*c2c66affSColin Finck extract_test (struct wine_test *test, const char *dir, LPTSTR res_name)
277*c2c66affSColin Finck {
278*c2c66affSColin Finck     BYTE* code;
279*c2c66affSColin Finck     DWORD size;
280*c2c66affSColin Finck     FILE* fout;
281*c2c66affSColin Finck     char *exepos;
282*c2c66affSColin Finck 
283*c2c66affSColin Finck     code = extract_rcdata (res_name, TESTRES, &size);
284*c2c66affSColin Finck     if (!code) report (R_FATAL, "Can't find test resource %s: %d",
285*c2c66affSColin Finck                        res_name, GetLastError ());
286*c2c66affSColin Finck     test->name = xstrdup( res_name );
287*c2c66affSColin Finck     test->exename = strmake (NULL, "%s/%s", dir, test->name);
288*c2c66affSColin Finck     exepos = strstr (test->name, testexe);
289*c2c66affSColin Finck     if (!exepos) report (R_FATAL, "Not an .exe file: %s", test->name);
290*c2c66affSColin Finck     *exepos = 0;
291*c2c66affSColin Finck     test->name = xrealloc (test->name, exepos - test->name + 1);
292*c2c66affSColin Finck     report (R_STEP, "Extracting: %s", test->name);
293*c2c66affSColin Finck 
294*c2c66affSColin Finck     if (!(fout = fopen (test->exename, "wb")) ||
295*c2c66affSColin Finck         (fwrite (code, size, 1, fout) != 1) ||
296*c2c66affSColin Finck         fclose (fout)) report (R_FATAL, "Failed to write file %s.",
297*c2c66affSColin Finck                                test->exename);
298*c2c66affSColin Finck }
299*c2c66affSColin Finck 
300*c2c66affSColin Finck /* Run a command for MS milliseconds.  If OUT != NULL, also redirect
301*c2c66affSColin Finck    stdout to there.
302*c2c66affSColin Finck 
303*c2c66affSColin Finck    Return the exit status, -2 if can't create process or the return
304*c2c66affSColin Finck    value of WaitForSingleObject.
305*c2c66affSColin Finck  */
306*c2c66affSColin Finck static int
run_ex(char * cmd,const char * out,const char * tempdir,DWORD ms)307*c2c66affSColin Finck run_ex (char *cmd, const char *out, const char *tempdir, DWORD ms)
308*c2c66affSColin Finck {
309*c2c66affSColin Finck     STARTUPINFO si;
310*c2c66affSColin Finck     PROCESS_INFORMATION pi;
311*c2c66affSColin Finck     int fd, oldstdout = -1;
312*c2c66affSColin Finck     DWORD wait, status;
313*c2c66affSColin Finck 
314*c2c66affSColin Finck     GetStartupInfo (&si);
315*c2c66affSColin Finck     si.dwFlags = 0;
316*c2c66affSColin Finck 
317*c2c66affSColin Finck     if (out) {
318*c2c66affSColin Finck         fd = open (out, O_WRONLY | O_CREAT, 0666);
319*c2c66affSColin Finck         if (-1 == fd)
320*c2c66affSColin Finck             report (R_FATAL, "Can't open '%s': %d", out, errno);
321*c2c66affSColin Finck         oldstdout = dup (1);
322*c2c66affSColin Finck         if (-1 == oldstdout)
323*c2c66affSColin Finck             report (R_FATAL, "Can't save stdout: %d", errno);
324*c2c66affSColin Finck         if (-1 == dup2 (fd, 1))
325*c2c66affSColin Finck             report (R_FATAL, "Can't redirect stdout: %d", errno);
326*c2c66affSColin Finck         close (fd);
327*c2c66affSColin Finck     }
328*c2c66affSColin Finck 
329*c2c66affSColin Finck     if (!CreateProcessA (NULL, cmd, NULL, NULL, TRUE, CREATE_DEFAULT_ERROR_MODE,
330*c2c66affSColin Finck                          NULL, tempdir, &si, &pi)) {
331*c2c66affSColin Finck         status = -2;
332*c2c66affSColin Finck     } else {
333*c2c66affSColin Finck         CloseHandle (pi.hThread);
334*c2c66affSColin Finck         wait = WaitForSingleObject (pi.hProcess, ms);
335*c2c66affSColin Finck         if (wait == WAIT_OBJECT_0) {
336*c2c66affSColin Finck             GetExitCodeProcess (pi.hProcess, &status);
337*c2c66affSColin Finck         } else {
338*c2c66affSColin Finck             switch (wait) {
339*c2c66affSColin Finck             case WAIT_FAILED:
340*c2c66affSColin Finck                 report (R_ERROR, "Wait for '%s' failed: %d", cmd,
341*c2c66affSColin Finck                         GetLastError ());
342*c2c66affSColin Finck                 break;
343*c2c66affSColin Finck             case WAIT_TIMEOUT:
344*c2c66affSColin Finck                 report (R_ERROR, "Process '%s' timed out.", cmd);
345*c2c66affSColin Finck                 break;
346*c2c66affSColin Finck             default:
347*c2c66affSColin Finck                 report (R_ERROR, "Wait returned %d", wait);
348*c2c66affSColin Finck             }
349*c2c66affSColin Finck             status = wait;
350*c2c66affSColin Finck             if (!TerminateProcess (pi.hProcess, 257))
351*c2c66affSColin Finck                 report (R_ERROR, "TerminateProcess failed: %d",
352*c2c66affSColin Finck                         GetLastError ());
353*c2c66affSColin Finck             wait = WaitForSingleObject (pi.hProcess, 5000);
354*c2c66affSColin Finck             switch (wait) {
355*c2c66affSColin Finck             case WAIT_FAILED:
356*c2c66affSColin Finck                 report (R_ERROR,
357*c2c66affSColin Finck                         "Wait for termination of '%s' failed: %d",
358*c2c66affSColin Finck                         cmd, GetLastError ());
359*c2c66affSColin Finck                 break;
360*c2c66affSColin Finck             case WAIT_OBJECT_0:
361*c2c66affSColin Finck                 break;
362*c2c66affSColin Finck             case WAIT_TIMEOUT:
363*c2c66affSColin Finck                 report (R_ERROR, "Can't kill process '%s'", cmd);
364*c2c66affSColin Finck                 break;
365*c2c66affSColin Finck             default:
366*c2c66affSColin Finck                 report (R_ERROR, "Waiting for termination: %d",
367*c2c66affSColin Finck                         wait);
368*c2c66affSColin Finck             }
369*c2c66affSColin Finck         }
370*c2c66affSColin Finck         CloseHandle (pi.hProcess);
371*c2c66affSColin Finck     }
372*c2c66affSColin Finck 
373*c2c66affSColin Finck     if (out) {
374*c2c66affSColin Finck         close (1);
375*c2c66affSColin Finck         if (-1 == dup2 (oldstdout, 1))
376*c2c66affSColin Finck             report (R_FATAL, "Can't recover stdout: %d", errno);
377*c2c66affSColin Finck         close (oldstdout);
378*c2c66affSColin Finck     }
379*c2c66affSColin Finck     return status;
380*c2c66affSColin Finck }
381*c2c66affSColin Finck 
382*c2c66affSColin Finck static void
get_subtests(const char * tempdir,struct wine_test * test,LPTSTR res_name)383*c2c66affSColin Finck get_subtests (const char *tempdir, struct wine_test *test, LPTSTR res_name)
384*c2c66affSColin Finck {
385*c2c66affSColin Finck     char *subname, *cmd;
386*c2c66affSColin Finck     FILE *subfile;
387*c2c66affSColin Finck     size_t total;
388*c2c66affSColin Finck     char buffer[8192], *index;
389*c2c66affSColin Finck     static const char header[] = "Valid test names:";
390*c2c66affSColin Finck     int allocated;
391*c2c66affSColin Finck 
392*c2c66affSColin Finck     test->subtest_count = 0;
393*c2c66affSColin Finck 
394*c2c66affSColin Finck     subname = tempnam (0, "sub");
395*c2c66affSColin Finck     if (!subname) report (R_FATAL, "Can't name subtests file.");
396*c2c66affSColin Finck 
397*c2c66affSColin Finck     extract_test (test, tempdir, res_name);
398*c2c66affSColin Finck     cmd = strmake (NULL, "%s --list", test->exename);
399*c2c66affSColin Finck     run_ex (cmd, subname, tempdir, 5000);
400*c2c66affSColin Finck     free (cmd);
401*c2c66affSColin Finck 
402*c2c66affSColin Finck     subfile = fopen (subname, "r");
403*c2c66affSColin Finck     if (!subfile) {
404*c2c66affSColin Finck         report (R_ERROR, "Can't open subtests output of %s: %d",
405*c2c66affSColin Finck                 test->name, errno);
406*c2c66affSColin Finck         goto quit;
407*c2c66affSColin Finck     }
408*c2c66affSColin Finck     total = fread (buffer, 1, sizeof buffer, subfile);
409*c2c66affSColin Finck     fclose (subfile);
410*c2c66affSColin Finck     if (sizeof buffer == total) {
411*c2c66affSColin Finck         report (R_ERROR, "Subtest list of %s too big.",
412*c2c66affSColin Finck                 test->name, sizeof buffer);
413*c2c66affSColin Finck         goto quit;
414*c2c66affSColin Finck     }
415*c2c66affSColin Finck     buffer[total] = 0;
416*c2c66affSColin Finck 
417*c2c66affSColin Finck     index = strstr (buffer, header);
418*c2c66affSColin Finck     if (!index) {
419*c2c66affSColin Finck         report (R_ERROR, "Can't parse subtests output of %s",
420*c2c66affSColin Finck                 test->name);
421*c2c66affSColin Finck         goto quit;
422*c2c66affSColin Finck     }
423*c2c66affSColin Finck     index += sizeof header;
424*c2c66affSColin Finck 
425*c2c66affSColin Finck     allocated = 10;
426*c2c66affSColin Finck     test->subtests = xmalloc (allocated * sizeof(char*));
427*c2c66affSColin Finck     index = strtok (index, whitespace);
428*c2c66affSColin Finck     while (index) {
429*c2c66affSColin Finck         if (test->subtest_count == allocated) {
430*c2c66affSColin Finck             allocated *= 2;
431*c2c66affSColin Finck             test->subtests = xrealloc (test->subtests,
432*c2c66affSColin Finck                                        allocated * sizeof(char*));
433*c2c66affSColin Finck         }
434*c2c66affSColin Finck         test->subtests[test->subtest_count++] = strdup (index);
435*c2c66affSColin Finck         index = strtok (NULL, whitespace);
436*c2c66affSColin Finck     }
437*c2c66affSColin Finck     test->subtests = xrealloc (test->subtests,
438*c2c66affSColin Finck                                test->subtest_count * sizeof(char*));
439*c2c66affSColin Finck 
440*c2c66affSColin Finck  quit:
441*c2c66affSColin Finck     if (remove (subname))
442*c2c66affSColin Finck         report (R_WARNING, "Can't delete file '%s': %d",
443*c2c66affSColin Finck                 subname, errno);
444*c2c66affSColin Finck     free (subname);
445*c2c66affSColin Finck }
446*c2c66affSColin Finck 
447*c2c66affSColin Finck static void
run_test(struct wine_test * test,const char * subtest,const char * tempdir)448*c2c66affSColin Finck run_test (struct wine_test* test, const char* subtest, const char *tempdir)
449*c2c66affSColin Finck {
450*c2c66affSColin Finck     int status;
451*c2c66affSColin Finck     const char* file = get_test_source_file(test->name, subtest);
452*c2c66affSColin Finck     const char* rev = get_file_rev(file);
453*c2c66affSColin Finck     char *cmd = strmake (NULL, "%s %s", test->exename, subtest);
454*c2c66affSColin Finck 
455*c2c66affSColin Finck     xprintf ("%s:%s start %s %s\n", test->name, subtest, file, rev);
456*c2c66affSColin Finck     status = run_ex (cmd, NULL, tempdir, 120000);
457*c2c66affSColin Finck     free (cmd);
458*c2c66affSColin Finck     xprintf ("%s:%s done (%d)\n", test->name, subtest, status);
459*c2c66affSColin Finck }
460*c2c66affSColin Finck 
461*c2c66affSColin Finck static BOOL CALLBACK
EnumTestFileProc(HMODULE hModule,LPCTSTR lpszType,LPTSTR lpszName,LONG_PTR lParam)462*c2c66affSColin Finck EnumTestFileProc (HMODULE hModule, LPCTSTR lpszType,
463*c2c66affSColin Finck                   LPTSTR lpszName, LONG_PTR lParam)
464*c2c66affSColin Finck {
465*c2c66affSColin Finck     (*(int*)lParam)++;
466*c2c66affSColin Finck     return TRUE;
467*c2c66affSColin Finck }
468*c2c66affSColin Finck 
469*c2c66affSColin Finck static BOOL CALLBACK
extract_test_proc(HMODULE hModule,LPCTSTR lpszType,LPTSTR lpszName,LONG_PTR lParam)470*c2c66affSColin Finck extract_test_proc (HMODULE hModule, LPCTSTR lpszType,
471*c2c66affSColin Finck                    LPTSTR lpszName, LONG_PTR lParam)
472*c2c66affSColin Finck {
473*c2c66affSColin Finck     const char *tempdir = (const char *)lParam;
474*c2c66affSColin Finck     char dllname[MAX_PATH];
475*c2c66affSColin Finck     HMODULE dll;
476*c2c66affSColin Finck 
477*c2c66affSColin Finck     /* Check if the main dll is present on this system */
478*c2c66affSColin Finck     CharLowerA(lpszName);
479*c2c66affSColin Finck     strcpy(dllname, lpszName);
480*c2c66affSColin Finck     *strstr(dllname, testexe) = 0;
481*c2c66affSColin Finck 
482*c2c66affSColin Finck     dll = LoadLibraryExA(dllname, NULL, LOAD_LIBRARY_AS_DATAFILE);
483*c2c66affSColin Finck     if (!dll) {
484*c2c66affSColin Finck         xprintf ("    %s=dll is missing\n", dllname);
485*c2c66affSColin Finck         return TRUE;
486*c2c66affSColin Finck     }
487*c2c66affSColin Finck     FreeLibrary(dll);
488*c2c66affSColin Finck 
489*c2c66affSColin Finck     xprintf ("    %s=%s\n", dllname, get_file_version(dllname));
490*c2c66affSColin Finck 
491*c2c66affSColin Finck     get_subtests( tempdir, &wine_tests[nr_of_files], lpszName );
492*c2c66affSColin Finck     nr_of_tests += wine_tests[nr_of_files].subtest_count;
493*c2c66affSColin Finck     nr_of_files++;
494*c2c66affSColin Finck     return TRUE;
495*c2c66affSColin Finck }
496*c2c66affSColin Finck 
497*c2c66affSColin Finck static char *
run_tests(char * logname)498*c2c66affSColin Finck run_tests (char *logname)
499*c2c66affSColin Finck {
500*c2c66affSColin Finck     int i;
501*c2c66affSColin Finck     char *tempdir, *shorttempdir;
502*c2c66affSColin Finck     int logfile;
503*c2c66affSColin Finck     char *strres, *eol, *nextline;
504*c2c66affSColin Finck     DWORD strsize;
505*c2c66affSColin Finck     char build[64];
506*c2c66affSColin Finck 
507*c2c66affSColin Finck     SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
508*c2c66affSColin Finck 
509*c2c66affSColin Finck     if (!logname) {
510*c2c66affSColin Finck         logname = tempnam (0, "res");
511*c2c66affSColin Finck         if (!logname) report (R_FATAL, "Can't name logfile.");
512*c2c66affSColin Finck     }
513*c2c66affSColin Finck     report (R_OUT, logname);
514*c2c66affSColin Finck 
515*c2c66affSColin Finck     logfile = open (logname, O_WRONLY | O_CREAT | O_EXCL | O_APPEND,
516*c2c66affSColin Finck                     0666);
517*c2c66affSColin Finck     if (-1 == logfile) {
518*c2c66affSColin Finck         if (EEXIST == errno)
519*c2c66affSColin Finck             report (R_FATAL, "File %s already exists.", logname);
520*c2c66affSColin Finck         else report (R_FATAL, "Could not open logfile: %d", errno);
521*c2c66affSColin Finck     }
522*c2c66affSColin Finck     if (-1 == dup2 (logfile, 1))
523*c2c66affSColin Finck         report (R_FATAL, "Can't redirect stdout: %d", errno);
524*c2c66affSColin Finck     close (logfile);
525*c2c66affSColin Finck 
526*c2c66affSColin Finck     tempdir = tempnam (0, "wct");
527*c2c66affSColin Finck     if (!tempdir)
528*c2c66affSColin Finck         report (R_FATAL, "Can't name temporary dir (check %%TEMP%%).");
529*c2c66affSColin Finck     shorttempdir = strdup (tempdir);
530*c2c66affSColin Finck     if (shorttempdir) {         /* try stable path for ZoneAlarm */
531*c2c66affSColin Finck         strstr (shorttempdir, "wct")[3] = 0;
532*c2c66affSColin Finck         if (CreateDirectoryA (shorttempdir, NULL)) {
533*c2c66affSColin Finck             free (tempdir);
534*c2c66affSColin Finck             tempdir = shorttempdir;
535*c2c66affSColin Finck         } else free (shorttempdir);
536*c2c66affSColin Finck     }
537*c2c66affSColin Finck     if (tempdir != shorttempdir && !CreateDirectoryA (tempdir, NULL))
538*c2c66affSColin Finck         report (R_FATAL, "Could not create directory: %s", tempdir);
539*c2c66affSColin Finck     report (R_DIR, tempdir);
540*c2c66affSColin Finck 
541*c2c66affSColin Finck     xprintf ("Version 4\n");
542*c2c66affSColin Finck     strres = extract_rcdata (MAKEINTRESOURCE(WINE_BUILD), STRINGRES, &strsize);
543*c2c66affSColin Finck     xprintf ("Tests from build ");
544*c2c66affSColin Finck     if (LoadStringA( 0, IDS_BUILD_ID, build, sizeof(build) )) xprintf( "%s\n", build );
545*c2c66affSColin Finck     else if (strres) xprintf ("%.*s", strsize, strres);
546*c2c66affSColin Finck     else xprintf ("-\n");
547*c2c66affSColin Finck     strres = extract_rcdata (MAKEINTRESOURCE(TESTS_URL), STRINGRES, &strsize);
548*c2c66affSColin Finck     xprintf ("Archive: ");
549*c2c66affSColin Finck     if (strres) xprintf ("%.*s", strsize, strres);
550*c2c66affSColin Finck     else xprintf ("-\n");
551*c2c66affSColin Finck     xprintf ("Tag: %s\n", tag);
552*c2c66affSColin Finck     xprintf ("Build info:\n");
553*c2c66affSColin Finck     strres = extract_rcdata (MAKEINTRESOURCE(BUILD_INFO), STRINGRES, &strsize);
554*c2c66affSColin Finck     while (strres) {
555*c2c66affSColin Finck         eol = memchr (strres, '\n', strsize);
556*c2c66affSColin Finck         if (!eol) {
557*c2c66affSColin Finck             nextline = NULL;
558*c2c66affSColin Finck             eol = strres + strsize;
559*c2c66affSColin Finck         } else {
560*c2c66affSColin Finck             strsize -= eol - strres + 1;
561*c2c66affSColin Finck             nextline = strsize?eol+1:NULL;
562*c2c66affSColin Finck             if (eol > strres && *(eol-1) == '\r') eol--;
563*c2c66affSColin Finck         }
564*c2c66affSColin Finck         xprintf ("    %.*s\n", eol-strres, strres);
565*c2c66affSColin Finck         strres = nextline;
566*c2c66affSColin Finck     }
567*c2c66affSColin Finck     xprintf ("Operating system version:\n");
568*c2c66affSColin Finck     print_version ();
569*c2c66affSColin Finck     xprintf ("Dll info:\n" );
570*c2c66affSColin Finck 
571*c2c66affSColin Finck     report (R_STATUS, "Counting tests");
572*c2c66affSColin Finck     if (!EnumResourceNames (NULL, MAKEINTRESOURCE(TESTRES),
573*c2c66affSColin Finck                             EnumTestFileProc, (LPARAM)&nr_of_files))
574*c2c66affSColin Finck         report (R_FATAL, "Can't enumerate test files: %d",
575*c2c66affSColin Finck                 GetLastError ());
576*c2c66affSColin Finck     wine_tests = xmalloc (nr_of_files * sizeof wine_tests[0]);
577*c2c66affSColin Finck 
578*c2c66affSColin Finck     report (R_STATUS, "Extracting tests");
579*c2c66affSColin Finck     report (R_PROGRESS, 0, nr_of_files);
580*c2c66affSColin Finck     nr_of_files = 0;
581*c2c66affSColin Finck     nr_of_tests = 0;
582*c2c66affSColin Finck     if (!EnumResourceNames (NULL, MAKEINTRESOURCE(TESTRES),
583*c2c66affSColin Finck                             extract_test_proc, (LPARAM)tempdir))
584*c2c66affSColin Finck         report (R_FATAL, "Can't enumerate test files: %d",
585*c2c66affSColin Finck                 GetLastError ());
586*c2c66affSColin Finck 
587*c2c66affSColin Finck     xprintf ("Test output:\n" );
588*c2c66affSColin Finck 
589*c2c66affSColin Finck     report (R_DELTA, 0, "Extracting: Done");
590*c2c66affSColin Finck 
591*c2c66affSColin Finck     report (R_STATUS, "Running tests");
592*c2c66affSColin Finck     report (R_PROGRESS, 1, nr_of_tests);
593*c2c66affSColin Finck     for (i = 0; i < nr_of_files; i++) {
594*c2c66affSColin Finck         struct wine_test *test = wine_tests + i;
595*c2c66affSColin Finck         int j;
596*c2c66affSColin Finck 
597*c2c66affSColin Finck 	for (j = 0; j < test->subtest_count; j++) {
598*c2c66affSColin Finck             report (R_STEP, "Running: %s:%s", test->name,
599*c2c66affSColin Finck                     test->subtests[j]);
600*c2c66affSColin Finck 	    run_test (test, test->subtests[j], tempdir);
601*c2c66affSColin Finck         }
602*c2c66affSColin Finck     }
603*c2c66affSColin Finck     report (R_DELTA, 0, "Running: Done");
604*c2c66affSColin Finck 
605*c2c66affSColin Finck     report (R_STATUS, "Cleaning up");
606*c2c66affSColin Finck     close (1);
607*c2c66affSColin Finck     remove_dir (tempdir);
608*c2c66affSColin Finck     free (tempdir);
609*c2c66affSColin Finck     free (wine_tests);
610*c2c66affSColin Finck 
611*c2c66affSColin Finck     return logname;
612*c2c66affSColin Finck }
613*c2c66affSColin Finck 
614*c2c66affSColin Finck static void
usage(void)615*c2c66affSColin Finck usage (void)
616*c2c66affSColin Finck {
617*c2c66affSColin Finck     fprintf (stderr,
618*c2c66affSColin Finck "Usage: winetest [OPTION]...\n\n"
619*c2c66affSColin Finck "  -c       console mode, no GUI\n"
620*c2c66affSColin Finck "  -e       preserve the environment\n"
621*c2c66affSColin Finck "  -h       print this message and exit\n"
622*c2c66affSColin Finck "  -p       shutdown when the tests are done\n"
623*c2c66affSColin Finck "  -q       quiet mode, no output at all\n"
624*c2c66affSColin Finck "  -o FILE  put report into FILE, do not submit\n"
625*c2c66affSColin Finck "  -s FILE  submit FILE, do not run tests\n"
626*c2c66affSColin Finck "  -t TAG   include TAG of characters [-.0-9a-zA-Z] in the report\n");
627*c2c66affSColin Finck }
628*c2c66affSColin Finck 
WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR cmdLine,int cmdShow)629*c2c66affSColin Finck int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst,
630*c2c66affSColin Finck                     LPSTR cmdLine, int cmdShow)
631*c2c66affSColin Finck {
632*c2c66affSColin Finck     char *logname = NULL;
633*c2c66affSColin Finck     const char *cp, *submit = NULL;
634*c2c66affSColin Finck     int reset_env = 1;
635*c2c66affSColin Finck     int poweroff = 0;
636*c2c66affSColin Finck     int interactive = 1;
637*c2c66affSColin Finck 
638*c2c66affSColin Finck     /* initialize the revision information first */
639*c2c66affSColin Finck     extract_rev_infos();
640*c2c66affSColin Finck 
641*c2c66affSColin Finck     cmdLine = strtok (cmdLine, whitespace);
642*c2c66affSColin Finck     while (cmdLine) {
643*c2c66affSColin Finck         if (cmdLine[0] != '-' || cmdLine[2]) {
644*c2c66affSColin Finck             report (R_ERROR, "Not a single letter option: %s", cmdLine);
645*c2c66affSColin Finck             usage ();
646*c2c66affSColin Finck             exit (2);
647*c2c66affSColin Finck         }
648*c2c66affSColin Finck         switch (cmdLine[1]) {
649*c2c66affSColin Finck         case 'c':
650*c2c66affSColin Finck             report (R_TEXTMODE);
651*c2c66affSColin Finck             interactive = 0;
652*c2c66affSColin Finck             break;
653*c2c66affSColin Finck         case 'e':
654*c2c66affSColin Finck             reset_env = 0;
655*c2c66affSColin Finck             break;
656*c2c66affSColin Finck         case 'h':
657*c2c66affSColin Finck         case '?':
658*c2c66affSColin Finck             usage ();
659*c2c66affSColin Finck             exit (0);
660*c2c66affSColin Finck         case 'p':
661*c2c66affSColin Finck             poweroff = 1;
662*c2c66affSColin Finck             break;
663*c2c66affSColin Finck         case 'q':
664*c2c66affSColin Finck             report (R_QUIET);
665*c2c66affSColin Finck             interactive = 0;
666*c2c66affSColin Finck             break;
667*c2c66affSColin Finck         case 's':
668*c2c66affSColin Finck             submit = strtok (NULL, whitespace);
669*c2c66affSColin Finck             if (tag)
670*c2c66affSColin Finck                 report (R_WARNING, "ignoring tag for submission");
671*c2c66affSColin Finck             send_file (submit);
672*c2c66affSColin Finck             break;
673*c2c66affSColin Finck         case 'o':
674*c2c66affSColin Finck             logname = strtok (NULL, whitespace);
675*c2c66affSColin Finck             break;
676*c2c66affSColin Finck         case 't':
677*c2c66affSColin Finck             tag = strtok (NULL, whitespace);
678*c2c66affSColin Finck             if (strlen (tag) > MAXTAGLEN)
679*c2c66affSColin Finck                 report (R_FATAL, "tag is too long (maximum %d characters)",
680*c2c66affSColin Finck                         MAXTAGLEN);
681*c2c66affSColin Finck             cp = findbadtagchar (tag);
682*c2c66affSColin Finck             if (cp) {
683*c2c66affSColin Finck                 report (R_ERROR, "invalid char in tag: %c", *cp);
684*c2c66affSColin Finck                 usage ();
685*c2c66affSColin Finck                 exit (2);
686*c2c66affSColin Finck             }
687*c2c66affSColin Finck             break;
688*c2c66affSColin Finck         default:
689*c2c66affSColin Finck             report (R_ERROR, "invalid option: -%c", cmdLine[1]);
690*c2c66affSColin Finck             usage ();
691*c2c66affSColin Finck             exit (2);
692*c2c66affSColin Finck         }
693*c2c66affSColin Finck         cmdLine = strtok (NULL, whitespace);
694*c2c66affSColin Finck     }
695*c2c66affSColin Finck     if (!submit) {
696*c2c66affSColin Finck         static CHAR platform_windows[]  = "WINETEST_PLATFORM=windows",
697*c2c66affSColin Finck                     platform_wine[]     = "WINETEST_PLATFORM=wine",
698*c2c66affSColin Finck                     debug_yes[]         = "WINETEST_DEBUG=1",
699*c2c66affSColin Finck                     interactive_no[]    = "WINETEST_INTERACTIVE=0",
700*c2c66affSColin Finck                     report_success_no[] = "WINETEST_REPORT_SUCCESS=0";
701*c2c66affSColin Finck         CHAR *platform;
702*c2c66affSColin Finck 
703*c2c66affSColin Finck         report (R_STATUS, "Starting up");
704*c2c66affSColin Finck 
705*c2c66affSColin Finck         if (!running_on_visible_desktop ())
706*c2c66affSColin Finck             report (R_FATAL, "Tests must be run on a visible desktop");
707*c2c66affSColin Finck 
708*c2c66affSColin Finck         platform = running_under_wine () ? platform_wine : platform_windows;
709*c2c66affSColin Finck 
710*c2c66affSColin Finck         if (reset_env && (putenv (platform) ||
711*c2c66affSColin Finck                           putenv (debug_yes)        ||
712*c2c66affSColin Finck                           putenv (interactive_no)   ||
713*c2c66affSColin Finck                           putenv (report_success_no)))
714*c2c66affSColin Finck             report (R_FATAL, "Could not reset environment: %d", errno);
715*c2c66affSColin Finck 
716*c2c66affSColin Finck         if (!tag) {
717*c2c66affSColin Finck             if (!interactive)
718*c2c66affSColin Finck                 report (R_FATAL, "Please specify a tag (-t option) if "
719*c2c66affSColin Finck                         "running noninteractive!");
720*c2c66affSColin Finck             if (guiAskTag () == IDABORT) exit (1);
721*c2c66affSColin Finck         }
722*c2c66affSColin Finck         report (R_TAG);
723*c2c66affSColin Finck 
724*c2c66affSColin Finck         if (!logname) {
725*c2c66affSColin Finck             logname = run_tests (NULL);
726*c2c66affSColin Finck             if (report (R_ASK, MB_YESNO, "Do you want to submit the "
727*c2c66affSColin Finck                         "test results?") == IDYES)
728*c2c66affSColin Finck                 if (!send_file (logname) && remove (logname))
729*c2c66affSColin Finck                     report (R_WARNING, "Can't remove logfile: %d.", errno);
730*c2c66affSColin Finck             free (logname);
731*c2c66affSColin Finck         } else run_tests (logname);
732*c2c66affSColin Finck         report (R_STATUS, "Finished");
733*c2c66affSColin Finck     }
734*c2c66affSColin Finck     if (poweroff)
735*c2c66affSColin Finck     {
736*c2c66affSColin Finck         HANDLE hToken;
737*c2c66affSColin Finck         TOKEN_PRIVILEGES npr;
738*c2c66affSColin Finck 
739*c2c66affSColin Finck         /* enable the shutdown privilege for the current process */
740*c2c66affSColin Finck         if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
741*c2c66affSColin Finck         {
742*c2c66affSColin Finck             LookupPrivilegeValueA(0, SE_SHUTDOWN_NAME, &npr.Privileges[0].Luid);
743*c2c66affSColin Finck             npr.PrivilegeCount = 1;
744*c2c66affSColin Finck             npr.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
745*c2c66affSColin Finck             AdjustTokenPrivileges(hToken, FALSE, &npr, 0, 0, 0);
746*c2c66affSColin Finck             CloseHandle(hToken);
747*c2c66affSColin Finck         }
748*c2c66affSColin Finck         ExitWindowsEx(EWX_SHUTDOWN | EWX_POWEROFF | EWX_FORCEIFHUNG, SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER);
749*c2c66affSColin Finck     }
750*c2c66affSColin Finck     exit (0);
751*c2c66affSColin Finck }
752