foreach: Avoid memory leak in case of error
[jimtcl.git] / jim-win32.c
blob33e7117db09e13bdcee809e9c39c6ae67c2ec8e9
1 /*
2 * WIN32 extension
4 * Copyright (C) 2005 Pat Thoyts <patthoyts@users.sourceforge.net>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 * The views and conclusions contained in the software and documentation
31 * are those of the authors and should not be interpreted as representing
32 * official policies, either expressed or implied, of the Jim Tcl Project.
35 #include <jim.h>
37 /* Apparently windows.h and cygwin don't mix, but we seem to get
38 * away with it here. Use at your own risk under cygwin
40 #if defined(__CYGWIN__) || defined(__MINGW32__)
41 #define WIN32_LEAN_AND_MEAN
42 #include <windows.h>
43 #endif
45 #include <shellapi.h>
46 #include <lmcons.h>
47 #include <psapi.h>
48 #include <ctype.h>
50 #if _MSC_VER >= 1000
51 #pragma comment(lib, "shell32")
52 #pragma comment(lib, "user32")
53 #pragma comment(lib, "advapi32")
54 #pragma comment(lib, "psapi")
55 #endif /* _MSC_VER >= 1000 */
57 #if _WIN32_WINNT < 0x600
58 #define GetTickCount64 GetTickCount
59 #endif
61 static Jim_Obj *
62 Win32ErrorObj(Jim_Interp *interp, const char * szPrefix, DWORD dwError)
64 Jim_Obj *msgObj = NULL;
65 char * lpBuffer = NULL;
66 DWORD dwLen = 0;
68 dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
69 | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, LANG_NEUTRAL,
70 (char *)&lpBuffer, 0, NULL);
71 if (dwLen < 1) {
72 dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
73 | FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
74 "code 0x%1!08X!%n", 0, LANG_NEUTRAL,
75 (char *)&lpBuffer, 0, (va_list *)&dwError);
78 msgObj = Jim_NewStringObj(interp, szPrefix, -1);
79 if (dwLen > 0) {
80 char *p = lpBuffer + dwLen - 1; /* remove cr-lf at end */
81 for ( ; p && *p && isspace(UCHAR(*p)); p--)
83 *++p = 0;
84 Jim_AppendString(interp, msgObj, ": ", 2);
85 Jim_AppendString(interp, msgObj, lpBuffer, -1);
87 LocalFree((HLOCAL)lpBuffer);
88 return msgObj;
91 /* win32.ShellExecute verb file args */
92 static int
93 Win32_ShellExecute(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
95 ptrdiff_t r;
96 const char *verb, *file, *parm = NULL;
97 char cwd[MAX_PATH + 1];
99 if (objc < 3 || objc > 4) {
100 Jim_WrongNumArgs(interp, 1, objv, "verb path ?parameters?");
101 return JIM_ERR;
103 verb = Jim_String(objv[1]);
104 file = Jim_String(objv[2]);
105 GetCurrentDirectoryA(MAX_PATH + 1, cwd);
106 if (objc == 4)
107 parm = Jim_String(objv[3]);
108 r = (ptrdiff_t)ShellExecuteA(NULL, verb, file, parm, cwd, SW_SHOWNORMAL);
109 if (r < 33)
110 Jim_SetResult(interp,
111 Win32ErrorObj(interp, "ShellExecute", GetLastError()));
112 return (r < 33) ? JIM_ERR : JIM_OK;
116 /* win32.FindWindow title ?class? */
117 static int
118 Win32_FindWindow(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
120 const char *title = NULL, *class = NULL;
121 HWND hwnd = NULL;
122 int r = JIM_OK;
124 if (objc < 2 || objc > 3) {
125 Jim_WrongNumArgs(interp, 1, objv, "title ?class?");
126 return JIM_ERR;
128 title = Jim_String(objv[1]);
129 if (objc == 3)
130 class = Jim_String(objv[2]);
131 hwnd = FindWindowA(class, title);
133 if (hwnd == NULL) {
134 Jim_SetResult(interp,
135 Win32ErrorObj(interp, "FindWindow", GetLastError()));
136 r = JIM_ERR;
137 } else {
138 Jim_SetResult(interp, Jim_NewIntObj(interp, (long)hwnd));
140 return r;
143 /* win32.CloseWindow windowHandle */
144 static int
145 Win32_CloseWindow(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
147 long hwnd;
149 if (objc != 2) {
150 Jim_WrongNumArgs(interp, 1, objv, "?windowHandle?");
151 return JIM_ERR;
153 if (Jim_GetLong(interp, objv[1], &hwnd) != JIM_OK)
154 return JIM_ERR;
155 if (!CloseWindow((HWND)hwnd)) {
156 Jim_SetResult(interp,
157 Win32ErrorObj(interp, "CloseWindow", GetLastError()));
158 return JIM_ERR;
160 return JIM_OK;
163 static int
164 Win32_GetActiveWindow(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
166 Jim_SetResult(interp, Jim_NewIntObj(interp, (ptrdiff_t)GetActiveWindow()));
167 return JIM_OK;
170 static int
171 Win32_SetActiveWindow(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
173 HWND hwnd, old;
174 int r = JIM_OK;
176 if (objc != 2) {
177 Jim_WrongNumArgs(interp, 1, objv, "windowHandle");
178 return JIM_ERR;
180 r = Jim_GetLong(interp, objv[1], (long *)&hwnd);
181 if (r == JIM_OK) {
182 old = SetActiveWindow(hwnd);
183 if (old == NULL) {
184 Jim_SetResult(interp,
185 Win32ErrorObj(interp, "SetActiveWindow", GetLastError()));
186 r = JIM_ERR;
187 } else {
188 Jim_SetResult(interp, Jim_NewIntObj(interp, (long)old));
191 return r;
194 static int
195 Win32_SetForegroundWindow(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
197 HWND hwnd;
198 int r = JIM_OK;
200 if (objc != 2) {
201 Jim_WrongNumArgs(interp, 1, objv, "windowHandle");
202 return JIM_ERR;
204 r = Jim_GetLong(interp, objv[1], (long *)&hwnd);
205 if (r == JIM_OK) {
206 if (!SetForegroundWindow(hwnd)) {
207 Jim_SetResult(interp,
208 Win32ErrorObj(interp, "SetForegroundWindow", GetLastError()));
209 r = JIM_ERR;
212 return r;
215 static int
216 Win32_Beep(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
218 long freq, duration;
219 int r = JIM_OK;
221 if (objc != 3) {
222 Jim_WrongNumArgs(interp, 1, objv, "freq duration");
223 return JIM_ERR;
225 r = Jim_GetLong(interp, objv[1], &freq);
226 if (r == JIM_OK)
227 r = Jim_GetLong(interp, objv[2], &duration);
228 if (freq < 0x25) freq = 0x25;
229 if (freq > 0x7fff) freq = 0x7fff;
230 if (r == JIM_OK) {
231 if (!Beep(freq, duration)) {
232 Jim_SetResult(interp,
233 Win32ErrorObj(interp, "Beep", GetLastError()));
234 r = JIM_ERR;
237 return r;
240 static int
241 Win32_GetComputerName(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
243 char name[MAX_COMPUTERNAME_LENGTH + 1];
244 DWORD size = MAX_COMPUTERNAME_LENGTH;
245 int r = JIM_OK;
247 if (objc != 1) {
248 Jim_WrongNumArgs(interp, 1, objv, "");
249 return JIM_ERR;
252 if (GetComputerNameA(name, &size)) {
253 Jim_Obj *nameObj = Jim_NewStringObj(interp, name, size);
254 Jim_SetResult(interp, nameObj);
255 } else {
256 Jim_SetResult(interp,
257 Win32ErrorObj(interp, "GetComputerName", GetLastError()));
258 r = JIM_ERR;
261 return r;
264 static int
265 Win32_GetUserName(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
267 char name[UNLEN + 1];
268 DWORD size = UNLEN;
269 int r = JIM_OK;
271 if (objc != 1) {
272 Jim_WrongNumArgs(interp, 1, objv, "");
273 return JIM_ERR;
276 if (GetUserNameA(name, &size)) {
277 Jim_Obj *nameObj = Jim_NewStringObj(interp, name, size);
278 Jim_SetResult(interp, nameObj);
279 } else {
280 Jim_SetResult(interp,
281 Win32ErrorObj(interp, "GetUserName", GetLastError()));
282 r = JIM_ERR;
285 return r;
288 static int
289 Win32_GetModuleFileName(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
291 HMODULE hModule = NULL;
292 char path[MAX_PATH];
293 DWORD len = 0;
295 if (objc > 2) {
296 Jim_WrongNumArgs(interp, 1, objv, "?moduleid?");
297 return JIM_ERR;
300 if (objc == 2) {
301 if (Jim_GetLong(interp, objv[1], (long *)&hModule) != JIM_OK) {
302 return JIM_ERR;
306 len = GetModuleFileNameA(hModule, path, MAX_PATH);
307 if (len != 0) {
308 Jim_Obj *pathObj = Jim_NewStringObj(interp, path, len);
309 Jim_SetResult(interp, pathObj);
310 } else {
311 Jim_SetResult(interp,
312 Win32ErrorObj(interp, "GetModuleFileName", GetLastError()));
313 return JIM_ERR;
316 return JIM_OK;
319 static int
320 Win32_GetVersion(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
322 Jim_SetResult(interp, Jim_NewIntObj(interp, GetVersion()));
323 return JIM_OK;
326 static int
327 Win32_GetTickCount(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
329 Jim_SetResult(interp, Jim_NewIntObj(interp, GetTickCount64()));
330 return JIM_OK;
333 static int
334 Win32_GetSystemTime(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
336 Jim_Obj *a[16];
337 size_t n = 0;
338 SYSTEMTIME t;
339 GetSystemTime(&t);
341 #define JIMADD(name) \
342 a[n++] = Jim_NewStringObj(interp, #name, -1); \
343 a[n++] = Jim_NewIntObj(interp, t.w ## name )
345 JIMADD(Year);
346 JIMADD(Month);
347 JIMADD(DayOfWeek);
348 JIMADD(Day);
349 JIMADD(Hour);
350 JIMADD(Minute);
351 JIMADD(Second);
352 JIMADD(Milliseconds);
353 #undef JIMADD
355 Jim_SetResult(interp, Jim_NewListObj(interp, a, n));
356 return JIM_OK;
359 /* function not available on mingw or cygwin */
360 #if !defined(__MINGW32__) && !defined(__CYGWIN__)
361 // FIX ME: win2k+ so should do version checks really.
362 static int
363 Win32_GetPerformanceInfo(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
365 Jim_Obj *a[26];
366 size_t n = 0;
367 PERFORMANCE_INFORMATION pi;
369 if (!GetPerformanceInfo(&pi, sizeof(pi))) {
370 Jim_SetResult(interp,
371 Win32ErrorObj(interp, "GetPerformanceInfo", GetLastError()));
372 return JIM_ERR;
375 #define JIMADD(name) \
376 a[n++] = Jim_NewStringObj(interp, #name, -1); \
377 a[n++] = Jim_NewIntObj(interp, pi. name )
379 JIMADD(CommitTotal);
380 JIMADD(CommitLimit);
381 JIMADD(CommitPeak);
382 JIMADD(PhysicalTotal);
383 JIMADD(PhysicalAvailable);
384 JIMADD(SystemCache);
385 JIMADD(KernelTotal);
386 JIMADD(KernelPaged);
387 JIMADD(KernelNonpaged);
388 JIMADD(PageSize);
389 JIMADD(HandleCount);
390 JIMADD(ProcessCount);
391 JIMADD(ThreadCount);
392 #undef JIMADD
394 Jim_SetResult(interp, Jim_NewListObj(interp, a, n));
395 return JIM_OK;
397 #endif
399 static int
400 Win32_SetComputerName(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
402 int r = JIM_OK;
403 const char *name;
404 if (objc != 2) {
405 Jim_WrongNumArgs(interp, 1, objv, "computername");
406 return JIM_ERR;
408 name = Jim_String(objv[1]);
409 if (!SetComputerNameA(name)) {
410 Jim_SetResult(interp,
411 Win32ErrorObj(interp, "SetComputerName", GetLastError()));
412 r = JIM_ERR;
414 return r;
417 static int
418 Win32_GetModuleHandle(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
420 HMODULE hModule = NULL;
421 const char *name = NULL;
423 if (objc < 1 || objc > 2) {
424 Jim_WrongNumArgs(interp, 1, objv, "?name?");
425 return JIM_ERR;
427 if (objc == 2)
428 name = Jim_String(objv[1]);
429 hModule = GetModuleHandleA(name);
430 if (hModule == NULL) {
431 Jim_SetResult(interp,
432 Win32ErrorObj(interp, "GetModuleHandle", GetLastError()));
433 return JIM_ERR;
435 Jim_SetResult(interp, Jim_NewIntObj(interp, (unsigned long)hModule));
436 return JIM_OK;
439 static int
440 Win32_LoadLibrary(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
442 HMODULE hLib = NULL;
443 if (objc != 2) {
444 Jim_WrongNumArgs(interp, 1, objv, "path");
445 return JIM_ERR;
447 hLib = LoadLibraryA(Jim_String(objv[1]));
448 if (hLib == NULL) {
449 Jim_SetResult(interp,
450 Win32ErrorObj(interp, "LoadLibrary", GetLastError()));
451 return JIM_ERR;
453 Jim_SetResult(interp, Jim_NewIntObj(interp, (unsigned long)hLib));
454 return JIM_OK;
457 static int
458 Win32_FreeLibrary(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
460 HMODULE hModule = NULL;
461 int r = JIM_OK;
463 if (objc != 2) {
464 Jim_WrongNumArgs(interp, 1, objv, "hmodule");
465 return JIM_ERR;
468 r = Jim_GetLong(interp, objv[1], (long *)&hModule);
469 if (r == JIM_OK) {
470 if (!FreeLibrary(hModule)) {
471 Jim_SetResult(interp,
472 Win32ErrorObj(interp, "FreeLibrary", GetLastError()));
473 r = JIM_ERR;
477 return r;
480 /* win32.MessageBox message title ?type? */
481 static int
482 Win32_MessageBox(Jim_Interp *interp, int objc, Jim_Obj * const *objv)
484 int r;
485 const char *message, *title;
486 long int type = 0;
488 if (objc < 3 || objc > 4) {
489 Jim_WrongNumArgs(interp, 1, objv, "message title ?type?");
490 return JIM_ERR;
492 message = Jim_String(objv[1]);
493 title = Jim_String(objv[2]);
494 if (objc == 4) {
495 if (Jim_GetLong(interp, objv[3], &type) != JIM_OK)
496 return JIM_ERR;
498 r = (int) MessageBoxA(NULL, message, title, (int)type);
499 Jim_SetResultInt(interp, r);
500 return JIM_OK;
504 /* ---------------------------------------------------------------------- */
507 Jim_win32Init(Jim_Interp *interp)
509 if (Jim_PackageProvide(interp, "win32", "1.0", JIM_ERRMSG))
510 return JIM_ERR;
512 #define CMD(name) \
513 Jim_CreateCommand(interp, "win32." #name , Win32_ ## name , NULL, NULL)
515 CMD(ShellExecute);
516 CMD(FindWindow);
517 CMD(CloseWindow);
518 CMD(GetActiveWindow);
519 CMD(SetActiveWindow);
520 CMD(SetForegroundWindow);
521 CMD(Beep);
522 CMD(GetComputerName);
523 CMD(SetComputerName);
524 CMD(GetUserName);
525 CMD(GetModuleFileName);
526 CMD(GetVersion);
527 CMD(GetTickCount);
528 CMD(GetSystemTime);
529 #if !defined(__MINGW32__) && !defined(__CYGWIN__)
530 CMD(GetPerformanceInfo);
531 #endif
532 CMD(GetModuleHandle);
533 CMD(LoadLibrary);
534 CMD(FreeLibrary);
535 CMD(MessageBox);
537 return JIM_OK;