rxwinsys.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2014 Rexx Language Association. All rights reserved. */
5 /* */
6 /* This program and the accompanying materials are made available under */
7 /* the terms of the Common Public License v1.0 which accompanies this */
8 /* distribution. A copy is also available at the following address: */
9 /* http://www.oorexx.org/license.html */
10 /* */
11 /* Redistribution and use in source and binary forms, with or */
12 /* without modification, are permitted provided that the following */
13 /* conditions are met: */
14 /* */
15 /* Redistributions of source code must retain the above copyright */
16 /* notice, this list of conditions and the following disclaimer. */
17 /* Redistributions in binary form must reproduce the above copyright */
18 /* notice, this list of conditions and the following disclaimer in */
19 /* the documentation and/or other materials provided with the distribution. */
20 /* */
21 /* Neither the name of Rexx Language Association nor the names */
22 /* of its contributors may be used to endorse or promote products */
23 /* derived from this software without specific prior written permission. */
24 /* */
25 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
26 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
27 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
28 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
29 /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
30 /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
31 /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
32 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
33 /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
34 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
35 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 /* */
37 /*----------------------------------------------------------------------------*/
38 
39 #include <windows.h>
40 #include "oorexxapi.h"
41 #include <stdio.h>
42 #include <string.h>
43 #include <ddeml.h>
44 #include <time.h>
45 #include <shlobj.h>
46 #include <shlwapi.h>
47 #include <algorithm>
48 
49 #define STR_BUFFER 256
50 #define MAX_TIME_DATE 128
51 
52 // The OS specifies a maximum size for a registry key name as 255.
53 #define MAX_REGISTRY_KEY_SIZE 256
54 
55 #define MSG_TIMEOUT 5000 // 5000ms
56 #if (WINVER >= 0x0500)
57 #define MSG_TIMEOUT_OPTS (SMTO_ABORTIFHUNG|SMTO_NORMAL|SMTO_NOTIMEOUTIFNOTHUNG)
58 #else
59 #define MSG_TIMEOUT_OPTS (SMTO_ABORTIFHUNG|SMTO_NORMAL)
60 #endif
61 
62 
63 /*********************************************************************/
64 /* */
65 /* Subroutine Name: memupper */
66 /* */
67 /* Descriptive Name: uppercase a memory location */
68 /* */
69 /* Entry Point: memupper */
70 /* */
71 /* Input: memory to upper case */
72 /* length of memory location */
73 /* */
74 /*********************************************************************/
75 
76 void memupper(
77  char *location, /* location to uppercase */
78  size_t length) /* length to uppercase */
79 {
80  for (; length--; location++) /* loop for entire string */
81  /* uppercase in place */
82  *location = toupper(*location);
83 }
84 
85 
86 /*********************************************************************/
87 /* Numeric Return calls */
88 /*********************************************************************/
89 
90 #define INVALID_ROUTINE 40 /* Raise Rexx error */
91 #define VALID_ROUTINE 0 /* Successful completion */
92 
93 VOID Little2BigEndian(BYTE *pbInt, INT iSize);
94 
95 size_t dwordPtrToRexx(DWORD_PTR val, PRXSTRING r)
96 {
97  _snprintf(r->strptr, RXAUTOBUFLEN, "%Iu", val);
98  r->strlength = strlen(r->strptr);
99  return 0;
100 }
101 
102 LONG HandleArgError(PRXSTRING r, BOOL ToMuch)
103 {
104  r->strlength = 2;
105  r->strptr[0] = '4';
106  r->strptr[1] = '0';
107  r->strptr[2] = '\0';
108  return 40;
109 }
110 
111 #define CHECKARG(argexpctl, argexpcth) \
112  if ((argc < argexpctl) || (argc > argexpcth)) return HandleArgError(retstr, (argc > argexpcth))
113 
114 
115 /* macros for a easier return code */
116 #define RETC(retcode) { \
117  retstr->strlength = 1;\
118  if (retcode) retstr->strptr[0] = '1'; else retstr->strptr[0] = '0'; \
119  retstr->strptr[1] = '\0'; \
120  return 0; \
121  }
122 
123 #define RETERR { \
124  retstr->strlength = 1;\
125  retstr->strptr[0] = '1'; \
126  retstr->strptr[1] = '\0'; \
127  return 40; \
128  }
129 
130 
131 #define RETVAL(retvalue) { \
132  itoa(retvalue, retstr->strptr, 10); \
133  retstr->strlength = strlen(retstr->strptr);\
134  return 0; \
135  }
136 
137 
138 #define RET_HANDLE(retvalue) { \
139  pointer2string(retstr, retvalue); \
140  return 0; \
141  }
142 
143 
144 
145 /* Note many existing programs abbreviate HKEY_LOCAL_MACHINE to "LOCAL_MACHINE",
146  * or "MACHINE", and many do not. Many existing programs use the full
147  * HKEY_LOCAL_MACHINE. So the comparison needs to remain strstr.
148  */
149 #define GET_HKEY(argum, ghk) { \
150  ghk = NULL; \
151  if (strstr(argum,"MACHINE")) ghk = HKEY_LOCAL_MACHINE; else \
152  if (strstr(argum,"CLASSES")) ghk = HKEY_CLASSES_ROOT; else \
153  if (strstr(argum,"CURRENT_USER")) ghk = HKEY_CURRENT_USER; else \
154  if (strstr(argum,"USERS")) ghk = HKEY_USERS; else \
155  if (strstr(argum,"PERFORMANCE")) ghk = HKEY_PERFORMANCE_DATA; else \
156  if (strstr(argum,"CURRENT_CONFIG")) ghk = HKEY_CURRENT_CONFIG; else \
157  if (strstr(argum,"DYN_DATA")) ghk = HKEY_DYN_DATA; else \
158  string2pointer(argum, (void **)&ghk); \
159 }
160 
161 
162 #define GET_HANDLE(argum, ghk) string2pointer(argum, (void **)&(ghk))
163 
164 
165 #define SET_VARIABLE(varname, data, retc) {\
166  shvb.shvnext = NULL; \
167  shvb.shvname.strptr = varname; \
168  shvb.shvname.strlength = strlen(varname); \
169  shvb.shvnamelen = shvb.shvname.strlength; \
170  shvb.shvvalue.strptr = data; \
171  shvb.shvvalue.strlength = strlen(data); \
172  shvb.shvvaluelen = strlen(data); \
173  shvb.shvcode = RXSHV_SYSET; \
174  shvb.shvret = 0; \
175  if (RexxVariablePool(&shvb) == RXSHV_BADN) RETC(retc); \
176  }
177 
178 
179 #define GET_TYPE_INDEX(type, index) \
180 { \
181  switch (type) \
182  { \
183  case EVENTLOG_ERROR_TYPE: \
184  index=0; \
185  break; \
186  case EVENTLOG_WARNING_TYPE: \
187  index=1; \
188  break; \
189  case EVENTLOG_INFORMATION_TYPE: \
190  index=2; \
191  break; \
192  case EVENTLOG_SUCCESS: \
193  index=2; \
194  break; \
195  case EVENTLOG_AUDIT_SUCCESS: \
196  index=3; \
197  break; \
198  case EVENTLOG_AUDIT_FAILURE: \
199  index=4; \
200  break; \
201  default: \
202  index=5; \
203  } \
204 }
205 
206 bool inline isHex(CSTRING value)
207 {
208  return ((value[0] == '0') && (toupper(value[1]) == 'X'));
209 }
210 
211 
212 /********************************************************************
213 * Function: string2pointer(string) *
214 * *
215 * Purpose: Validates and converts an ASCII-Z string from string *
216 * form to a pointer value. Returns false if the number *
217 * is not valid, true if the number was successfully *
218 * converted. *
219 * *
220 * RC: true - Good number converted *
221 * false - Invalid number supplied. *
222 *********************************************************************/
223 
225  const char *string, /* string to convert */
226  void **pointer) /* converted number */
227 {
228  if ( strlen(string) == 0 )
229  {
230  *pointer = NULL;
231  return FALSE;
232  }
233 
234  if ( isHex(string) )
235  {
236  return (string[1] == 'x' ?
237  sscanf(string, "0x%p", pointer) == 1 : sscanf(string, "0X%p", pointer) == 1);
238  }
239 
240  return sscanf(string, "%p", pointer) == 1;
241 }
242 
243 
244 void pointer2string(PRXSTRING result, void *pointer)
245 {
246  if ( pointer == NULL )
247  {
248  result->strlength = 1;
249  result->strptr[0] = '0';
250  result->strptr[1] = '\0';
251  }
252  else
253  {
254  sprintf(result->strptr, "0x%p", pointer);
255  result->strlength = strlen(result->strptr);
256  }
257 }
258 
259 
261 {
262  OSVERSIONINFO version_info={0};
263 
264  version_info.dwOSVersionInfoSize = sizeof(version_info);
265  GetVersionEx(&version_info);
266  if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT) return TRUE; // Windows NT
267  else return FALSE; // Windows 95
268 }
269 
270 
271 // TODO START The following functions come from from oodCommon.cpp, need to put
272 // all this stuff all together in a common object.
273 
274 /**
275  * Converts a pointer-sized type to a pointer-string, or 0 if the type is null.
276  *
277  * @param result [out] Pointer-string is returned here. Ensure the storage
278  * pointed to is big enough for a 64-bit pointer.
279  *
280  * @param pointer [in] The pointer, or pointer-sized type to convert.
281  *
282  * @remarks Pointer-sized type is used to indicate that this will work for
283  * opaque types, like HANDLE, HMENU, HINST, UINT_PTR, DWORD_PTR, etc.,
284  * that are pointer size.
285  *
286  * For now, 0 is returned for null rather than 0x00000000 because
287  * many, many places in the Windows specific classes test for 0 to
288  * detect error.
289  *
290  * This function should go away when the Windows classes are converted
291  * to use .Pointer for all pointer-sized data types.
292  */
293 void pointer2string(char *result, void *pointer)
294 {
295  if ( pointer == NULL )
296  {
297  sprintf(result, "0");
298  }
299  else
300  {
301  sprintf(result, "0x%p", pointer);
302  }
303 }
304 
305 
306 /**
307  * Variation of above. Converts the pointer and returns it as a
308  * RexxStringObject.
309  *
310  * @param c Method context we are operating in.
311  * @param pointer Pointer to convert
312  *
313  * @return A string object representing the pointer as either 0xffff1111 if not
314  * null or as 0 if null.
315  */
317 {
318  char buf[32];
319  pointer2string(buf, pointer);
320  return c->String(buf);
321 }
322 
323 // TODO END
324 
325 
326 /**
327  * Resolves a, possibly omitted, registry key handle in string form to a HKEY.
328  *
329  * Most of the registry functions require an open 'parent' key handle. Most of
330  * the WindowsRegistry methods allow the user to omitt the parent key handle, in
331  * which case the stored 'current key' value is used.
332  *
333  * @param c Method context we are operating under.
334  * @param hkParent The parent key value to resolve.
335  *
336  * @return A HKEY resolved from hkParent, which could be the null value.
337  */
339 {
340  HKEY hk = NULL;
341 
342  if ( hkParent == NULL )
343  {
344  hkParent = "";
345  RexxObjectPtr rxHK = c->GetObjectVariable("CURRENT_KEY");
346  if ( rxHK != NULLOBJECT )
347  {
348  hkParent = c->ObjectToStringValue(rxHK);
349  }
350  }
351  else if ( ! isHex(hkParent) )
352  {
353  if ( StrStrI(hkParent, "MACHINE") ) hk = HKEY_LOCAL_MACHINE;
354  else if ( StrStrI(hkParent, "CLASSES") ) hk = HKEY_CLASSES_ROOT;
355  else if ( StrStrI(hkParent, "CURRENT_USER") ) hk = HKEY_CURRENT_USER;
356  else if ( StrStrI(hkParent, "USERS") ) hk = HKEY_USERS;
357  else if ( StrStrI(hkParent, "PERFORMANCE") ) hk = HKEY_PERFORMANCE_DATA;
358  else if ( StrStrI(hkParent, "CURRENT_CONFIG") ) hk = HKEY_CURRENT_CONFIG;
359  else if ( StrStrI(hkParent, "DYN_DATA") ) hk = HKEY_DYN_DATA;
360  }
361 
362  if ( hk == NULL )
363  {
364  string2pointer(hkParent, (void **)&hk);
365  }
366  return hk;
367 }
368 
369 
370 RexxMethod0(RexxObjectPtr, WSRegistry_init)
371 {
372  context->SetObjectVariable("CURRENT_KEY", pointer2string(context, HKEY_LOCAL_MACHINE));
373  return NULLOBJECT;
374 }
375 
376 RexxMethod0(RexxObjectPtr, getCurrent_Key)
377 {
378  return context->GetObjectVariable("CURRENT_KEY");
379 }
380 
381 RexxMethod1(RexxObjectPtr, setCurrent_Key, RexxStringObject, regKeyHandle)
382 {
383  context->SetObjectVariable("CURRENT_KEY", regKeyHandle);
384  return NULLOBJECT;
385 }
386 
387 RexxMethod0(POINTERSTRING, getLocal_Machine)
388 {
389  return HKEY_LOCAL_MACHINE;
390 }
391 
392 RexxMethod0(POINTERSTRING, getCurrent_User)
393 {
394  return HKEY_CURRENT_USER;
395 }
396 
397 RexxMethod0(POINTERSTRING, getUsers)
398 {
399  return HKEY_USERS;
400 }
401 
402 RexxMethod0(POINTERSTRING, getClasses_Root)
403 {
404  return HKEY_CLASSES_ROOT;
405 }
406 
407 RexxMethod0(POINTERSTRING, getPerformance_Data)
408 {
409  return HKEY_PERFORMANCE_DATA;
410 }
411 
412 RexxMethod0(POINTERSTRING, getCurrent_Config)
413 {
414  return HKEY_CURRENT_CONFIG;
415 }
416 
417 /** WindowsRegistry::delete() | WindowsRegistry::deleteKey()
418  *
419  * Deletes a registry key. Maps to both the delete() and the deleteKey()
420  * methods.
421  *
422  * delete() deletes a subkey and all its descendents (subkeys.) deleteKey()
423  * will only delete the subkey if it is empty, i.e. it contains no subkeys.
424  *
425  * @param hkParent [optional] A handle to an open registry key, or the name
426  * of one of the prefined, always open, registry keys. The
427  * key must have been opened with the DELETE access right.
428  * If this argument is omitted then the CURRENT_KEY
429  * attribute is used.
430  *
431  * @param subkeyName The name of the subkey to be deleted. The name is case
432  * insensitive.
433  *
434  * @return O on success, otherwise the Windows system error code.
435  */
436 RexxMethod2(uint32_t, WSRegistry_delete, OPTIONAL_CSTRING, hkParent, CSTRING, subKeyName)
437 {
438  HKEY hk = getParentKeyHandle(context, hkParent);
439 
440  if ( strcmp(context->GetMessageName(), "DELETEKEY") == 0 )
441  {
442  return RegDeleteKey(hk, subKeyName);
443  }
444  else
445  {
446  return SHDeleteKey(hk, subKeyName);
447  }
448 }
449 
450 /** WindowsRegistry::open()
451  *
452  * Opens a subkey with the specified access rights. When the open is
453  * successful, the CURRENT_KEY attribute is set to the opened key.
454  *
455  * @param hkParent [optional] A handle to an open registry key, or the name
456  * of one of the prefined, always open, registry keys. The
457  * key must have been opened with the DELETE access right.
458  * If this argument is omitted then the CURRENT_KEY
459  * attribute is used.
460  *
461  * @param subkeyName [optional] The name of the subkey to be opened. The name
462  * is case insensitive. If this argument is omitted or the
463  * empty string, the operating system will open a new handle
464  * to the key identified by hkParent.
465  *
466  * @param access [optional] A string of 0 or more keywords specifying the
467  * desired access rights. The default if this argument is
468  * omitted is all access. Specifying a higher level of
469  * access than the user has, will cause the open to fail.
470  *
471  */
472 RexxMethod3(RexxObjectPtr, WSRegistry_open, OPTIONAL_CSTRING, hkParent, OPTIONAL_CSTRING, subKeyName, OPTIONAL_CSTRING, access)
473 {
474  RexxMethodContext *c = context;
475  DWORD dwAccess = 0;
476  HKEY hk = getParentKeyHandle(context, hkParent);
477 
478  // Docs say, have always said, that the access arg can be more than one
479  // keyword. So, even if "ALL" makes the other keywords unnecessary, we can't
480  // rely on it being the only word in the string.
481  if ( argumentOmitted(3) || StrStrI(access, "ALL") != 0 )
482  {
483  dwAccess = KEY_ALL_ACCESS;
484  }
485  else
486  {
487  if (StrStrI(access, "WRITE")) dwAccess |= KEY_WRITE;
488  if (StrStrI(access, "READ")) dwAccess |= KEY_READ;
489  if (StrStrI(access, "QUERY")) dwAccess |= KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS;
490  if (StrStrI(access, "INQUIRE")) dwAccess |= KEY_QUERY_VALUE;
491  if (StrStrI(access, "ENUMERATE")) dwAccess |= KEY_ENUMERATE_SUB_KEYS;
492  if (StrStrI(access, "SET")) dwAccess |= KEY_SET_VALUE;
493  if (StrStrI(access, "DELETE")) dwAccess |= KEY_SET_VALUE;
494  if (StrStrI(access, "CREATE")) dwAccess |= KEY_CREATE_SUB_KEY;
495  if (StrStrI(access, "NOTIFY")) dwAccess |= KEY_NOTIFY;
496  if (StrStrI(access, "EXECUTE")) dwAccess |= KEY_EXECUTE;
497  if (StrStrI(access, "LINK")) dwAccess |= KEY_CREATE_LINK; // reserved for system use only.
498  }
499 
500  RexxObjectPtr result = c->WholeNumber(0);
501  HKEY hkResult = NULL;
502 
503  if ( RegOpenKeyEx(hk, subKeyName, 0, dwAccess, &hkResult ) == ERROR_SUCCESS)
504  {
505  result = pointer2string(context, hkResult);
506  c->SetObjectVariable("CURRENT_KEY", result);
507  }
508  return result;
509 }
510 
511 size_t RexxEntry WSRegistryKey(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
512 {
513  HKEY hk;
514 
515  CHECKARG(2,5);
516 
517  if ( strcmp(argv[0].strptr, "CREATE") == 0 )
518  {
519  HKEY hkResult;
520 
521  GET_HANDLE(argv[1].strptr, hk);
522  if (RegCreateKey(hk, argv[2].strptr, &hkResult ) == ERROR_SUCCESS)
523  {
524  RET_HANDLE(hkResult);
525  }
526  else
527  {
528  RETC(0);
529  }
530  }
531  else if ( strcmp(argv[0].strptr, "CLOSE") == 0 )
532  {
533  GET_HANDLE(argv[1].strptr, hk);
534  if (RegCloseKey(hk) == ERROR_SUCCESS)
535  {
536  RETC(0);
537  }
538  else
539  {
540  RETC(1);
541  }
542  }
543  else if ( strcmp(argv[0].strptr, "QUERY") == 0 )
544  {
545  char Class[256];
546  DWORD retcode, cbClass, cSubKeys, cbMaxSubKeyLen,
547  cbMaxClassLen, cValues, cbMaxValueNameLen, cbMaxValueLen, cbSecurityDescriptor;
548  FILETIME ftLastWriteTime;
549  SYSTEMTIME stTime;
550 
551  cbClass = 256;
552 
553  GET_HANDLE(argv[1].strptr, hk);
554 
555  if ((retcode=RegQueryInfoKey(hk, // handle of key to query
556  Class, // address of buffer for class string
557  &cbClass, // address of size of class string buffer
558  NULL, // reserved
559  &cSubKeys, // address of buffer for number of subkeys
560  &cbMaxSubKeyLen, // address of buffer for longest subkey name length
561  &cbMaxClassLen, // address of buffer for longest class string length
562  &cValues, // address of buffer for number of value entries
563  &cbMaxValueNameLen, // address of buffer for longest value name length
564  &cbMaxValueLen, // address of buffer for longest value data length
565  &cbSecurityDescriptor, // address of buffer for security descriptor length
566  &ftLastWriteTime // address of buffer for last write time
567  )) == ERROR_SUCCESS)
568  {
569  if (FileTimeToSystemTime(&ftLastWriteTime, &stTime))
570  {
571 
572  sprintf(retstr->strptr,"%s, %ld, %ld, %04d/%02d/%02d, %02d:%02d:%02d",
573  Class, cSubKeys, cValues, stTime.wYear, stTime.wMonth, stTime.wDay,
574  stTime.wHour, stTime.wMinute, stTime.wSecond);
575  }
576  else
577  {
578  sprintf(retstr->strptr,"%s, %ld, %ld",Class, cSubKeys, cValues);
579  }
580 
581  retstr->strlength = strlen(retstr->strptr);
582  return 0;
583  }
584  else
585  {
586  RETC(0);
587  }
588  }
589  else if ( strcmp(argv[0].strptr, "LIST") == 0 )
590  {
591  DWORD retcode, ndx=0;
592  char Name[256];
593  char sname[64];
594  SHVBLOCK shvb;
595 
596  GET_HKEY(argv[1].strptr, hk);
597  do
598  {
599  retcode = RegEnumKey(hk, // handle of key to query
600  ndx++, // index of subkey to query
601  Name, // address of buffer for subkey name
602  sizeof(Name)); // size of subkey buffer
603  if (retcode == ERROR_SUCCESS)
604  {
605  strcpy(sname, argv[2].strptr);
606  // make sure there is a period on the stem name
607  if (sname[argv[2].strlength - 1] != '.')
608  {
609  strcat(sname, ".");
610  }
611  sprintf(sname + strlen(sname),"%d", ndx);
612  SET_VARIABLE(sname, Name, 2);
613  }
614  else if (retcode != ERROR_NO_MORE_ITEMS)
615  {
616  RETC(1);
617  }
618  } while (retcode == ERROR_SUCCESS);
619  RETC(0);
620  }
621  else if ( strcmp(argv[0].strptr, "FLUSH") == 0 )
622  {
623  GET_HKEY(argv[1].strptr, hk);
624 
625  if (RegFlushKey(hk) == ERROR_SUCCESS)
626  {
627  RETC(0);
628  }
629  else
630  {
631  RETC(1);
632  }
633  }
634  else
635  {
636  RETC(1);
637  }
638 }
639 
640 
641 size_t RexxEntry WSRegistryValue(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
642 {
643  HKEY hk;
644  LONG rc;
645 
646  CHECKARG(2,5);
647 
648  if ( strcmp(argv[0].strptr, "SET") == 0 )
649  {
650  DWORD valType;
651  DWORD dwNumber;
652  DWORD dataLen;
653  const BYTE * data;
654 
655  GET_HKEY(argv[1].strptr, hk);
656 
657  if (!strcmp(argv[4].strptr,"EXPAND")) valType = REG_EXPAND_SZ;
658  else
659  if (!strcmp(argv[4].strptr,"MULTI")) valType = REG_MULTI_SZ;
660  else
661  if (!strcmp(argv[4].strptr,"NUMBER")) valType = REG_DWORD;
662  else
663  if (!strcmp(argv[4].strptr,"BINARY")) valType = REG_BINARY;
664  else
665  if (!strcmp(argv[4].strptr,"LINK")) valType = REG_LINK;
666  else
667  if (!strcmp(argv[4].strptr,"RESOURCELIST")) valType = REG_RESOURCE_LIST;
668  else
669  if (!strcmp(argv[4].strptr,"RESOURCEDESC")) valType = REG_FULL_RESOURCE_DESCRIPTOR;
670  else
671  if (!strcmp(argv[4].strptr,"RESOURCEREQS")) valType = REG_RESOURCE_REQUIREMENTS_LIST;
672  else
673  if (!strcmp(argv[4].strptr,"BIGENDIAN")) valType = REG_DWORD_BIG_ENDIAN;
674  else
675  if (!strcmp(argv[4].strptr,"NONE")) valType = REG_NONE;
676  else
677  valType = REG_SZ;
678 
679  if ((valType == REG_DWORD) || (valType == REG_DWORD_BIG_ENDIAN))
680  {
681  dwNumber = atoi(argv[3].strptr);
682 
683  if (valType == REG_DWORD_BIG_ENDIAN)
684  {
685  Little2BigEndian((BYTE *) &dwNumber, sizeof(dwNumber));
686  }
687 
688  data = (const BYTE *) &dwNumber;
689  dataLen = sizeof(dwNumber);
690  }
691  else
692  {
693  data = (const BYTE *) argv[3].strptr;
694  switch (valType)
695  {
696  case REG_BINARY:
697  case REG_NONE:
698  case REG_LINK:
699  case REG_RESOURCE_LIST:
700  case REG_FULL_RESOURCE_DESCRIPTOR:
701  case REG_RESOURCE_REQUIREMENTS_LIST:
702  dataLen = (DWORD)argv[3].strlength;
703  break;
704 
705  case REG_EXPAND_SZ:
706  case REG_MULTI_SZ:
707  case REG_SZ:
708  dataLen = (DWORD)argv[3].strlength+1;
709  break;
710  }
711  }
712 
713  if (RegSetValueEx(hk, argv[2].strptr, 0, valType, data, dataLen) == ERROR_SUCCESS)
714  {
715  RETC(0);
716  }
717  else
718  {
719  RETC(1);
720  }
721 
722  }
723  else if ( strcmp(argv[0].strptr, "QUERY") == 0 )
724  {
725  DWORD valType, cbData;
726  char * valData, *vType;
727 
728  cbData = sizeof(valData);
729 
730  GET_HKEY(argv[1].strptr, hk);
731 
732  if (RegQueryValueEx(hk, // handle of key to query
733  argv[2].strptr, // address of name of value to query
734  NULL, // reserved
735  &valType, // address of buffer for value type
736  NULL, // NULL to get the size
737  &cbData) == ERROR_SUCCESS) // address of data buffer size
738  {
739  valData = (char *)GlobalAlloc(GPTR, cbData);
740 
741  if (!valData)
742  {
743  RETERR;
744  }
745 
746  if (RegQueryValueEx(hk, // handle of key to query
747  argv[2].strptr, // address of name of value to query
748  NULL, // reserved
749  &valType, // address of buffer for value type
750  (LPBYTE)valData, // address of data buffer
751  &cbData) == ERROR_SUCCESS) // address of data buffer size
752  {
753  // If the size of the value data is larger than the default
754  // return string buffer, we need to allocate a bigger buffer.
755  if ( cbData + sizeof("RESOURCEDESC, ") > STR_BUFFER )
756  {
757  retstr->strptr = (char *)RexxAllocateMemory(cbData + sizeof("RESOURCEDESC, "));
758  if ( retstr->strptr == NULL )
759  {
760  RETERR;
761  }
762  }
763 
764  switch (valType)
765  {
766  case REG_MULTI_SZ:
767  vType = "MULTI";
768  sprintf(retstr->strptr,"%s, ",vType);
769  memcpy(&retstr->strptr[strlen(retstr->strptr)], valData, cbData);
770  break;
771  case REG_DWORD:
772  vType = "NUMBER";
773  sprintf(retstr->strptr,"%s, %ld",vType, *(DWORD *)valData);
774  break;
775  case REG_BINARY:
776  vType = "BINARY";
777  sprintf(retstr->strptr,"%s, ",vType);
778  memcpy(&retstr->strptr[strlen(retstr->strptr)], valData, cbData);
779  break;
780  case REG_NONE:
781  vType = "NONE";
782  sprintf(retstr->strptr,"%s, ",vType);
783  memcpy(&retstr->strptr[strlen(retstr->strptr)], valData, cbData);
784  break;
785  case REG_SZ:
786  vType = "NORMAL";
787  sprintf(retstr->strptr,"%s, %s",vType, valData);
788  break;
789  case REG_EXPAND_SZ:
790  vType = "EXPAND";
791  sprintf(retstr->strptr,"%s, %s",vType, valData);
792  break;
793  case REG_RESOURCE_LIST:
794  vType = "RESOURCELIST";
795  sprintf(retstr->strptr,"%s, ",vType);
796  memcpy(&retstr->strptr[strlen(retstr->strptr)], valData, cbData);
797  break;
798  case REG_FULL_RESOURCE_DESCRIPTOR:
799  vType = "RESOURCEDESC";
800  sprintf(retstr->strptr,"%s, ",vType);
801  memcpy(&retstr->strptr[strlen(retstr->strptr)], valData, cbData);
802  break;
803  case REG_RESOURCE_REQUIREMENTS_LIST:
804  vType = "RESOURCEREQS";
805  sprintf(retstr->strptr,"%s, ",vType);
806  memcpy(&retstr->strptr[strlen(retstr->strptr)], valData, cbData);
807  break;
808  case REG_LINK:
809  vType = "LINK";
810  sprintf(retstr->strptr,"%s, ",vType);
811  memcpy(&retstr->strptr[strlen(retstr->strptr)], valData, cbData);
812  break;
813  case REG_DWORD_BIG_ENDIAN:
814  {
815  DWORD dwNumber;
816  vType = "BIGENDIAN";
817  dwNumber = * (DWORD *)valData;
818  Little2BigEndian((BYTE *) &dwNumber, sizeof(dwNumber));
819  sprintf(retstr->strptr,"%s, %ld",vType, dwNumber);
820  }
821  break;
822 
823  default:
824  vType = "OTHER";
825  sprintf(retstr->strptr,"%s,",vType);
826  }
827  if ((valType == REG_MULTI_SZ) ||
828  (valType == REG_BINARY) ||
829  (valType == REG_RESOURCE_LIST) ||
830  (valType == REG_FULL_RESOURCE_DESCRIPTOR) ||
831  (valType == REG_RESOURCE_REQUIREMENTS_LIST) ||
832  (valType == REG_NONE))
833  {
834  retstr->strlength = strlen(vType) + 2 + cbData;
835  }
836  else
837  {
838  retstr->strlength = strlen(retstr->strptr);
839  }
840 
841  GlobalFree(valData);
842  return 0;
843  }
844 
845  GlobalFree(valData);
846  RETC(0);
847  }
848  else
849  {
850  RETC(0);
851  }
852  }
853  else if ( strcmp(argv[0].strptr, "LIST") == 0 )
854  {
855  DWORD retcode, ndx=0, valType, cbValue, cbData, initData = 1024;
856  char * valData, *pTail, Name[256];
857  char sname[300];
858  SHVBLOCK shvb;
859 
860  GET_HKEY(argv[1].strptr, hk);
861  valData = (char *)GlobalAlloc(GPTR, initData);
862  if (!valData)
863  {
864  RETERR
865  }
866 
867  // Copy the stem name to our sname buffer, point to the last character.
868  // Ensure the last character is a period, then point to the start of the
869  // tail.
870  strcpy(sname, argv[2].strptr);
871  pTail = sname + argv[2].strlength - 1;
872  if ( *pTail != '.')
873  {
874  *++pTail = '.';
875  }
876  pTail++;
877 
878  do
879  {
880  cbData = initData;
881  cbValue = sizeof(Name);
882  retcode = RegEnumValue(hk, // handle of key to query
883  ndx++, // index of subkey to query
884  Name, // address of buffer for subkey name
885  &cbValue,
886  NULL, // reserved
887  &valType, // address of buffer for type code
888  (LPBYTE)valData, // address of buffer for value data
889  &cbData); // address for size of data buffer
890 
891  if ((retcode == ERROR_MORE_DATA) && (cbData > initData)) /* we need more memory */
892  {
893  GlobalFree(valData);
894  initData = cbData;
895  valData = (char *)GlobalAlloc(GPTR, cbData);
896  if (!valData) RETERR
897  ndx--; /* try to get the previous one again */
898  cbValue = sizeof(Name);
899  retcode = RegEnumValue(hk, // handle of key to query
900  ndx++, // index of subkey to query
901  Name, // address of buffer for subkey name
902  &cbValue,
903  NULL, // reserved
904  &valType, // address of buffer for type code
905  (LPBYTE)valData, // address of buffer for value data
906  &cbData); // address for size of data buffer
907  }
908 
909  if (retcode == ERROR_SUCCESS)
910  {
911  sprintf(pTail, "%d.Name", ndx);
912  SET_VARIABLE(sname, Name, 2);
913 
914  sprintf(pTail, "%d.Type", ndx);
915  switch (valType)
916  {
917  case REG_EXPAND_SZ:
918  SET_VARIABLE(sname, "EXPAND", 2);
919  break;
920  case REG_NONE:
921  SET_VARIABLE(sname, "NONE", 2);
922  break;
923  case REG_DWORD:
924  SET_VARIABLE(sname, "NUMBER", 2);
925  break;
926  case REG_MULTI_SZ:
927  SET_VARIABLE(sname, "MULTI", 2);
928  break;
929  case REG_BINARY:
930  SET_VARIABLE(sname, "BINARY", 2);
931  break;
932  case REG_SZ:
933  SET_VARIABLE(sname, "NORMAL", 2);
934  break;
935  case REG_RESOURCE_LIST:
936  SET_VARIABLE(sname, "RESOURCELIST", 2);
937  break;
938  case REG_FULL_RESOURCE_DESCRIPTOR:
939  SET_VARIABLE(sname, "RESOURCEDESC", 2);
940  break;
941  case REG_RESOURCE_REQUIREMENTS_LIST:
942  SET_VARIABLE(sname, "RESOURCEREQS", 2);
943  break;
944  case REG_LINK:
945  SET_VARIABLE(sname, "LINK", 2);
946  break;
947  case REG_DWORD_BIG_ENDIAN:
948  SET_VARIABLE(sname, "BIGENDIAN", 2);
949  break;
950  default:
951  SET_VARIABLE(sname, "OTHER", 2);
952  }
953 
954  sprintf(pTail, "%d.Data", ndx);
955  if ((valType == REG_MULTI_SZ) ||
956  (valType == REG_BINARY) ||
957  (valType == REG_LINK) ||
958  (valType == REG_RESOURCE_LIST) ||
959  (valType == REG_FULL_RESOURCE_DESCRIPTOR) ||
960  (valType == REG_RESOURCE_REQUIREMENTS_LIST) ||
961  (valType == REG_NONE))
962  {
963  shvb.shvnext = NULL;
964  shvb.shvname.strptr = sname;
965  shvb.shvname.strlength = strlen(sname);
966  shvb.shvnamelen = shvb.shvname.strlength;
967  shvb.shvvalue.strptr = valData;
968  shvb.shvvalue.strlength = cbData;
969  shvb.shvvaluelen = cbData;
970  shvb.shvcode = RXSHV_SYSET;
971  shvb.shvret = 0;
972  if (RexxVariablePool(&shvb) == RXSHV_BADN)
973  {
974  GlobalFree(valData);
975  RETC(2);
976  }
977  }
978  else if ((valType == REG_EXPAND_SZ) || (valType == REG_SZ))
979  {
980  SET_VARIABLE(sname, valData, 2);
981  }
982  else if ((valType == REG_DWORD) || (valType == REG_DWORD_BIG_ENDIAN))
983  {
984  char tmp[30];
985  DWORD dwNumber;
986 
987  dwNumber = *(DWORD *) valData;
988  if (valType == REG_DWORD_BIG_ENDIAN)
989  {
990  Little2BigEndian((BYTE *)&dwNumber, sizeof(dwNumber));
991  }
992  ltoa(dwNumber, tmp, 10);
993  SET_VARIABLE(sname, tmp, 2);
994  }
995  else
996  {
997  SET_VARIABLE(sname, "", 2);
998  }
999  }
1000  else if (retcode != ERROR_NO_MORE_ITEMS)
1001  {
1002  GlobalFree(valData);
1003  RETC(1);
1004  }
1005  } while (retcode == ERROR_SUCCESS);
1006  GlobalFree(valData);
1007  RETC(0);
1008  }
1009  else if ( strcmp(argv[0].strptr, "DELETE") == 0 )
1010  {
1011  GET_HKEY(argv[1].strptr, hk);
1012 
1013  if ((rc = RegDeleteValue(hk, argv[2].strptr)) == ERROR_SUCCESS)
1014  {
1015  RETC(0);
1016  }
1017  else
1018  {
1019  RETVAL(rc);
1020  }
1021  }
1022  else
1023  {
1024  RETC(1);
1025  }
1026 }
1027 
1028 
1029 size_t RexxEntry WSRegistryFile(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
1030 {
1031  DWORD retc, rc;
1032  HKEY hk;
1033  HANDLE hToken; /* handle to process token */
1034  TOKEN_PRIVILEGES tkp; /* ptr. to token structure */
1035 
1036  CHECKARG(2,5);
1037 
1038  if ( strcmp(argv[0].strptr, "CONNECT") == 0 )
1039  {
1040  HKEY hkResult;
1041  GET_HKEY(argv[1].strptr, hk);
1042 
1043  if (RegConnectRegistry(argv[2].strptr, hk, &hkResult ) == ERROR_SUCCESS)
1044  {
1045  RET_HANDLE(hkResult);
1046  }
1047  else
1048  {
1049  RETC(0);
1050  }
1051  }
1052  else if ( strcmp(argv[0].strptr, "SAVE") == 0 )
1053  {
1054  /* set SE_BACKUP_NAME privilege. */
1055 
1056  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
1057  {
1058  RETVAL(GetLastError());
1059  }
1060 
1061  LookupPrivilegeValue(NULL, SE_BACKUP_NAME,&tkp.Privileges[0].Luid);
1062 
1063  tkp.PrivilegeCount = 1; /* one privilege to set */
1064  tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1065 
1066  /* Set SE_BACKUP_NAME privilege for this process. */
1067 
1068  AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
1069  (PTOKEN_PRIVILEGES) NULL, 0);
1070 
1071  if ((rc = GetLastError()) != ERROR_SUCCESS)
1072  {
1073  RETVAL(rc);
1074  }
1075 
1076  GET_HKEY(argv[1].strptr, hk);
1077  if ((retc = RegSaveKey(hk, argv[2].strptr, NULL)) == ERROR_SUCCESS)
1078  {
1079  RETC(0);
1080  }
1081  else
1082  {
1083  RETVAL(retc);
1084  }
1085  }
1086  else if ( strcmp(argv[0].strptr, "LOAD") == 0 || strcmp(argv[0].strptr, "RESTORE") == 0 ||
1087  strcmp(argv[0].strptr, "REPLACE") == 0 || strcmp(argv[0].strptr, "UNLOAD") == 0 )
1088  {
1089  /* set SE_RESTORE_NAME privilege. */
1090 
1091  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
1092  {
1093  RETVAL(GetLastError())
1094  }
1095 
1096  LookupPrivilegeValue(NULL, SE_RESTORE_NAME,&tkp.Privileges[0].Luid);
1097 
1098  tkp.PrivilegeCount = 1; /* one privilege to set */
1099  tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1100 
1101  /* Set SE_BACKUP_NAME privilege for this process. */
1102 
1103  AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
1104 
1105  if ((rc = GetLastError()) != ERROR_SUCCESS)
1106  {
1107  RETVAL(rc);
1108  }
1109 
1110  if ( strcmp(argv[0].strptr, "UNLOAD") == 0 )
1111  {
1112  GET_HKEY(argv[1].strptr, hk);
1113  if ((retc = RegUnLoadKey(hk, argv[2].strptr)) == ERROR_SUCCESS)
1114  {
1115  RETC(0);
1116  }
1117  else
1118  {
1119  RETVAL(retc);
1120  }
1121  }
1122  else if ( strcmp(argv[0].strptr, "LOAD") == 0 )
1123  {
1124  GET_HKEY(argv[1].strptr, hk);
1125  if ((retc = RegLoadKey(hk, argv[2].strptr, argv[3].strptr)) == ERROR_SUCCESS)
1126  {
1127  RETC(0);
1128  }
1129  else
1130  {
1131  RETVAL(retc);
1132  }
1133  }
1134  else if ( strcmp(argv[0].strptr, "RESTORE") == 0 )
1135  {
1136  DWORD vola;
1137 
1138  GET_HKEY(argv[1].strptr, hk);
1139  if (!strcmp(argv[3].strptr, "VOLATILE")) vola = REG_WHOLE_HIVE_VOLATILE;
1140  else vola = 0;
1141 
1142  if ((retc = RegRestoreKey(hk, argv[2].strptr, vola)) == ERROR_SUCCESS)
1143  {
1144  RETC(0);
1145  }
1146  else
1147  {
1148  RETVAL(retc);
1149  }
1150  }
1151  else if ( strcmp(argv[0].strptr, "REPLACE") == 0 )
1152  {
1153  const char * p;
1154  GET_HKEY(argv[1].strptr, hk);
1155  if (!strcmp(argv[2].strptr, "%NULL%"))
1156  {
1157  p = NULL;
1158  }
1159  else
1160  {
1161  p = argv[2].strptr;
1162  }
1163 
1164  if ((retc = RegReplaceKey(hk, p, argv[3].strptr, argv[4].strptr)) == ERROR_SUCCESS)
1165  {
1166  RETC(0);
1167  }
1168  else
1169  {
1170  RETVAL(retc);
1171  }
1172  }
1173  RETC(1);
1174  }
1175  else
1176  {
1177  // MessageBox(0,"Illegal registry file command!","Error",MB_OK | MB_ICONHAND | MB_SYSTEMMODAL);
1178  RETC(1);
1179  }
1180 }
1181 
1182 
1183 HDDEDATA CALLBACK DDECallback(UINT wType,
1184  UINT wFmt,
1185  HCONV hConv,
1186  HSZ hsz1,
1187  HSZ hsz2,
1188  HDDEDATA hDDEData,
1189  DWORD dwData1,
1190  DWORD dwData2)
1191 {
1192  return NULL;
1193 }
1194 
1195 BOOL ProgmanCmd(LPSTR lpszCmd)
1196 {
1197  DWORD dwDDEInst = 0;
1198  UINT ui;
1199  HSZ hszProgman;
1200  HCONV hConv;
1201  HDDEDATA hExecData;
1202  HDDEDATA exRes;
1203 
1204 
1205 
1206  ui = DdeInitialize(&dwDDEInst,
1207  (PFNCALLBACK)DDECallback,
1208  CBF_FAIL_ALLSVRXACTIONS,
1209  0l);
1210 
1211  if (ui != DMLERR_NO_ERROR) return FALSE;
1212 
1213 
1214 
1215  hszProgman = DdeCreateStringHandle(dwDDEInst,
1216  "PROGMAN",
1217  CP_WINANSI);
1218 
1219  hConv = DdeConnect(dwDDEInst,
1220  hszProgman,
1221  hszProgman,
1222  NULL);
1223 
1224 
1225  DdeFreeStringHandle(dwDDEInst, hszProgman);
1226 
1227  if (!hConv) return FALSE;
1228 
1229 
1230  hExecData = DdeCreateDataHandle(dwDDEInst,
1231  (LPBYTE)lpszCmd,
1232  lstrlen(lpszCmd)+1,
1233  0,
1234  NULL,
1235  0,
1236  0);
1237 
1238 
1239  exRes = DdeClientTransaction((LPBYTE)hExecData,
1240  (DWORD)-1,
1241  hConv,
1242  NULL,
1243  0,
1244  XTYP_EXECUTE,
1245  2000, // ms timeout
1246  NULL);
1247 
1248 
1249  DdeDisconnect(hConv);
1250  DdeUninitialize(dwDDEInst);
1251 
1252  return exRes != 0;
1253 }
1254 
1255 
1256 
1257 BOOL AddPMGroup(const char *lpszGroup, const char *lpszPath)
1258 {
1259  char buf[1024];
1260 
1261  if (lpszPath && lstrlen(lpszPath))
1262  {
1263  wsprintf(buf,
1264  "[CreateGroup(%s,%s)]",
1265  lpszGroup,
1266  lpszPath);
1267  }
1268  else
1269  {
1270  wsprintf(buf,
1271  "[CreateGroup(%s)]",
1272  lpszGroup);
1273  }
1274 
1275  return ProgmanCmd(buf);
1276 }
1277 
1278 BOOL DeletePMGroup(const char *lpszGroup)
1279 {
1280  char buf[512];
1281 
1282  if (lpszGroup && lstrlen(lpszGroup))
1283  {
1284  wsprintf(buf,
1285  "[DeleteGroup(%s)]",
1286  lpszGroup);
1287  }
1288 
1289  return ProgmanCmd(buf);
1290 }
1291 
1292 BOOL ShowPMGroup(const char *lpszGroup, WORD wCmd)
1293 {
1294  char buf[512];
1295 
1296  if (lpszGroup && lstrlen(lpszGroup))
1297  {
1298  wsprintf(buf,
1299  "[ShowGroup(%s,%u)]",
1300  lpszGroup,
1301  wCmd);
1302  }
1303 
1304  return ProgmanCmd(buf);
1305 }
1306 
1307 
1308 BOOL AddPMItem(const char *lpszCmdLine,
1309  const char *lpszCaption,
1310  const char *lpszIconPath,
1311  WORD wIconIndex,
1312  const char *lpszDir,
1313  BOOL bLast,
1314  const char *lpszHotKey,
1315  const char *lpszModifier,
1316  BOOL bMin)
1317 {
1318  char buf[2048];
1319  int Pos;
1320 
1321  if (bLast) Pos = -1; else Pos = 0;
1322 
1323  if (lpszIconPath && lstrlen(lpszIconPath))
1324  {
1325  wsprintf(buf,
1326  "[AddItem(%s,%s,%s,%u,%d,%d,%s,%d,%d)]",
1327  lpszCmdLine,
1328  lpszCaption,
1329  lpszIconPath,
1330  wIconIndex,
1331  Pos,Pos,
1332  lpszDir,
1333  MAKEWORD( atoi(lpszHotKey), atoi(lpszModifier)),
1334  bMin);
1335  }
1336  else
1337  {
1338  wsprintf(buf,
1339  "[AddItem(%s,%s,"","",%d,%d,%s,%d,%d)]",
1340  lpszCmdLine,
1341  lpszCaption,
1342  Pos, Pos,
1343  lpszDir,
1344  MAKEWORD( atoi(lpszHotKey), atoi(lpszModifier)),
1345  bMin);
1346  }
1347 
1348  return ProgmanCmd(buf);
1349 }
1350 
1351 
1352 BOOL DeletePMItem(const char *lpszItem)
1353 {
1354  char buf[512];
1355 
1356  if (lpszItem && lstrlen(lpszItem))
1357  {
1358  wsprintf(buf,
1359  "[DeleteItem(%s)]",
1360  lpszItem);
1361  }
1362 
1363  return ProgmanCmd(buf);
1364 }
1365 
1366 
1367 BOOL LeavePM(BOOL bSaveGroups)
1368 {
1369  char buf[256];
1370 
1371  wsprintf(buf,
1372  "[ExitProgman(%u)]",
1373  bSaveGroups ? 1 : 0);
1374 
1375  return ProgmanCmd(buf);
1376 }
1377 
1378 
1379 //-----------------------------------------------------------------------------
1380 //
1381 // Function: BOOL GetCurrentUserDesktopLocation
1382 //
1383 // This function reads the CurrentUser Desktop Path from the registry.
1384 //
1385 // Syntax: call GetCurrentUserDesktopLocation( LPBYTE szDesktopDir, LPDWORD lpcbData )
1386 //
1387 // Params:
1388 //
1389 // szDesktopDir - Drive and Path of the Desktop to be returned
1390 // lpcbData _ Max Size of szDesktopDir on entry, Size of szDesktopDir on exit
1391 //
1392 // return : TRUE - No error
1393 // FALSE - Error
1394 //-----------------------------------------------------------------------------
1395 #define IDS_REGISTRY_KEY_CURRENT_SHELLFOLDER "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
1396 #define IDS_CURRENT_DESKTOP "Desktop"
1397 
1398 BOOL GetCurrentUserDesktopLocation ( LPBYTE szDesktopDir, LPDWORD lpcbData )
1399 {
1400 
1401 
1402  HKEY hKey; //handle of key
1403  long rc;
1404  DWORD Type ;
1405 
1406  szDesktopDir[0] ='\0'; //initialize return
1407 
1408  if ( (rc = RegOpenKeyEx(HKEY_CURRENT_USER,
1410  0,
1411  KEY_QUERY_VALUE,
1412  &hKey)) == ERROR_SUCCESS )
1413  {
1414  if ( (rc = RegQueryValueEx(hKey, // handle of key to query
1415  IDS_CURRENT_DESKTOP , // address of name of value to query
1416  NULL, // reserved
1417  &Type, // address of buffer for value type
1418  szDesktopDir , // address of returned data
1419  lpcbData)) == ERROR_SUCCESS ) // .. returned here
1420  {
1421  RegCloseKey ( hKey ) ;
1422  return TRUE ;
1423  }
1424  RegCloseKey ( hKey ) ;
1425  }
1426 
1427  // Error occured
1428  return FALSE ;
1429 
1430 }
1431 //-----------------------------------------------------------------------------
1432 //
1433 // Function: BOOL GetAllUserDesktopLocation
1434 //
1435 // This function reads All UsersDesktop Path from the registry.
1436 //
1437 // Syntax: call GetAllUserDesktopLocation( LPBYTE szDesktopDir, LPDWORD lpcbData )
1438 //
1439 // Params:
1440 //
1441 // szDesktopDir - Drive and Path of the Desktop to be returned
1442 // lpcbData _ Max Size of szDesktopDir on entry, Size of szDesktopDir on exit
1443 //
1444 // return : TRUE - No error
1445 // FALSE - Error
1446 //-----------------------------------------------------------------------------
1447 #define IDS_REGISTRY_KEY_ALL_NT_SHELLFOLDER "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
1448 #define IDS_ALL_NT_DESKTOP "Common Desktop"
1449 
1450 #define IDS_REGISTRY_KEY_ALL_9x_SHELLFOLDER ".DEFAULT\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
1451 #define IDS_ALL_9x_DESKTOP "Desktop"
1452 
1453 
1454 BOOL GetAllUserDesktopLocation ( LPBYTE szDesktopDir, LPDWORD lpcbData )
1455 {
1456 
1457 
1458  HKEY hKey; //handle of key
1459  long rc;
1460  DWORD lpType ;
1461  LPTSTR lpValueName ;
1462 
1463  szDesktopDir[0] ='\0'; //initialize return
1464 
1465  // Test, if 95/98/Millenium or NT/Win2000
1466  if ( IsRunningNT() )
1467  {
1468  rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1470  0,
1471  KEY_QUERY_VALUE,
1472  &hKey) ;
1473  lpValueName = IDS_ALL_NT_DESKTOP ;
1474  }
1475  else
1476  {
1477  rc = RegOpenKeyEx(HKEY_USERS,
1479  0,
1480  KEY_QUERY_VALUE,
1481  &hKey) ;
1482  lpValueName = IDS_ALL_9x_DESKTOP ;
1483  }
1484  if ( rc == ERROR_SUCCESS )
1485  {
1486  if ( (rc = RegQueryValueEx(hKey, // handle of key to query
1487  lpValueName , // address of name of value to query
1488  NULL, // reserved
1489  &lpType, // address of buffer for value type
1490  szDesktopDir , // address of returned data
1491  lpcbData)) == ERROR_SUCCESS ) // .. returned here
1492  {
1493  RegCloseKey ( hKey ) ;
1494  return TRUE ;
1495  }
1496  RegCloseKey ( hKey ) ;
1497  }
1498 
1499  // Error occured
1500  return FALSE ;
1501 
1502 }
1503 
1504 //-----------------------------------------------------------------------------
1505 //
1506 // Function: BOOL AddPMDesktopIcon
1507 //
1508 // This function creates a shortcut to a file on the desktop.
1509 //
1510 // Syntax: call AddPmDesktopIcon( lpszName, lpszProgram, lpszIcon, iIconIndex, lpszWorkDir,
1511 // lpszLocation, lpszArguments, iscKey, iscModifier, run )
1512 //
1513 // Params:
1514 //
1515 // lpszName - Name of the short cut, displayed on the desktop below the icon
1516 // lpszProgram - full name of the file of the shortcut referes to
1517 // lpszIcon - full name of the icon file
1518 // iIconIndex - index of the icon within the icon file
1519 // lpszWorkDir - working directory of the shortcut
1520 // lpszLocation - "PERSONAL" : icon is only on desktop for current user
1521 // - "COMMON" : icon is displayed on desktop of all users
1522 // - "" : it's a link and can be placed in any folder
1523 // lpszArguments - arguments passed to the shortcut
1524 // iScKey - shortcut key
1525 // iScModifier - modifier of shortcut key
1526 // run - run application in a : NORMAL
1527 // MINIMIZED
1528 // MAXIMIZED window
1529 //
1530 // return : TRUE - No error
1531 // FALSE - Error
1532 //-----------------------------------------------------------------------------
1533 
1534 BOOL AddPMDesktopIcon(const char *lpszName,
1535  const char *lpszProgram,
1536  const char *lpszIcon,
1537  int iIconIndex,
1538  const char *lpszWorkDir,
1539  const char *lpszLocation,
1540  const char *lpszArguments,
1541  int iScKey,
1542  int iScModifier,
1543  const char *lpszRun )
1544 {
1545  HRESULT hres;
1546  IShellLink* psl;
1547  BOOL bRc = TRUE;
1548  int iRun = SW_NORMAL;
1549 
1550  CoInitialize(NULL);
1551 
1552  // Get a pointer to the IShellLink interface.
1553  hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl);
1554 
1555  if (SUCCEEDED(hres))
1556  {
1557  IPersistFile* ppf;
1558 
1559  // Set the path to the shortcut target
1560  psl->SetPath(lpszProgram );
1561 
1562  // icon location. default of iIconIndex is 0, set in WINSYSTM.CLS
1563  psl->SetIconLocation(lpszIcon, iIconIndex);
1564 
1565  // command-line arguments
1566  psl->SetArguments(lpszArguments );
1567 
1568  //shortcut key, the conversion to hex is done in WINSYSTM.CLS
1569  // modificationflag:
1570  // The modifier flags can be a combination of the following values:
1571  // HOTKEYF_SHIFT = SHIFT key 0x01
1572  // HOTKEYF_CONTROL = CTRL key 0x02
1573  // HOTKEYF_ALT = ALT key 0x04
1574  // HOTKEYF_EXT = Extended key 0x08
1575  psl->SetHotkey(MAKEWORD( iScKey, iScModifier) );
1576 
1577  // working directory
1578  psl->SetWorkingDirectory(lpszWorkDir );
1579 
1580  // run in normal, maximized , minimized window, default is NORMAL, set in WINSYSTM.CLS
1581  if ( !stricmp(lpszRun,"MAXIMIZED") )
1582  {
1583  iRun = SW_SHOWMAXIMIZED;
1584  }
1585  else if ( !stricmp(lpszRun,"MINIMIZED") )
1586  {
1587  iRun = SW_SHOWMINNOACTIVE;
1588  }
1589 
1590  psl->SetShowCmd(iRun );
1591 
1592  // Query IShellLink for the IPersistFile interface for saving the
1593  // shortcut in persistent storage.
1594  hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
1595 
1596  if (SUCCEEDED(hres))
1597  {
1598  WCHAR wsz[MAX_PATH];
1599  CHAR szShortCutName[MAX_PATH];
1600  CHAR szDesktopDir[MAX_PATH];
1601  DWORD dwSize = MAX_PATH;
1602 
1603  // If strlen(lpszLocation is < 6, then lpszName contains a full qualified filename
1604  if (strlen(lpszLocation)>5)
1605  {
1606  // if icon should only be created on the desktop of current user
1607  // get current user
1608  if (!stricmp(lpszLocation,"PERSONAL"))
1609  {
1610  bRc = GetCurrentUserDesktopLocation ( (LPBYTE)szDesktopDir , &dwSize ) ;
1611  }
1612  else
1613  { // Location is COMMON
1614  bRc = GetAllUserDesktopLocation ( (LPBYTE)szDesktopDir , &dwSize ) ;
1615  }
1616 
1617  if ( bRc)
1618  {
1619  //.lnk must be added to identify the file as a shortcut
1620  sprintf( szShortCutName, "%s\\%s.lnk", szDesktopDir, lpszName);
1621  }
1622  }
1623  else /* empty specifier so it's a link */
1624  {
1625  sprintf( szShortCutName, "%s.lnk", lpszName); /* lpszName contains a full qualified filename */
1626  }
1627 
1628  // Continueonly, if bRC is TRUE
1629  if ( bRc )
1630  {
1631 
1632  // Ensure that the string is Unicode.
1633  MultiByteToWideChar(CP_ACP, 0, szShortCutName, -1, wsz, MAX_PATH);
1634 
1635  // Save the link by calling IPersistFile::Save.
1636  hres = ppf->Save(wsz, TRUE);
1637  if (!SUCCEEDED(hres))
1638  {
1639  bRc = FALSE;
1640  }
1641  ppf->Release();
1642  }
1643  else
1644  {
1645  bRc = FALSE;
1646  }
1647  }
1648  else
1649  {
1650  bRc = FALSE;
1651  }
1652 
1653  psl->Release();
1654 
1655  }
1656  else
1657  {
1658  bRc = FALSE;
1659  }
1660 
1661  return bRc;
1662 }
1663 
1664 //-----------------------------------------------------------------------------
1665 //
1666 // Function: INT DelPMDesktopIcon
1667 //
1668 // This function deletes a shortcut created with AddPMDektopIcon
1669 //
1670 // Syntax: call DelPMDesktopIcon( lpszName,lpszLocation )
1671 //
1672 // Params:
1673 //
1674 // lpszName - Name of the short cut to be deleted
1675 //
1676 // return : 0 - No error
1677 // others - Error codes from DeleteFile
1678 // Note: The returncode of GetCurrentXXXDesktopLocation is not checked because
1679 // this hould be handeled by DeleteFile. So if an error occured during this
1680 // function, file nit found is returned
1681 //-----------------------------------------------------------------------------
1682 INT DelPMDesktopIcon( const char *lpszName, const char *lpszLocation)
1683 {
1684  CHAR szDesktopDir[MAX_PATH];
1685  CHAR szShortCutName[MAX_PATH];
1686  DWORD dwSize = MAX_PATH;
1687 
1688  // get the location (directory) of the shortcut file in dependency of
1689  // the specified location
1690  if (!stricmp(lpszLocation,"PERSONAL"))
1691  {
1692  GetCurrentUserDesktopLocation ( (LPBYTE)szDesktopDir , &dwSize );
1693  }
1694  else
1695  { // Location is COMMON
1696  GetAllUserDesktopLocation ( (LPBYTE)szDesktopDir , &dwSize );
1697  }
1698 
1699  //.lnk must be added to identify the file as a shortcut
1700  sprintf( szShortCutName, "%s\\%s.lnk", szDesktopDir, lpszName);
1701 
1702  if (!DeleteFile(szShortCutName))
1703  {
1704  return GetLastError();
1705  }
1706  else
1707  {
1708  return 0;
1709  }
1710 }
1711 
1712 size_t RexxEntry WSProgManager(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
1713 {
1714  CHECKARG(2,11);
1715 
1716  if (strcmp( argv[0].strptr, "ADDGROUP") == 0 )
1717  {
1718  RETC(!AddPMGroup(argv[1].strptr, argv[2].strptr));
1719  }
1720  else if ( strcmp(argv[0].strptr, "DELGROUP") == 0 )
1721  {
1722  RETC(!DeletePMGroup(argv[1].strptr));
1723  }
1724  else if ( strcmp(argv[0].strptr, "SHOWGROUP") == 0 )
1725  {
1726  INT stype;
1727  if ( strcmp(argv[2].strptr, "MAX") == 0 )
1728  {
1729  stype = 3;
1730  }
1731  else if ( strcmp(argv[2].strptr, "MIN") == 0 )
1732  {
1733  stype = 2;
1734  }
1735  else
1736  {
1737  stype = 1;
1738  }
1739 
1740  RETC(!ShowPMGroup(argv[1].strptr, (WORD)stype));
1741  }
1742  else if ( strcmp(argv[0].strptr, "ADDITEM") == 0 )
1743  {
1744  if (argc > 7)
1745  {
1746 
1747  RETC(!AddPMItem(argv[1].strptr, argv[2].strptr, argv[3].strptr, (WORD)atoi(argv[4].strptr), argv[5].strptr,\
1748  (BOOL)atoi(argv[6].strptr), argv[7].strptr, argv[8].strptr, (BOOL)atoi(argv[9].strptr)));
1749  }
1750  else
1751  {
1752  RETC(!AddPMItem(argv[1].strptr, argv[2].strptr, argv[3].strptr, (WORD)atoi(argv[4].strptr), argv[5].strptr,\
1753  (BOOL)atoi(argv[6].strptr), "", "", 0));
1754  }
1755 
1756  }
1757  else if ( strcmp(argv[0].strptr, "DELITEM") == 0 )
1758  {
1759  RETC(!DeletePMItem(argv[1].strptr));
1760  }
1761  else if (strcmp(argv[0].strptr, "LEAVE") == 0 )
1762  {
1763  if ( strcmp(argv[1].strptr, "SAVE") == 0 )
1764  {
1765  RETC(!LeavePM(TRUE));
1766  }
1767  else
1768  {
1769  RETC(!LeavePM(FALSE));
1770  }
1771  }
1772  else if ( strcmp(argv[0].strptr, "ADDDESKTOPICON") == 0 )
1773  {
1774  RETC(!AddPMDesktopIcon( argv[1].strptr, argv[2].strptr, argv[3].strptr, atoi(argv[4].strptr),
1775  argv[5].strptr, argv[6].strptr, argv[7].strptr, atoi(argv[8].strptr),
1776  atoi(argv[9].strptr), argv[10].strptr ));
1777  }
1778  else if ( strcmp(argv[0].strptr, "DELDESKTOPICON") == 0 )
1779  {
1780  CHECKARG(3,3);
1781  RETVAL(DelPMDesktopIcon( argv[1].strptr, argv[2].strptr));
1782  }
1783  else
1784  {
1785  RETC(0);
1786  }
1787 }
1788 
1789 
1790 
1791 //-----------------------------------------------------------------------------
1792 //
1793 // Section for the WindowsEventLog class
1794 //
1795 //-----------------------------------------------------------------------------
1796 
1797 #define WSEL_DEFAULT_EVENTS_ARRAY_SIZE 512
1798 #define WSEL_DEFAULT_SOURCE "Application"
1799 #define HANDLE_ATTRIBUTE "CURRENTHANDLE"
1800 #define RECORDS_ATTRIBUTE "EVENTS"
1801 #define INITCODE_ATTRIBUTE "INITCODE"
1802 #define MIN_READ_BUFFER_ATTRIBUTE "MINIMUMREADBUFFER"
1803 #define MIN_READ_MIN_ATTRIBUTE "MINIMUMREADMIN"
1804 #define MIN_READ_MAX_ATTRIBUTE "MINIMUMREADMAX"
1805 
1806 // This is the OS defined maximum size for any single record.
1807 #define MAX_RECORD_SIZE 256 * 1024
1808 
1809 // The default max and min read buffer sizes.
1810 #define MAX_READ_KB_COUNT 256
1811 #define MAX_READ_BUFFER MAX_READ_KB_COUNT * 1024
1812 #define MIN_READ_KB_COUNT 16
1813 #define MIN_READ_BUFFER MIN_READ_KB_COUNT * 1024
1814 
1815 #define BAD_RECORD_ARRAY_MSG "The events attribute has been altered so it is no longer an array object"
1816 #define BAD_RECORD_START_MSG "Requested start exceeds number of records"
1817 #define TOO_SMALLBUFFER_MSG "An event log record is too large (%u) for the read buffer (%u.)"
1818 #define START_RECORD_OUT_OF_RANGE_MSG "The start record (%u) is out of range; (%u - %u)"
1819 #define END_RECORD_OUT_OF_RANGE_MSG "start and count produce an end record (%u) out of range; (%u - %u)"
1820 
1822 
1823 /**
1824  * Query the registery for the name of an event record's message file. The file
1825  * name of the message file is stored as a value of a subkey under an
1826  * application log. The subkey name is the source name of the event. The value
1827  * name corresponds to the type of message file, event, parameter, or category.
1828  * (Although the category message file is not currently used by the
1829  * WindowsEventLog object.)
1830  *
1831  * @param pValueName The name of the value whose data is being sought. The data
1832  * is the message file name.
1833  * @param logName The name of the event log.
1834  * @param source The source of the event in the specified event log.
1835  * @param fileBuffer [out] The message file path name is returned in this
1836  * buffer, which must be at least MAX_PATH in size.
1837  */
1838 void lookupMessageFile(char *pValueName, char *logName, char *source, char *fileBuffer)
1839 {
1840  char eventLogKey[] = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
1841 
1842  DWORD dataSize; // size of the message file name returned from RegQueryValueEx
1843  DWORD valueType = REG_EXPAND_SZ; // type for call to RegQueryValueEx
1844  char *valueData; // value returned from RegQueryValueEx
1845  char *pKey; // name of complete key
1846  HKEY hKey; // handle of key
1847 
1848  // If there is no value in the registry for the message file, or an error
1849  // happens, return the empty string
1850  fileBuffer[0] ='\0';
1851 
1852  // Build the complete key name
1853  pKey = (char *)LocalAlloc(LPTR, strlen(eventLogKey) + strlen(logName) + 1 + strlen(source) + 1);
1854  sprintf(pKey, "%s%s\\%s", eventLogKey, logName, source);
1855 
1856  if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, pKey, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS )
1857  {
1858  // Use null for the data value to query for the size of the data.
1859  if ( RegQueryValueEx(hKey, pValueName, NULL, &valueType, NULL, &dataSize) == ERROR_SUCCESS )
1860  {
1861  // No error getting the size of the message file name, allocate a
1862  // buffer and get the value
1863  valueData = (char *)LocalAlloc(LPTR, dataSize);
1864 
1865  if ( RegQueryValueEx(hKey, pValueName, NULL, &valueType, (LPBYTE)valueData, &dataSize) == ERROR_SUCCESS )
1866  {
1867  // Place the message file path name in the return buffer,
1868  // expanding any environment variables in the process.
1869  ExpandEnvironmentStrings(valueData, fileBuffer, MAX_PATH);
1870  }
1871  LocalFree(valueData);
1872  }
1873  }
1874  LocalFree(pKey);
1875 }
1876 
1877 /**
1878  * Search a list of messages files for a specified message ID and, if found,
1879  * format and return the message.
1880  *
1881  * @param files List of message files separated by a semi-colon.
1882  * @param msgID The message ID to search for.
1883  * @param ppInserts Possible array of insertion strings to use when formatting
1884  * the message.
1885  * @param lpMsgBuf On success, a returned buffer containing the formatted
1886  * message.
1887  *
1888  * @return True if the message was found and formatted, false if not found or
1889  * any other error.
1890  *
1891  * @note On success, lpMsgBuf will have been allocated by FormatMessage(). This
1892  * buffer must be freed by the caller with LocalFree().
1893  */
1894 BOOL findAndFormatDescription(char *files, DWORD msgID, char **ppInserts, LPVOID *lpMsgBuf )
1895 {
1896  HINSTANCE h = NULL;
1897  char *pBuffer = NULL;
1898  char *pTemp = NULL;
1899  char *pNext = NULL;
1900  DWORD count = 0;
1901 
1902  if ( *files != '\0' )
1903  {
1904  DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY;
1905  DWORD langID = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
1906 
1907  pBuffer = (char *)LocalAlloc(LPTR, strlen(files) + 1);
1908  if ( pBuffer != NULL )
1909  {
1910  strcpy(pBuffer, files);
1911  pTemp = pBuffer;
1912 
1913  while ( pTemp != NULL && count == 0 )
1914  {
1915  pNext = strchr(pTemp, ';');
1916  if ( pNext != NULL )
1917  {
1918  *pNext = '\0';
1919  pNext++;
1920  }
1921 
1922  h = LoadLibrary(pTemp);
1923  if ( h != NULL )
1924  {
1925  count = FormatMessage(flags, h, msgID, langID, (LPTSTR)lpMsgBuf, 0, ppInserts);
1926  FreeLibrary(h);
1927  h = NULL;
1928  }
1929  pTemp = pNext;
1930  }
1931  LocalFree(pBuffer);
1932  }
1933  }
1934  return(count != 0);
1935 }
1936 
1937 /**
1938  * Builds up the descriptive message that goes with an event record.
1939  *
1940  * @param pEvLogRecord Event record of interest.
1941  * @param pchSource Source of event with an event log.
1942  * @param ppMessage Formatted description is returned here.
1943  */
1944 void getEventDescription(PEVENTLOGRECORD pEvLogRecord , const char *pchSource, char **ppMessage)
1945 {
1946  char *pchString = NULL; // pointer to substitution string within event log record
1947  int iStringLen = 0; // accumulation of the string length
1948 
1949  HINSTANCE hInstance = NULL; // handle for message files
1950  LPVOID lpMsgBuf = NULL; // buffer to format the string
1951  char *pSubstitutions[100]; // array of substitution strings
1952  char chMessageFile[MAX_PATH]; // name of message file
1953  char *pchPercent; // pointer to "%%" within a substitution string
1954 
1955  // Initialize the array of substitution strings.
1956  memset(pSubstitutions, 0, sizeof(pSubstitutions));
1957 
1958  // Point to first substitution string in the event log record.
1959  pchString = (char*)pEvLogRecord + pEvLogRecord->StringOffset;
1960 
1961  // It is possible that the substitution strings themselves contain
1962  // substitutions in the form of %%nn. The replacement for these comes from
1963  // the 'ParameterMessageFile'
1964  //
1965  // Loop over all the substitution strings, replacing each "%%" with a value
1966  // from the ParameterMessageFile. This builds an array of formatted
1967  // substitution strings.
1968  for ( int i = 0; i < pEvLogRecord->NumStrings; i++ )
1969  {
1970  // If a substitution string contains "%%", the name of the parameter
1971  // message file is read from the registry log. Each "%%" is followed by
1972  // an id, which is the id of the parameter string to be loaded from the
1973  // parameter message file. The %% and id are then replaced by the
1974  // parameter string in the substitution string by FormatMessage().
1975  if ( (pchPercent = strstr(pchString, "%%")) )
1976  {
1977  // This substitution string contains a placeholder for parameters.
1978  // Get the name of parameter message file from the registry.
1979  lookupMessageFile("ParameterMessageFile", const_cast<char *>(pchSource),
1980  (char *)pEvLogRecord + sizeof(EVENTLOGRECORD), chMessageFile);
1981 
1982  DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS;
1983  DWORD langID = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);
1984  int id = atoi(pchPercent + 2); // ID of message to be loaded
1985 
1986  // Load the parameter message file and format the substitution
1987  // string.
1988  if ( (hInstance = LoadLibrary(chMessageFile)) != NULL )
1989  {
1990  if ( FormatMessage(flags, hInstance, id, langID, (LPTSTR)&pSubstitutions[i], 0, 0) == 0 )
1991  {
1992  // An error occurred formatting the string, use the empty
1993  // string.
1994  pSubstitutions[i] = (char *)LocalAlloc(LPTR, 1);
1995  }
1996  FreeLibrary(hInstance);
1997  hInstance = NULL;
1998  }
1999  else
2000  {
2001  // The parameter message file could not be loaded, use the empty
2002  // string.
2003  pSubstitutions[i] = (char *)LocalAlloc(LPTR, 1);
2004  }
2005 
2006  // Accumulate the total string length.
2007  iStringLen += (int)strlen(pSubstitutions[i]) + 1;
2008  }
2009  else
2010  {
2011  // The substitution string does not have any replaceable parameters,
2012  // use them as is.
2013  iStringLen += (int)strlen(pchString) + 1;
2014  pSubstitutions[i] = (char *)LocalAlloc(LMEM_FIXED, strlen(pchString) + 1);
2015  strcpy(pSubstitutions[i], pchString);
2016  }
2017  // Point to next substitution string within the log record.
2018  pchString += strlen(pchString) + 1;
2019  }
2020 
2021  // Get the name of event log message file from the registry
2022  lookupMessageFile("EventMessageFile", const_cast<char *>(pchSource),
2023  (char *)pEvLogRecord + sizeof(EVENTLOGRECORD), chMessageFile);
2024 
2025  // Search for the message ID in the all event message files and format the
2026  // descriptive message if it is found.
2027  if ( findAndFormatDescription(chMessageFile, pEvLogRecord->EventID, pSubstitutions, &lpMsgBuf) )
2028  {
2029  // The message ID was found and the descriptive message was formatted.
2030  // So return that.
2031  *ppMessage = (char *)LocalAlloc(LPTR, strlen((char*)lpMsgBuf) + 1);
2032  strcpy(*ppMessage, (const char *)lpMsgBuf);
2033 
2034  // lpMsgBuf will have been allocated by FormatMessage(), it is no longer
2035  // needed so free it here.
2036  LocalFree(lpMsgBuf);
2037  }
2038  else
2039  {
2040  // The message ID was not found, or some other error. Use the
2041  // substitution strings for the descriptive message, if there are any.
2042  if ( pEvLogRecord->NumStrings )
2043  {
2044  *ppMessage = (char *)LocalAlloc(LPTR, iStringLen + 1);
2045 
2046  for ( int i = 0; i < pEvLogRecord->NumStrings; i++ )
2047  {
2048  strcat(*ppMessage, pSubstitutions[i]);
2049  strcat(*ppMessage, " ");
2050  }
2051  }
2052  else
2053  {
2054  // No substitution strings, just return the empty string for the
2055  // descriptive message.
2056  *ppMessage = (char *)LocalAlloc(LPTR, 1);
2057  }
2058  }
2059 
2060  // Free any substitution strings.
2061  for ( int i = 0; i < pEvLogRecord->NumStrings; i++ )
2062  {
2063  LocalFree(pSubstitutions[i]);
2064  }
2065 }
2066 
2067 /**
2068  * Translates the security ID value in the event log record to a user name, or
2069  * "N/A" if that is not possible.
2070  *
2071  * @param pEvLogRecord Pointer to event log record.
2072  *
2073  * @return Allocated string naming the user.
2074  *
2075  * @note The string is allocated using LocalAlloc and must be freed using
2076  * LocalFree.
2077  */
2078 char * getEventUserName(PEVENTLOGRECORD pEvLogRecord)
2079 {
2080  SID *psid;
2081  char *pUserID = NULL;
2082  DWORD sizeID = 0;
2083  char defUserID[] = "N/A";
2084 
2085  // Needed for LookupAccountSid(), but not used.
2086  SID_NAME_USE strDummy;
2087  char *pDomain = NULL;
2088  DWORD sizeDomain = 0;
2089 
2090  if ( pEvLogRecord->UserSidLength == 0 )
2091  {
2092  // No SID record avaialable return default user ID
2093  pUserID = (char *)LocalAlloc(LPTR, strlen(defUserID) + 1);
2094  strcpy(pUserID, defUserID);
2095  }
2096  else
2097  {
2098  // Get the SID record within the event log record
2099  psid = (SID *)((char*)pEvLogRecord + pEvLogRecord->UserSidOffset);
2100 
2101  // Get the size required for the return buffers
2102  LookupAccountSid(NULL, psid, pUserID, &sizeID, pDomain, &sizeDomain, &strDummy);
2103 
2104  pUserID = (char *)LocalAlloc(LPTR, std::max(sizeID, (DWORD)strlen(defUserID) + 1));
2105  pDomain = (char *)LocalAlloc(LPTR, sizeDomain);
2106 
2107  if ( LookupAccountSid(NULL, psid, pUserID, &sizeID, pDomain, &sizeDomain, &strDummy) == 0 )
2108  {
2109  // Some type of error, just use the default.
2110  strcpy(pUserID, defUserID);
2111  }
2112 
2113  // Historically, the domain name has not been returned to the ooRexx
2114  // user. Seems as though some one might want it.
2115  LocalFree(pDomain);
2116  }
2117  return pUserID;
2118 }
2119 
2120 /**
2121  * Allocates a buffer and fills it with the event log record's binary data field
2122  * converted to a character representation. I.e., 4115 would be converted to a
2123  * series of chars: 01000103 (0x1013 == 4115) and returned in the buffer.
2124  *
2125  * @param pEvLogRecord [in] Pointer to an event log record.
2126  * @param ppBinData [out] Allocated buffer address is returned here.
2127  *
2128  * @return Number of characters used to represent the binary data bytes, with 0
2129  * having the special meaning that there was no binary data in the
2130  * event record. When 0 is returned, the actual size of the buffer is
2131  * 1.
2132  *
2133  * @note The buffer is allocated using LocalAlloc and must be freed using
2134  * LocalFree.
2135  */
2136 size_t getEventBinaryData(PEVENTLOGRECORD pEvLogRecord, char **ppBinData )
2137 {
2138  char *pRecData;
2139  char pTemp[3];
2140  char *p;
2141 
2142  if ( pEvLogRecord->DataLength != 0 )
2143  {
2144  *ppBinData = (char *)LocalAlloc(LPTR, (pEvLogRecord->DataLength + 1) * 2);
2145  p = *ppBinData;
2146 
2147  pRecData = (char*)pEvLogRecord + pEvLogRecord->DataOffset;
2148 
2149  for ( DWORD i = 0; i < pEvLogRecord->DataLength; i++ )
2150  {
2151  _snprintf(pTemp, sizeof(pTemp), "%02x", *pRecData);
2152  memcpy(p, pTemp, 2);
2153  p += 2;
2154  pRecData++;
2155  }
2156  }
2157  else
2158  {
2159  *ppBinData = (char *)LocalAlloc(LPTR, 1);
2160  }
2161 
2162  return (pEvLogRecord->DataLength * 2);
2163 }
2164 
2166 {
2167  context->RaiseException1(Rexx_Error_System_service_user_defined, context->NewStringFromAsciiz(msg));
2168 }
2169 
2171 {
2172  systemServiceException(c, "Failed to allocate memory");
2173 }
2174 
2175 void wrongArgValueException(RexxMethodContext *c, int pos, const char *list, RexxObjectPtr actual)
2176 {
2177  c->RaiseException(Rexx_Error_Incorrect_method_list,
2178  c->ArrayOfFour(c->String("positional"), c->WholeNumberToObject(pos), c->String(list), actual));
2179 }
2180 
2181 void wrongArgValueException(RexxMethodContext *c, int pos, const char *list, const char *actual)
2182 {
2183  wrongArgValueException(c, pos, list, c->NewStringFromAsciiz(actual));
2184 }
2185 
2186 inline unsigned int wrongClassException(RexxMethodContext *c, int pos, const char *n)
2187 {
2188  c->RaiseException3(Rexx_Error_Incorrect_method_noclass, c->String("positional"), c->WholeNumberToObject(pos), c->NewStringFromAsciiz(n));
2189  return 0;
2190 }
2191 
2192 
2193 inline void setCurrentHandle(RexxMethodContext *context, HANDLE h)
2194 {
2195  context->SetObjectVariable(HANDLE_ATTRIBUTE, context->NewPointer(h));
2196 }
2197 
2198 /**
2199  * Gets the handle value at the CURRENTHANDLE attribute. If the handle is not
2200  * null, it is an open handle to an event log.
2201  *
2202  * @param context The method context we are operating under.
2203  *
2204  * @return An event handle. It is not unexpected that this handle is NULL.
2205  */
2207 {
2208  HANDLE h = NULL;
2209 
2210  RexxObjectPtr ptr = context->GetObjectVariable(HANDLE_ATTRIBUTE);
2211  if ( ptr != NULLOBJECT )
2212  {
2213  h = (HANDLE)context->PointerValue((RexxPointerObject)ptr);
2214  }
2215  return h;
2216 }
2217 
2218 /**
2219  * Gets the minimum read buffer size for reading event records from the object
2220  * attribute. This minimum can be changed by the user. If some error happens,
2221  * then the original default value is returned.
2222  *
2223  * @param c The method context we are operating under.
2224  *
2225  * @return The current minimum buffer size.
2226  */
2228 {
2229  uint32_t val = 0;
2230 
2231  RexxObjectPtr ptr = c->GetObjectVariable(MIN_READ_BUFFER_ATTRIBUTE);
2232  if ( ptr != NULLOBJECT )
2233  {
2234  if ( ! c->ObjectToUnsignedInt32(ptr, &val) )
2235  {
2236  val = MIN_READ_BUFFER;
2237  }
2238  }
2239  return (DWORD)val;
2240 }
2241 
2242 /**
2243  * Gets the records array (the array at the EVENTS attribute.) The array is
2244  * emptied before it is returnd.
2245  *
2246  * @param context The method context we are operating under.
2247  *
2248  * @return The empty records array on success, a null object on error.
2249  *
2250  * @note If NULLOBJECT is returned an exception has been raised.
2251  */
2253 {
2254  RexxArrayObject records = NULLOBJECT;
2255 
2256  RexxObjectPtr ptr = context->GetObjectVariable(RECORDS_ATTRIBUTE);
2257  if ( ptr != NULLOBJECT )
2258  {
2259  if ( context->IsArray(ptr) )
2260  {
2261  records = (RexxArrayObject)ptr;
2262  }
2263  }
2264 
2265  if ( records == NULLOBJECT )
2266  {
2267  context->RaiseException1(Rexx_Error_Execution_user_defined,
2268  context->NewStringFromAsciiz(BAD_RECORD_ARRAY_MSG));
2269  }
2270  else
2271  {
2272  context->SendMessage0(records, "EMPTY");
2273  }
2274 
2275  return records;
2276 }
2277 
2278 /** getOpenEventLog()
2279  *
2280  * Gets the opened handle to the specified event log.
2281  *
2282  * For the WindowsEventLog class, a currently opened event log handle always
2283  * take precedence over anything specified by the user. If currentHandle is not
2284  * null, then server and source are always ignored.
2285  *
2286  * If there is not a currently opened handle, then the user specified server and
2287  * source are used. Both server and source have defaults, so the user does not
2288  * need to specify either.
2289  *
2290  * Note that in the OpenEventLog() call, null is used to indicate the default
2291  * server (the local machine.) If the user omitted the server arg, then sever
2292  * will be null, which gives us the default automatically. Also note that
2293  * historically the empty string has also been used to indicate the default, so
2294  * that is maintained here.
2295  *
2296  * @param context The method context pointer
2297  * @param server The server to open the event log on. If specified this must
2298  * be in UNC format, but we do not check for that, merely let
2299  * the function fail.
2300  * @param source The source event log to open, may be omitted.
2301  * @param pHandle The opened handle is returned here.
2302  * @param pRC On an OS error, the value of GetLastError() is returned here,
2303  * otherwise 0 is returned here.
2304  *
2305  * @return True indicates that the event log handle was opened by this
2306  * function and should be closed by the caller. False
2307  * indicates that the handle comes from the currentHandle
2308  * attribute and should not be closed by the caller.
2309  */
2310 bool getOpenEventLog(RexxMethodContext *context, const char *server, const char *source, HANDLE *pHandle, DWORD *pRC)
2311 {
2312  bool didOpen = false;
2313 
2314  *pRC = 0;
2315  HANDLE hEventLog = getCurrentHandle(context);
2316 
2317  if ( hEventLog == NULL )
2318  {
2319  if ( server != NULL && strlen(server) == 0 )
2320  {
2321  server = NULL;
2322  }
2323  if ( source == NULL || strlen(source) == 0 )
2324  {
2325  source = WSEL_DEFAULT_SOURCE;
2326  }
2327 
2328  hEventLog = OpenEventLog(server, source);
2329  if ( hEventLog == NULL )
2330  {
2331  *pRC = GetLastError();
2332  }
2333  else
2334  {
2335  didOpen = true;
2336  }
2337  }
2338 
2339  *pHandle = hEventLog;
2340  return didOpen;
2341 }
2342 
2343 /**
2344  * If the current handle attribute has an opened handle, then it is closed.
2345  *
2346  * @param context The method context we are operating under.
2347  *
2348  * @return 0 if there is no open handle, or on success. If there is an error
2349  * closing an open handle, then the system error code is returned.
2350  */
2352 {
2353  DWORD rc = 0;
2354  HANDLE hEventLog = getCurrentHandle(context);
2355 
2356  if ( hEventLog != NULL )
2357  {
2358  rc = (CloseEventLog(hEventLog) == 0) ? GetLastError() : 0;
2359  setCurrentHandle(context, NULL);
2360  }
2361  return rc;
2362 }
2363 
2364 /**
2365  * Gets either the total count of records in the event log, or the record number
2366  * of the oldest record, or the record number of the youngest record in the
2367  * event log.
2368  *
2369  * The first record written to the log is record number 1, so 1 is often the
2370  * oldest record. However, if the user elects to have the log records
2371  * over-written when the log is full, the oldest record will no longer be 1 as
2372  * soon as the log starts to overwrite exsiting records.
2373  *
2374  * @param context The method context we are operating under.
2375  * @param numberType The number operation, i.e. what to do.
2376  * @param server The server arg, see getOpenEventLog() for details.
2377  * @param source The source arg, see getOpenEventLog() for details.
2378  *
2379  * @return The number requested on success, or the negated system error code on
2380  * failure.
2381  */
2383 {
2384  int32_t retNum = 0;
2385  HANDLE hEventLog;
2386  DWORD number;
2387  BOOL success;
2388 
2389  // We use number to hold a returned error code, if there is one.
2390  bool didOpen = getOpenEventLog(context, server, source, &hEventLog, &number);
2391 
2392  if ( hEventLog == NULL )
2393  {
2394  retNum = -(int32_t)number;
2395  goto finished;
2396  }
2397 
2398  // Unfortunately, on Vista, GetOldestEventLogRecord() returns an error if
2399  // the log is empty. So, we work around that.
2400  DWORD count;
2401  success = GetNumberOfEventLogRecords(hEventLog, &count);
2402  if ( ! success )
2403  {
2404  retNum = -(int32_t)GetLastError();
2405  goto finished;
2406  }
2407 
2408  if ( count == 0 )
2409  {
2410  retNum = 0;
2411  goto finished;
2412  }
2413 
2414  DWORD oldest;
2415  success = GetOldestEventLogRecord(hEventLog, &oldest);
2416  if ( ! success )
2417  {
2418  retNum = -(int32_t)GetLastError();
2419  goto finished;
2420  }
2421 
2422  switch ( numberType )
2423  {
2424  case record_count :
2425  retNum = count;
2426  break;
2427 
2428  case oldest_record :
2429  retNum = oldest;
2430  break;
2431 
2432  case youngest_record :
2433  retNum = oldest + count - 1;
2434  break;
2435 
2436  default :
2437  // This really should be impossible.
2438  break;
2439  }
2440 
2441 finished:
2442 
2443  if ( didOpen )
2444  {
2445  CloseEventLog(hEventLog);
2446  }
2447  return retNum;
2448 }
2449 
2450 /**
2451  * Convenience function to do most of the arg checking for the
2452  * WindowsEventLog::readRecords() method.
2453  *
2454  * @param context The method context we're operating under.
2455  * @param hLog The opened handle to the event log we are dealing with.
2456  * @param direction The direction arg (arg 1) passed to the method.
2457  * @param start The start arg (arg 4) passed to the method.
2458  * @param count Pointer to the count arg (arg 5) passed to the method. The
2459  * true count of records to be read is returned here.
2460  * @param flags Pointer to the flags to use in the ReadEventLog() API. The
2461  * actual flags to use are based on the args passed to the
2462  * method and returned here.
2463  * @param rc A returned error code, 0 or a system error code.
2464  *
2465  * @return True if things should continue, or false if they should not
2466  * continue.
2467  *
2468  * @note If false is returned, it is likely that an exception has
2469  * been raised. But, it may just be an OS system error. In
2470  * all cases the value of rc should be returned to the
2471  * interpreter. If an exception has not been raised, it is the
2472  * system error code that needs to be returned to the ooRexx
2473  * programmer.
2474  */
2475 bool checkReadRecordsArgs(RexxMethodContext *context, HANDLE hLog, CSTRING direction,
2476  uint32_t start, uint32_t *count, DWORD *flags, DWORD *rc)
2477 {
2478  *flags = EVENTLOG_FORWARDS_READ;
2479  if ( argumentExists(1) )
2480  {
2481  if ( stricmp("FORWARDS", direction) == 0 )
2482  {
2483  ; // Do nothing, flags are correct.
2484  }
2485  else if ( stricmp("BACKWARDS", direction) == 0 )
2486  {
2487  *flags = EVENTLOG_BACKWARDS_READ;
2488  }
2489  else
2490  {
2491  wrongArgValueException(context, 1, "FORWARDS, BACKWARDS", direction);
2492  return false;
2493  }
2494  }
2495 
2496  if ( argumentExists(4) || argumentExists(5) )
2497  {
2498  if ( argumentOmitted(4) )
2499  {
2500  context->RaiseException2(Rexx_Error_Incorrect_method_noarg, context->String("positional"), context->WholeNumberToObject(4));
2501  return false;
2502  }
2503  if ( argumentOmitted(5) )
2504  {
2505 
2506  context->RaiseException2(Rexx_Error_Incorrect_method_noarg, context->String("positional"), context->WholeNumberToObject(5));
2507  return false;
2508  }
2509  *flags |= EVENTLOG_SEEK_READ;
2510  }
2511  else
2512  {
2513  *flags |= EVENTLOG_SEQUENTIAL_READ;
2514  }
2515 
2516  DWORD totalRecords;
2517  if ( GetNumberOfEventLogRecords(hLog, &totalRecords) == 0 )
2518  {
2519  *rc = GetLastError();
2520  return false;
2521  }
2522 
2523  // We only need good start and count values if we are doing a seek read.
2524  // The start value is ignored on sequential reads. The oldest record has
2525  // the smallest record number, always.
2526  if ( *flags & EVENTLOG_SEEK_READ )
2527  {
2528  DWORD oldestRecord;
2529  if ( GetOldestEventLogRecord(hLog, &oldestRecord) == 0 )
2530  {
2531  *rc = GetLastError();
2532  return false;
2533  }
2534 
2535  DWORD youngestRecord = oldestRecord + totalRecords - 1;
2536  char tmp[256];
2537 
2538  if ( youngestRecord < start || start < oldestRecord )
2539  {
2540  _snprintf(tmp, sizeof(tmp), START_RECORD_OUT_OF_RANGE_MSG, start, oldestRecord, youngestRecord);
2541 
2542  context->RaiseException1(Rexx_Error_Incorrect_method_user_defined,
2543  context->NewStringFromAsciiz(tmp));
2544  return false;
2545  }
2546 
2547  DWORD endRecord;
2548  if ( *flags & EVENTLOG_FORWARDS_READ )
2549  {
2550  endRecord = start + *count - 1;
2551  }
2552  else
2553  {
2554  endRecord = start - *count + 1;
2555  }
2556 
2557  if ( youngestRecord < endRecord || endRecord < oldestRecord )
2558  {
2559  _snprintf(tmp, sizeof(tmp), END_RECORD_OUT_OF_RANGE_MSG, endRecord, oldestRecord, youngestRecord);
2560 
2561  context->RaiseException1(Rexx_Error_Incorrect_method_user_defined,
2562  context->NewStringFromAsciiz(tmp));
2563  return false;
2564  }
2565  }
2566  else
2567  {
2568  // For a sequential read, start is ignored. But having a valid count
2569  // value makes looping through the log easier.
2570  *count = totalRecords;
2571  }
2572  return true;
2573 }
2574 
2576 {
2577  return (type == EVENTLOG_SUCCESS ||
2578  type == EVENTLOG_AUDIT_FAILURE ||
2579  type == EVENTLOG_AUDIT_SUCCESS ||
2580  type == EVENTLOG_ERROR_TYPE ||
2581  type == EVENTLOG_INFORMATION_TYPE ||
2582  type == EVENTLOG_WARNING_TYPE);
2583 }
2584 
2585 /** WindowsEventLog::init()
2586  *
2587  * The init() method for the WindowsEventLog object. Sets the object attributes
2588  * to their default values.
2589  */
2590 RexxMethod0(int, WSEventLog_init)
2591 {
2592  RexxArrayObject obj = context->NewArray(WSEL_DEFAULT_EVENTS_ARRAY_SIZE);
2593  context->SetObjectVariable(RECORDS_ATTRIBUTE, obj);
2594 
2595  context->SetObjectVariable(MIN_READ_BUFFER_ATTRIBUTE, context->WholeNumberToObject(MIN_READ_BUFFER));
2596  context->SetObjectVariable(MIN_READ_MIN_ATTRIBUTE, context->WholeNumberToObject(MIN_READ_KB_COUNT));
2597  context->SetObjectVariable(MIN_READ_MAX_ATTRIBUTE, context->WholeNumberToObject(MAX_READ_KB_COUNT));
2598 
2599  context->SetObjectVariable(INITCODE_ATTRIBUTE, context->WholeNumberToObject(0));
2600 
2601  setCurrentHandle(context, NULL);
2602 
2603  return 0;
2604 }
2605 
2606 /** WindowsEventLog::open()
2607  *
2608  * Opens a handle to the specified event log.
2609  *
2610  * @param server [optional] The server to open the event log on. If specified
2611  * this should be in UNC format. The default is the local
2612  * machine. The empty string can also be used to specify the
2613  * local macnine.
2614  *
2615  * @param source [optional] The event log to open, the default is the
2616  * Application log. The empty string can also be used to
2617  * specify the local machine.
2618  *
2619  * @note The event logging service will open the Application log if the
2620  * specified log (the source arg) can not be found.
2621  */
2622 RexxMethod2(uint32_t, WSEventLog_open, OPTIONAL_CSTRING, server, OPTIONAL_CSTRING, source)
2623 {
2624  // Close an already opened handle if there is one.
2625  closeEventLog(context);
2626 
2627  // See the comment header for getOpenEventLog() for detail on the server and
2628  // source arguments.
2629  if ( server != NULL && strlen(server) == 0 )
2630  {
2631  server = NULL;
2632  }
2633  if ( source == NULL || strlen(source) == 0 )
2634  {
2635  source = WSEL_DEFAULT_SOURCE;
2636  }
2637 
2638  HANDLE hEventLog = OpenEventLog(server, source);
2639  if ( hEventLog == NULL )
2640  {
2641  return GetLastError();
2642  }
2643 
2644  setCurrentHandle(context, hEventLog);
2645  return 0;
2646 }
2647 
2648 /** WindowsEventLog::getNumber()
2649  *
2650  * Returns the number of event records in the specified log.
2651  *
2652  * @param server The server to open the event log on. If specified this
2653  * should be in UNC format. The default is the local machine.
2654  * The empty string can also be used to specify the local
2655  * macnine.
2656  *
2657  * @param source The event log to open, the default is the Application log.
2658  * The empty string can also be used to specify the local
2659  * machine.
2660  *
2661  * @return The count of records in the log.
2662  */
2663 RexxMethod2(int32_t, WSEventLog_getNumber, OPTIONAL_CSTRING, server, OPTIONAL_CSTRING, source)
2664 {
2665  return getEventLogNumber(context, record_count, server, source);
2666 }
2667 
2668 /** WindowsEventLog::getFirst()
2669  *
2670  * Returns the record number of the first recorded, or oldest, event. Event
2671  * log messages are numbered sequentially, with the oldest record having the
2672  * lowest record number.
2673  *
2674  * @param server The server to open the event log on. If specified this
2675  * should be in UNC format. The default is the local machine.
2676  * The empty string can also be used to specify the local
2677  * macnine.
2678  *
2679  * @param source The event log to open, the default is the Application log.
2680  * The empty string can also be used to specify the local
2681  * machine.
2682  *
2683  * @return The record number of the first recorded event.
2684  */
2685 RexxMethod2(int32_t, WSEventLog_getFirst, OPTIONAL_CSTRING, server, OPTIONAL_CSTRING, source)
2686 {
2687  return getEventLogNumber(context, oldest_record, server, source);
2688 }
2689 
2690 /** WindowsEventLog::getLast()
2691  *
2692  * Returns the record number of the last recorded, or youngest, event. Event
2693  * log messages are numbered sequentially, with the oldest record have the
2694  * lowest record number.
2695  *
2696  * @param server The server to open the event log on. If specified this
2697  * should be in UNC format. The default is the local machine.
2698  * The empty string can also be used to specify the local
2699  * macnine.
2700  *
2701  * @param source The event log to open, the default is the Application log.
2702  * The empty string can also be used to specify the local
2703  * machine.
2704  *
2705  * @return The record number of the last recorded event.
2706  */
2707 RexxMethod2(int32_t, WSEventLog_getLast, OPTIONAL_CSTRING, server, OPTIONAL_CSTRING, source)
2708 {
2709  return getEventLogNumber(context, youngest_record, server, source);
2710 }
2711 
2712 /** WindowsEventLog::isFull()
2713  *
2714  * Queries the event log to see if it is full.
2715  *
2716  * @return True if event log is full, othewise false. False is also returned
2717  * if any system error happens.
2718  */
2719 RexxMethod2(logical_t, WSEventLog_isFull, OPTIONAL_CSTRING, server, OPTIONAL_CSTRING, source)
2720 {
2721  HANDLE hLog;
2722  DWORD rc = 0;
2723  BOOL isFull = FALSE;
2724 
2725  // An error is just ignored and false returned.
2726  bool didOpen = getOpenEventLog(context, server, source, &hLog, &rc);
2727 
2728  if ( hLog != NULL )
2729  {
2730  EVENTLOG_FULL_INFORMATION efi;
2731  DWORD needed;
2732  if ( GetEventLogInformation(hLog, EVENTLOG_FULL_INFO, (void *)&efi, sizeof(efi), &needed) != 0 )
2733  {
2734  isFull = efi.dwFull ? TRUE : FALSE;
2735  }
2736  }
2737 
2738  if ( didOpen )
2739  {
2740  CloseEventLog(hLog);
2741  }
2742  return isFull;
2743 }
2744 
2745 /** WindowsEventLog::clear()
2746  *
2747  * Clears, empties, the specified event log. Optionally backs up the log before
2748  * clearing it.
2749  *
2750  * @param server The server to open the event log on. If specified this
2751  * should be in UNC format. The default is the local machine.
2752  * The empty string can also be used to specify the local
2753  * macnine.
2754  *
2755  * @param source The event log to open, the default is the Application log.
2756  * The empty string can also be used to specify the local
2757  * machine.
2758  *
2759  * @param backupFile If specified, the log will be backed up to this file.
2760  * There is no default value. If backupFile is omitted, the
2761  * log is not backed up. If a file name is supplied with no
2762  * extension, the the default event log extension of ".evt"
2763  * is added.
2764  *
2765  * @return 0 on success, the system error code otherwise.
2766  *
2767  * @note Note for the ooRexx docs: The backup file can not exist on a remote
2768  * machine. In particular, you can not use a backup file that is on a
2769  * network mapped drive. The clear operation will fail with rc 3 'The
2770  * system cannot find the path specified.'
2771  */
2772 RexxMethod3(uint32_t, WSEventLog_clear, OPTIONAL_CSTRING, server, OPTIONAL_CSTRING, source,
2773  OPTIONAL_CSTRING, backupFile)
2774 {
2775  HANDLE hEventLog;
2776  DWORD rc = 0;
2777 
2778  bool didOpen = getOpenEventLog(context, server, source, &hEventLog, &rc);
2779 
2780  if ( hEventLog == NULL )
2781  {
2782  return rc;
2783  }
2784 
2785  if ( backupFile != NULL && strlen(backupFile) == 0 )
2786  {
2787  backupFile = NULL;
2788  }
2789 
2790  // If the user supplied a backup file name, and it does not have an
2791  // extension, then add the default extension for event log files. Note that
2792  // PathAddExtension() only adds the extension if the file does not already
2793  // have any extension.
2794 
2795  char *pDupeName = NULL;
2796  if ( backupFile != NULL )
2797  {
2798  pDupeName = (char *)LocalAlloc(LPTR, MAX_PATH);
2799  if ( ! pDupeName )
2800  {
2801  outOfMemoryException(context);
2802  return 0;
2803  }
2804  strcpy(pDupeName, backupFile);
2805  PathAddExtension(pDupeName, ".evt");
2806  }
2807 
2808  if ( ClearEventLog(hEventLog, pDupeName) == 0 )
2809  {
2810  rc = GetLastError();
2811  }
2812 
2813  // If the arg to LocalFree() is null, LocalFree() does nothing.
2814  LocalFree(pDupeName);
2815 
2816  if ( didOpen )
2817  {
2818  CloseEventLog(hEventLog);
2819  }
2820 
2821  return rc;
2822 }
2823 
2824 /** WindowsEventLog::write()
2825  *
2826  * Write an event to an event log. In theory all args are optional, but that
2827  * would not be much of an event.
2828  *
2829  * @param server The server to open the event log on. If specified this
2830  * should be in UNC format. The default is the local machine.
2831  * The empty string can also be used to specify the local
2832  * macnine.
2833  *
2834  * @param source The event log to open, the default is the Application log.
2835  * The empty string can also be used to specify the local
2836  * machine.
2837  *
2838  * @param type The event type (EVENTLOG_SUCCESS EVENTLOG_AUDIT_FAILURE,
2839  * etc.,) the default is 1.
2840  * @param category The event category, default is 0.
2841  * @param id The event ID, default is 0.
2842  * @param data Raw binary data, default is none.
2843  * @param strings Args 7 to any number. Any number of strings. These are
2844  * the insertion strings used to contruct the event
2845  * descripiton.
2846  *
2847  * @return 0 on success, system error code on failure.
2848  */
2849 RexxMethod7(uint32_t, WSEventLog_write, OPTIONAL_CSTRING, server, OPTIONAL_CSTRING, source,
2850  OPTIONAL_uint16_t, t, OPTIONAL_uint16_t, category, OPTIONAL_uint32_t, id,
2851  OPTIONAL_RexxStringObject, rawData, ARGLIST, args)
2852 {
2853  DWORD rc = 0;
2854 
2855  WORD type = 1;
2856  void *data = NULL;
2857  DWORD dataSize = 0;
2858  WORD countStrings = 0;
2859  const char **strings = NULL;
2860 
2861  // See the comment header for getOpenEventLog() for detail on the server and
2862  // source arguments.
2863  if ( server != NULL && strlen(server) == 0 )
2864  {
2865  server = NULL;
2866  }
2867  if ( source == NULL || strlen(source) == 0 )
2868  {
2869  source = WSEL_DEFAULT_SOURCE;
2870  }
2871 
2872  if ( argumentExists(3) && isGoodEventType(t) )
2873  {
2874  type = t;
2875  }
2876 
2877  if ( argumentExists(6) )
2878  {
2879  dataSize = (DWORD)context->StringLength(rawData);
2880  data = malloc(dataSize);
2881  if ( data != NULL )
2882  {
2883  memcpy(data, (void *)context->StringData(rawData), dataSize);
2884  }
2885  else
2886  {
2887  dataSize = 0;
2888  }
2889  }
2890 
2891  size_t argc = context->ArraySize(args);
2892  if ( argc > 6 )
2893  {
2894  strings = (const char **)malloc((argc - 6) * sizeof(const char **));
2895  if ( strings != NULL )
2896  {
2897  const char **p = strings;
2898  for ( size_t i = 7; i <= argc; i++, countStrings++)
2899  {
2900  RexxObjectPtr obj = context->ArrayAt(args, i);
2901  if ( obj == NULLOBJECT )
2902  {
2903  break;
2904  }
2905  else
2906  {
2907  *p++ = context->ObjectToStringValue(obj);
2908  }
2909  }
2910  }
2911  }
2912 
2913  HANDLE hSource = RegisterEventSource(server, source);
2914  if ( hSource != NULL )
2915  {
2916  if ( ReportEvent(hSource, type, category, id, NULL, countStrings, dataSize, strings, data) == 0 )
2917  {
2918  rc = GetLastError();
2919  }
2920 
2921  DeregisterEventSource(hSource);
2922  }
2923  else
2924  {
2925  rc = GetLastError();
2926  }
2927 
2928  if ( data != NULL )
2929  {
2930  free(data);
2931  }
2932  if ( strings != NULL )
2933  {
2934  free(strings);
2935  }
2936 
2937  return rc;
2938 }
2939 
2940 /** WindowsEventLog::minimumRead=
2941  *
2942  * Sets the minimumReadBuffer attribute. This attribute controls the minimum
2943  * allocation size of the buffer used to read in event log records. This value
2944  * is specified in multiples of 1 KB and must be wtihin the range of
2945  * MIN_READ_KB_COUNT and MAX_READ_KB_COUNT.
2946  *
2947  * @param countKB The number of kilobytes for the minimum buffer allocation.
2948  */
2949 RexxMethod1(int, WSEventLog_minimumReadSet, uint32_t, countKB)
2950 {
2951  if ( countKB < MIN_READ_KB_COUNT || MAX_READ_KB_COUNT < countKB )
2952  {
2953  context->RaiseException(
2955  context->ArrayOfFive(context->String("positional"),
2956  context->Int32ToObject(1),
2957  context->Int32ToObject(MIN_READ_KB_COUNT),
2958  context->Int32ToObject(MAX_READ_KB_COUNT),
2959  context->Int32ToObject(countKB)));
2960  }
2961  else
2962  {
2963  context->SetObjectVariable(MIN_READ_BUFFER_ATTRIBUTE, context->WholeNumberToObject(countKB * 1024));
2964  }
2965  return 0;
2966 }
2967 
2968 /** WindowsEventLog::minimumRead
2969  *
2970  * Returns the current setting for the minimum read buffer. The value returned
2971  * is the size of the buffer in KiloBytes.
2972  *
2973  * @return The minimum size allocation size of the event log read buffer
2974  * buffer, in KiloBytes. E.g., if the minimum is set at a 4096 byte
2975  * buffer, then the return will be 4.
2976  */
2977 RexxMethod0(uint32_t, WSEventLog_minimumReadGet)
2978 {
2979  return getMinimumReadBufferSize(context) / 1024;
2980 }
2981 
2982 /** WindowsEventLog::readRecords()
2983  *
2984  * Reads records from an event log. If no args are given then all records from
2985  * the default system (the local machine) and the default source (the
2986  * Application log) are read.
2987  *
2988  * Each record is read from the event log, formatted into a string
2989  * representation and placed in an ooRexx array object. The array object is the
2990  * 'events' attribute of the WindowsEventLog. Before the read is started, the
2991  * array is emptied. After each read, the array will contain the records for
2992  * the previous read.
2993  *
2994  * @param direction FORWARDS or BACKWARDS default is forwards. The oldest
2995  * record is considered record 1.
2996  * @param server The server to open the event log on. If specified this
2997  * should be in UNC format. The default is the local machine.
2998  * The empty string can also be used to specify the local
2999  * macnine.
3000  * @param source The event log to open, the default is the Application log.
3001  * The empty string can also be used to specify the local
3002  * machine.
3003  * @param start First record to read. The default is all records. If a
3004  * start record is specified then count is mandatory.
3005  * @param count Count of records to read. Only used if start is specified,
3006  * in which case count is not optional.
3007  *
3008  * @return 0 on success, the system error code on failure.
3009  */
3010 RexxMethod5(uint32_t, WSEventLog_readRecords, OPTIONAL_CSTRING, direction, OPTIONAL_CSTRING, server,
3011  OPTIONAL_CSTRING, source, OPTIONAL_uint32_t, start, OPTIONAL_uint32_t, count)
3012 {
3013  DWORD rc = 0;
3014  HANDLE hLog;
3015 
3016  bool didOpen = getOpenEventLog(context, server, source, &hLog, &rc);
3017 
3018  if ( hLog == NULL )
3019  {
3020  return rc;
3021  }
3022 
3023  // source is optional, but if it was omitted we need to set the variable to
3024  // the default. It is used later.
3025  if ( source == NULL )
3026  {
3027  source = WSEL_DEFAULT_SOURCE;
3028  }
3029 
3030  DWORD flags;
3031  if ( ! checkReadRecordsArgs(context, hLog, direction, start, &count, &flags, &rc) )
3032  {
3033  if ( didOpen )
3034  {
3035  CloseEventLog(hLog);
3036  }
3037  return rc;
3038  }
3039 
3040  // Array to convert the event type into its string representation.
3041  char evType[6][12]={"Error","Warning","Information","Success","Failure","Unknown"};
3042  int evTypeIndex;
3043 
3044  PEVENTLOGRECORD pEvLogRecord; // pointer to one event record
3045  PVOID pBuffer = NULL; // buffer for event records
3046  DWORD bufferPos = 0; // position within the eventlog buffer
3047  DWORD bytesRead; // count of bytes read into buffer by ReadEventlog
3048  DWORD needed; // returned from ReadEventlog if buffer to small
3049  char * pchEventSource = NULL; // source from event log record
3050  char * pchComputerName = NULL; // computer name of event
3051  char * pchMessage = NULL; // text of description string
3052  char * pBinaryData = NULL; // raw data of event log record
3053  char time[MAX_TIME_DATE]; // converted time of event
3054  char date[MAX_TIME_DATE]; // converted date of event
3055  struct tm * DateTime; // converted from elapsed seconds
3056  char * pchStrBuf = NULL; // temp buffer for event string
3057  char * pchUser = NULL; // user name for event
3058  DWORD countRecords = 0; // number of event log records processed
3059 
3060  // We'll try to allocate a buffer big enough to hold all the records, but
3061  // not bigger than our max. Same idea for a minimum, we'll make sure the
3062  // buffer is at least a minimum size. The minimum size can be adjusted by
3063  // the user, up to the maximum size of one record.
3064  DWORD bufSize = count * 1024;
3065  DWORD minSize = getMinimumReadBufferSize(context);
3066  bufSize = bufSize > MAX_READ_BUFFER ? MAX_READ_BUFFER : bufSize;
3067  bufSize = bufSize < minSize ? minSize : bufSize;
3068 
3069  pBuffer = LocalAlloc(LPTR, bufSize);
3070  if ( pBuffer == NULL )
3071  {
3072  outOfMemoryException(context);
3073  return 0;
3074  }
3075 
3076  // Get the ooRexx array object that will hold the event records.
3077  RexxArrayObject rxRecords = getRecordArray(context);
3078  if ( rxRecords == NULLOBJECT )
3079  {
3080  return 0;
3081  }
3082 
3083  BOOL success = TRUE;
3084 
3085  // Loop through the event log reading the number of records specified by the
3086  // ooRexx programmer.
3087  while ( (countRecords < count) && (success = ReadEventLog(hLog, flags, start, pBuffer, bufSize, &bytesRead, &needed)) )
3088  {
3089  pEvLogRecord = (PEVENTLOGRECORD)pBuffer;
3090  bufferPos = 0;
3091 
3092  while ( (bufferPos < bytesRead) && (countRecords < count) )
3093  {
3094  if (flags & EVENTLOG_FORWARDS_READ)
3095  {
3096  start = pEvLogRecord->RecordNumber + 1;
3097  }
3098  else
3099  {
3100  start = pEvLogRecord->RecordNumber - 1;
3101  }
3102 
3103  // Count each event record
3104  countRecords++;
3105 
3106  // Gather together all the pieces that will make up our final string
3107  // representation of this record.
3108 
3109  // Get index to event type string
3110  GET_TYPE_INDEX(pEvLogRecord->EventType, evTypeIndex);
3111 
3112  // Get time and date converted to local time. The TimeWritten field
3113  // in the event log record struct is 32 bits but in VC++ 8.0, and
3114  // on, time_t is inlined to be 64-bits, and on 64-bit Windows time_t
3115  // is 64-bit to begin with. So, _localtime32() needs to be used.
3116  // However, VC++ 7.0 doesn't appear to have __time32_t.
3117 #if _MSC_VER < 1400
3118  DateTime = localtime((const time_t *)&pEvLogRecord->TimeWritten);
3119 #else
3120  DateTime = _localtime32((const __time32_t *)&pEvLogRecord->TimeWritten);
3121 #endif
3122  strftime(date, MAX_TIME_DATE,"%x", DateTime);
3123  strftime(time, MAX_TIME_DATE,"%X", DateTime);
3124 
3125  // Get the event source, computer name, and user name strings.
3126  pchEventSource = (char*)pEvLogRecord + sizeof(EVENTLOGRECORD);
3127  pchComputerName = pchEventSource + strlen(pchEventSource)+1;
3128  pchUser = getEventUserName(pEvLogRecord);
3129 
3130  // Build the description string, which involves a look up from the
3131  // message files and possibly some string substitution.
3132  getEventDescription(pEvLogRecord, source, &pchMessage);
3133 
3134  // Get any binary data associated with the record.
3135  size_t cch = getEventBinaryData(pEvLogRecord, &pBinaryData);
3136 
3137  // Calculate how big a buffer we need for the final string, and
3138  // allocate it.
3139  size_t len = strlen(evType[evTypeIndex]) + 1 +
3140  strlen(date) + 1 +
3141  strlen(time) + 1 +
3142  strlen(pchEventSource) + 3 +
3143  12 +
3144  strlen(pchUser) + 1 +
3145  strlen(pchComputerName) + 1 +
3146  strlen(pchMessage) + 3 +
3147  cch + 3;
3148 
3149  pchStrBuf = (char *)LocalAlloc(LPTR, len);
3150 
3151  // Put it all together. The binary data is surrounded by single
3152  // quotes so that the ooRexx user can parse it out.
3153  //
3154  // Type Date Time Source Id User Computer Details rawData
3155  sprintf(pchStrBuf,
3156  "%s %s %s '%s' %u %s %s '%s' '",
3157  evType[evTypeIndex],
3158  date,
3159  time,
3160  pchEventSource,
3161  LOWORD(pEvLogRecord->EventID),
3162  pchUser,
3163  pchComputerName,
3164  pchMessage);
3165 
3166  // Now we need to tack on the binary data, the last apostrophe,
3167  // and the trailing null.
3168  len = strlen(pchStrBuf);
3169  memcpy((pchStrBuf + len), pBinaryData, cch);
3170  len += cch;
3171 
3172  *(pchStrBuf + len++) = '\'';
3173  *(pchStrBuf + len) = '\0';
3174 
3175  LocalFree(pchMessage);
3176  pchMessage = NULL;
3177  LocalFree(pBinaryData);
3178  LocalFree(pchUser);
3179  pchMessage = NULL;
3180 
3181  // Add the record to the array.
3182  context->ArrayPut(rxRecords, context->NewString(pchStrBuf, len), countRecords);
3183 
3184  LocalFree(pchStrBuf);
3185 
3186  // Update postion and point to next event record
3187  bufferPos += pEvLogRecord->Length;
3188  pEvLogRecord = (PEVENTLOGRECORD)(((char*)pEvLogRecord) + pEvLogRecord->Length);
3189  }
3190  memset(pBuffer, 0, bufSize);
3191  }
3192 
3193  if ( ! success )
3194  {
3195  rc = GetLastError();
3196 
3197  if ( rc == ERROR_INSUFFICIENT_BUFFER )
3198  {
3199  // Seems unlikely the buffer could be too small, but if it is we're
3200  // not going to fool with trying to reallocate a bigger buffer.
3201  char tmp[256];
3202  _snprintf(tmp, sizeof(tmp), TOO_SMALLBUFFER_MSG, needed, bufSize);
3203  context->RaiseException1(Rexx_Error_Execution_user_defined, context->NewStringFromAsciiz(tmp));
3204  }
3205  else if ( rc == ERROR_HANDLE_EOF )
3206  {
3207  // If we read to the end of log, we'll count that as okay.
3208  rc = 0;
3209  }
3210  }
3211 
3212  // Clean up and return.
3213  LocalFree(pBuffer);
3214 
3215  if ( didOpen )
3216  {
3217  CloseEventLog(hLog);
3218  }
3219  return rc;
3220 }
3221 
3222 /** WindowsEventLog::getLogNames()
3223  *
3224  * Obtains a list of all the event log names on the current system.
3225  *
3226  * @param logNames [in/out]
3227  * An .array object in which the event log names are returned.
3228  *
3229  * @return An OS return code, 0 on success, the system error code on failure.
3230  *
3231  * @note The passed in array is emptied before the log names are added. On
3232  * error, the array will also be empty.
3233  */
3234 RexxMethod1(uint32_t, WSEventLog_getLogNames, RexxObjectPtr, obj)
3235 {
3236  DWORD rc = 0;
3237  const char key[] = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\";
3238  HKEY hKey;
3239 
3240  if ( ! context->IsOfType(obj, "ARRAY") )
3241  {
3242  return wrongClassException(context, 1, "Array");
3243  }
3244  RexxArrayObject logNames = (RexxArrayObject)obj;
3245 
3246  context->SendMessage0(logNames, "EMPTY");
3247 
3248  // Open the ..\Services\EventLog key and then enumerate each of its subkeys.
3249  // The name of each subkey is the name of an event log.
3250 
3251  rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_ENUMERATE_SUB_KEYS, &hKey);
3252  if ( rc == ERROR_SUCCESS )
3253  {
3254  DWORD index = 0;
3255  TCHAR name[MAX_REGISTRY_KEY_SIZE];
3256  DWORD cName = MAX_REGISTRY_KEY_SIZE;
3257 
3258  while ( (rc = RegEnumKeyEx(hKey, index, name, &cName, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS )
3259  {
3260  context->ArrayAppendString(logNames, name, strlen(name));
3261  index++;
3262  cName = MAX_REGISTRY_KEY_SIZE;
3263  }
3264 
3265  if ( rc == ERROR_NO_MORE_ITEMS )
3266  {
3267  // Not an error, this is the expected.
3268  rc = 0;
3269  }
3270  else
3271  {
3272  // An actual error, empty the array.
3273  context->SendMessage0(logNames, "EMPTY");
3274  }
3275 
3276  RegCloseKey(hKey);
3277  }
3278 
3279  return rc;
3280 }
3281 
3282 /** WindowsEventLog::close()
3283  *
3284  * If we have an open open event log handle, closes it.
3285  *
3286  * @return Returns 0 on success and if there is no open handle to begin with.
3287  * If there is an error attempting to close an open handle, the OS
3288  * system error code is returned.
3289  */
3290 RexxMethod0(uint32_t, WSEventLog_close)
3291 {
3292  return closeEventLog(context);
3293 }
3294 
3295 /** WindowsEventLog::uninit()
3296  *
3297  * Uninit method for the class. Simply makes sure, if we have an open handle,
3298  * it gets closed.
3299  */
3300 RexxMethod0(uint32_t, WSEventLog_uninit)
3301 {
3302  closeEventLog(context);
3303  return 0;
3304 }
3305 
3306 /* This method is used as a convenient way to test code. */
3307 RexxMethod4(int, WSEventLog_test, RexxStringObject, data, OPTIONAL_CSTRING, server, OPTIONAL_CSTRING, source, ARGLIST, args)
3308 {
3309  return 0;
3310 }
3311 
3312 
3313 
3314 size_t RexxEntry WSCtrlWindow(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
3315 {
3316  HWND hW;
3317  HWND thW;
3318  char *tmp_ptr;
3319  DWORD_PTR dwResult;
3320  LRESULT lResult;
3321 
3322  CHECKARG(1,10);
3323 
3324  if (!strcmp(argv[0].strptr,"DESK"))
3325  {
3326  RET_HANDLE(GetDesktopWindow());
3327  }
3328  else if (!strcmp(argv[0].strptr,"FIND"))
3329  {
3330  CHECKARG(2,2);
3331  hW = FindWindow(NULL, argv[1].strptr);
3332  RET_HANDLE(hW);
3333  }
3334  else if (!strcmp(argv[0].strptr,"FINDCHILD"))
3335  {
3336  CHECKARG(3,3);
3337  GET_HANDLE(argv[1].strptr, thW);
3338 
3339  hW = FindWindowEx(thW, NULL, NULL, argv[2].strptr);
3340  RET_HANDLE(hW);
3341  }
3342  else if (!strcmp(argv[0].strptr,"SETFG"))
3343  {
3344  CHECKARG(2,2);
3345  hW = GetForegroundWindow();
3346  GET_HANDLE(argv[1].strptr, thW);
3347 
3348  if (hW != thW)
3349  {
3350  SetForegroundWindow(thW);
3351  }
3352  RET_HANDLE(hW);
3353  }
3354  else if (!strcmp(argv[0].strptr,"GETFG"))
3355  {
3356  RET_HANDLE(GetForegroundWindow())
3357  }
3358  else if (!strcmp(argv[0].strptr,"TITLE"))
3359  {
3360  CHECKARG(2,3);
3361  GET_HANDLE(argv[1].strptr, hW);
3362  if (hW != NULL)
3363  {
3364  if (argc ==3)
3365  {
3366  RETC(!SetWindowText(hW, argv[2].strptr));
3367  }
3368  else
3369  {
3370  // GetWindowText do not work for controls from other processes and for listboxes
3371  // Now using WM_GETTEXT which works for all controls.
3372  // The return string was limitted to 255 characters, now it's unlimited.
3373  // retstr->strlength = GetWindowText(hW, retstr->strptr, 255);
3374 
3375  // at first get the text length to determine if the default lenght (255)of strptr would be exceeded.
3376  lResult = SendMessageTimeout(hW, WM_GETTEXTLENGTH ,0, 0, MSG_TIMEOUT_OPTS, MSG_TIMEOUT, &dwResult);
3377  // time out or error?
3378  if (lResult == 0)
3379  {
3380  // an error, but we return an empty string
3381  retstr->strlength = 0;
3382  return 0;
3383  }
3384  else
3385  {
3386  retstr->strlength = dwResult;
3387  }
3388  if ( retstr->strlength > (RXAUTOBUFLEN - 1) )
3389  {
3390  // text larger than 255, allocate a new one
3391  // the original REXX retstr->strptr must not be released! REXX keeps track of this
3392  tmp_ptr = (char *)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, retstr->strlength + 1);
3393  if (!tmp_ptr)
3394  {
3395  RETERR
3396  }
3397  retstr->strptr = tmp_ptr;
3398  }
3399  // now get the text
3400  lResult = SendMessageTimeout(hW, WM_GETTEXT, retstr->strlength + 1, (LPARAM)retstr->strptr,
3401  MSG_TIMEOUT_OPTS, MSG_TIMEOUT, &dwResult);
3402  // time out or error?
3403  if (lResult == 0)
3404  {
3405  // an error, but we return an empty string
3406  retstr->strlength = 0;
3407  return 0;
3408  }
3409  else
3410  {
3411  retstr->strlength = dwResult;
3412  }
3413 
3414  // if still no text found, the control may be a listbox, for which LB_GETTEXT must be used
3415  if ( retstr->strlength == 0 )
3416  {
3417  // at first get the selected item
3418  int curSel;
3419  lResult = SendMessageTimeout(hW, LB_GETCURSEL, 0, 0, MSG_TIMEOUT_OPTS, MSG_TIMEOUT, &dwResult);
3420  // time out or error?
3421  if (lResult == 0)
3422  {
3423  // an error, but we return an empty string
3424  retstr->strlength = 0;
3425  return 0;
3426  }
3427  else
3428  {
3429  curSel = (int) dwResult;
3430  }
3431  if (curSel != LB_ERR)
3432  {
3433  // get the text length and allocate a new strptr if needed
3434  lResult = SendMessageTimeout(hW, LB_GETTEXTLEN, curSel, 0, MSG_TIMEOUT_OPTS, MSG_TIMEOUT, &dwResult);
3435  // time out or error?
3436  if (lResult == 0)
3437  {
3438  // an error, but we return an empty string
3439  retstr->strlength = 0;
3440  return 0;
3441  }
3442  else
3443  {
3444  retstr->strlength = dwResult;
3445  }
3446 
3447  if ( retstr->strlength > (RXAUTOBUFLEN - 1) )
3448  {
3449  tmp_ptr = (char *)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT, retstr->strlength + 1);
3450  if (!tmp_ptr)
3451  {
3452  RETERR
3453  }
3454  retstr->strptr = tmp_ptr;
3455  }
3456  lResult = SendMessageTimeout(hW, LB_GETTEXT, curSel, (LPARAM)retstr->strptr, MSG_TIMEOUT_OPTS, MSG_TIMEOUT, &dwResult);
3457  // time out or error?
3458  if (lResult == 0)
3459  {
3460  // an error, but we return an empty string
3461  retstr->strlength = 0;
3462  return 0;
3463  }
3464  else
3465  {
3466  retstr->strlength = dwResult;
3467  }
3468  }
3469  }
3470  }
3471  return 0;
3472  }
3473  else
3474  {
3475  if (argc ==3)
3476  {
3477  RETC(!SetConsoleTitle(argv[2].strptr))
3478  }
3479  else
3480  {
3481  retstr->strlength = GetConsoleTitle(retstr->strptr, 255);
3482  }
3483  return 0;
3484  }
3485  }
3486  else if (!strcmp(argv[0].strptr,"CLASS"))
3487  {
3488  CHECKARG(2,2);
3489  GET_HANDLE(argv[1].strptr, hW);
3490  {
3491  retstr->strlength = GetClassName(hW, retstr->strptr, 255);
3492  return 0;
3493  }
3494  }
3495 
3496  else if (!strcmp(argv[0].strptr,"SHOW"))
3497  {
3498  CHECKARG(3,3);
3499  GET_HANDLE(argv[1].strptr, hW);
3500  if (hW)
3501  {
3502  INT sw;
3503  if (!strcmp(argv[2].strptr,"HIDE")) sw = SW_HIDE;
3504  else
3505  if (!strcmp(argv[2].strptr,"MAX")) sw = SW_SHOWMAXIMIZED;
3506  else
3507  if (!strcmp(argv[2].strptr,"MIN")) sw = SW_MINIMIZE;
3508  else
3509  if (!strcmp(argv[2].strptr,"RESTORE")) sw = SW_RESTORE;
3510  else sw = SW_SHOW;
3511  RETC(ShowWindow(hW, sw))
3512  }
3513  }
3514  else if (!strcmp(argv[0].strptr,"HNDL"))
3515  {
3516  CHECKARG(3,3);
3517  GET_HANDLE(argv[1].strptr, hW);
3518  if (hW)
3519  {
3520  INT gw;
3521 
3522  if (!strcmp(argv[2].strptr,"NEXT")) gw = GW_HWNDNEXT;
3523  else if (!strcmp(argv[2].strptr,"PREV")) gw = GW_HWNDPREV;
3524  else if (!strcmp(argv[2].strptr,"FIRSTCHILD")) gw = GW_CHILD;
3525  else if (!strcmp(argv[2].strptr,"FIRST")) gw = GW_HWNDFIRST;
3526  else if (!strcmp(argv[2].strptr,"LAST")) gw = GW_HWNDLAST;
3527  else if (!strcmp(argv[2].strptr,"OWNER")) gw = GW_OWNER;
3528  else RETC(1);
3529  RET_HANDLE(GetWindow(hW, gw));
3530  }
3531  else
3532  {
3533  RETC(0)
3534  }
3535  }
3536  else if (!strcmp(argv[0].strptr,"ID"))
3537  {
3538  CHECKARG(2,2);
3539 
3540  GET_HANDLE(argv[1].strptr, hW);
3541  RETVAL(GetDlgCtrlID(hW))
3542  }
3543  else if (!strcmp(argv[0].strptr,"ATPOS"))
3544  {
3545  POINT pt;
3546  CHECKARG(3,4);
3547  pt.x = atol(argv[1].strptr);
3548  pt.y = atol(argv[2].strptr);
3549 
3550  if (argc == 4)
3551  {
3552  GET_HANDLE(argv[3].strptr, hW);
3553  }
3554  else
3555  {
3556  hW = 0;
3557  }
3558  if (hW)
3559  {
3560  RET_HANDLE(ChildWindowFromPointEx(hW, pt, CWP_ALL));
3561  }
3562  else
3563  {
3564  RET_HANDLE(WindowFromPoint(pt));
3565  }
3566  }
3567  else if (!strcmp(argv[0].strptr,"RECT"))
3568  {
3569  RECT r;
3570  CHECKARG(2,2);
3571  retstr->strlength = 0;
3572  GET_HANDLE(argv[1].strptr, hW);
3573  if (hW && GetWindowRect(hW, &r))
3574  {
3575  sprintf(retstr->strptr,"%d,%d,%d,%d",r.left, r.top, r.right, r.bottom);
3576  retstr->strlength = strlen(retstr->strptr);
3577  }
3578  return 0;
3579  }
3580  else if (!strcmp(argv[0].strptr,"GETSTATE"))
3581  {
3582  CHECKARG(2,2);
3583  retstr->strlength = 0;
3584  GET_HANDLE(argv[1].strptr, hW);
3585  if (hW)
3586  {
3587  retstr->strptr[0] = '\0';
3588  if (!IsWindow(hW)) strcpy(retstr->strptr, "Illegal Handle");
3589  else
3590  {
3591  if (IsWindowEnabled(hW)) strcat(retstr->strptr, "Enabled ");
3592  else strcat(retstr->strptr, "Disabled ");
3593  if (IsWindowVisible(hW)) strcat(retstr->strptr, "Visible ");
3594  else strcat(retstr->strptr, "Invisible ");
3595  if (IsZoomed(hW)) strcat(retstr->strptr, "Zoomed ");
3596  else if (IsIconic(hW)) strcat(retstr->strptr, "Minimized ");
3597  if (GetForegroundWindow() == hW) strcat(retstr->strptr, "Foreground ");
3598  }
3599  retstr->strlength = strlen(retstr->strptr);
3600  }
3601  return 0;
3602  }
3603  else if (!strcmp(argv[0].strptr,"GETSTYLE"))
3604  {
3605  CHECKARG(2,2);
3606  GET_HANDLE(argv[1].strptr, hW);
3607  if (hW)
3608  {
3609  WINDOWINFO wi;
3610 
3611  wi.cbSize = sizeof(WINDOWINFO);
3612  if ( GetWindowInfo(hW, &wi) )
3613  {
3614  _snprintf(retstr->strptr, RXAUTOBUFLEN, "0x%08x 0x%08x", wi.dwStyle, wi.dwExStyle);
3615  retstr->strlength = strlen(retstr->strptr);
3616  return 0;
3617  }
3618  else
3619  {
3620  _snprintf(retstr->strptr, RXAUTOBUFLEN, "%d", GetLastError());
3621  retstr->strlength = strlen(retstr->strptr);
3622  return 0;
3623  }
3624  }
3625  }
3626  else if (!strcmp(argv[0].strptr,"ENABLE"))
3627  {
3628  CHECKARG(3,3);
3629  GET_HANDLE(argv[1].strptr, hW);
3630  if (hW)
3631  {
3632  RETC(EnableWindow(hW, atoi(argv[2].strptr)));
3633  }
3634  }
3635  else if (!strcmp(argv[0].strptr,"MOVE"))
3636  {
3637  CHECKARG(4,4);
3638  GET_HANDLE(argv[1].strptr, hW);
3639  if (hW)
3640  {
3641  RECT r;
3642  if (!GetWindowRect(hW, &r))
3643  {
3644  RETC(1);
3645  }
3646  RETC(!MoveWindow(hW, atol(argv[2].strptr), atol(argv[3].strptr), r.right - r.left, r.bottom-r.top, TRUE))
3647  }
3648  }
3649  else if (!strcmp(argv[0].strptr,"SIZE"))
3650  {
3651  CHECKARG(4,4);
3652  GET_HANDLE(argv[1].strptr, hW);
3653  if (hW)
3654  {
3655  RECT r;
3656  if (!GetWindowRect(hW, &r))
3657  {
3658  RETC(1);
3659  }
3660  RETC(!SetWindowPos(hW, NULL, r.left, r.top, atol(argv[2].strptr), atol(argv[3].strptr), SWP_NOMOVE | SWP_NOZORDER))
3661  }
3662  }
3663  RETC(1); /* in this case 0 is an error */
3664 }
3665 
3666 
3667 
3668 size_t RexxEntry WSCtrlSend(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
3669 {
3670  HWND hW,HwndFgWin;
3671 
3672  CHECKARG(1,10);
3673 
3674  if ( strcmp(argv[0].strptr, "KEY") == 0 )
3675  {
3676  CHECKARG(4,5);
3677  GET_HANDLE(argv[1].strptr, hW);
3678  if (hW)
3679  {
3680  WPARAM k;
3681  LPARAM l;
3682 
3683  k = (WPARAM)atoi(argv[2].strptr);
3684  if (argc == 5)
3685  {
3686  l = strtol(argv[4].strptr,'\0',16);
3687  }
3688  else
3689  {
3690  l = 0;
3691  }
3692 
3693  /* Combination of keys (e.g. SHIFT END) and function keys (e.g. F1) dont worked */
3694  /* The reson is, that the WM_KEWUP/Doen message cannot handle this ! */
3695  /* To get the function keys working, WM_KEYDOWN/UP can be used, but the message MUST be posted! */
3696  /* To get key combination working the keybd_event function must be use. But ..... */
3697  /* The window must be in the Foreground and The handle must be associated: */
3698  /* To get these working, the REXX code must be lok like: */
3699  /* EditHandle = npe~Handle */
3700  /* npe~ToForeground */
3701  /* npe~AssocWindow(np~Handle) */
3702  /* npe~SendKeyDown("SHIFT") */
3703  /* npe~SendKeyDown("HOME") */
3704  /* npe~SendKeyUp("SHIFT") */
3705  /* npe~SendKeyUp("HOME") */
3706 
3707  if (argv[3].strptr[0] == 'D')
3708  {
3709  HwndFgWin = GetForegroundWindow();
3710  if ( hW == HwndFgWin)
3711  {
3712  if (l != 0)
3713  {
3714  keybd_event((BYTE)k,0,KEYEVENTF_EXTENDEDKEY,0);
3715  }
3716  else
3717  {
3718  keybd_event((BYTE)k,0,0,0);
3719  }
3720 
3721  retstr->strptr[0] = '1';
3722  retstr->strptr[1] = '\0';
3723  }
3724  else
3725  {
3726  itoa(PostMessage(hW,WM_KEYDOWN, k,l), retstr->strptr, 10);
3727  }
3728  }
3729  else if (argv[3].strptr[0] == 'U')
3730  {
3731  HwndFgWin = GetForegroundWindow();
3732  if ( hW == HwndFgWin )
3733  {
3734  if (l == 1)
3735  {
3736  keybd_event((BYTE)k,0,KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY,0);
3737  }
3738  else
3739  {
3740  keybd_event((BYTE)k,0,KEYEVENTF_KEYUP,0);
3741  }
3742 
3743  retstr->strptr[0] = '1';
3744  retstr->strptr[1] = '\0';
3745  }
3746  else
3747  {
3748  itoa(PostMessage(hW,WM_KEYUP, k, l | 0xC0000000), retstr->strptr, 10);
3749  retstr->strptr[0] = '1';
3750  retstr->strptr[1] = '\0';
3751  }
3752  }
3753  /* WM_CHAR don't works for ALT key combinations */
3754  /* WM_SYSKEYDOWN and hW,WM_SYSKEYUP must used */
3755  else if ( l != 0 )
3756  {
3757  itoa(PostMessage(hW,WM_SYSKEYDOWN, k,l), retstr->strptr, 10);
3758  itoa(PostMessage(hW,WM_SYSKEYUP, k,l), retstr->strptr, 10);
3759  }
3760  else
3761  {
3762  itoa(SendNotifyMessage(hW,WM_CHAR, k,l), retstr->strptr, 10);
3763  }
3764  retstr->strlength = strlen(retstr->strptr);
3765  return 0;
3766  }
3767  }
3768  else if ( strcmp(argv[0].strptr, "MSG") == 0 )
3769  {
3770  UINT msg;
3771  WPARAM wp;
3772  LPARAM lp;
3773 
3774  CHECKARG(5,6);
3775 
3776  GET_HANDLE(argv[1].strptr, hW);
3777  GET_HANDLE(argv[2].strptr, msg);
3778  GET_HANDLE(argv[3].strptr, wp);
3779  GET_HANDLE(argv[4].strptr, lp);
3780 
3781  /* The 6th arg can, optionally, be used as an extra qualifier. Currently
3782  * only used in one case.
3783  */
3784  if ( argc > 5 && argv[5].strptr[0] == 'E' )
3785  {
3786  lp = (LPARAM)"Environment";
3787  }
3788  if ( SendNotifyMessage(hW, msg, wp, lp) )
3789  {
3790  RETVAL(0)
3791  }
3792  else
3793  {
3794  RETVAL(GetLastError())
3795  }
3796  }
3797  else if ( strcmp(argv[0].strptr,"TO") == 0 ) /* Send message Time Out */
3798  {
3799  UINT msg, timeOut;
3800  WPARAM wp;
3801  LPARAM lp;
3802  DWORD_PTR dwResult;
3803  LRESULT lResult;
3804 
3805  CHECKARG(6,7);
3806 
3807  GET_HANDLE(argv[1].strptr, hW);
3808  GET_HANDLE(argv[2].strptr, msg);
3809  GET_HANDLE(argv[3].strptr, wp);
3810  GET_HANDLE(argv[4].strptr, lp);
3811  GET_HANDLE(argv[5].strptr, timeOut);
3812 
3813  /* The 7th arg can, optionally, be used as an extra qualifier. Currently
3814  * only used in one case.
3815  */
3816  if ( argc > 6 && argv[6].strptr[0] == 'E' )
3817  {
3818  lp = (LPARAM)"Environment";
3819  }
3820 
3821  lResult = SendMessageTimeout(hW, msg, wp, lp, MSG_TIMEOUT_OPTS, timeOut, &dwResult);
3822  if ( lResult == 0 )
3823  {
3824  /* Some error occurred, if last error is 0 it is a time out,
3825  * otherwise some other system error.
3826  */
3827  DWORD err = GetLastError();
3828  if ( err == 0 )
3829  {
3830  err = 1; // We are going to negate this.
3831  }
3832  RETVAL(-(INT)err);
3833  }
3834  else
3835  {
3836  return dwordPtrToRexx(dwResult, retstr);
3837  }
3838  }
3839  else if ( strcmp(argv[0].strptr, "MAP") == 0 )
3840  {
3841  CHECKARG(2,2);
3842  RETVAL(MapVirtualKey((UINT)atoi(argv[1].strptr), 2));
3843  }
3844 
3845  RETC(1); /* in this case 0 is an error */
3846 }
3847 
3848 
3849 
3850 size_t RexxEntry WSCtrlMenu(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
3851 {
3852  CHECKARG(1,10);
3853 
3854  if (!strcmp(argv[0].strptr,"GET"))
3855  {
3856  CHECKARG(2,2);
3857  HWND hMenu;
3858  GET_HANDLE(argv[1].strptr, hMenu);
3859 
3860  RET_HANDLE(GetMenu(hMenu));
3861  }
3862  else if (!strcmp(argv[0].strptr,"SYS"))
3863  {
3864  CHECKARG(2,2);
3865  HWND hMenu;
3866  GET_HANDLE(argv[1].strptr, hMenu);
3867 
3868  RET_HANDLE(GetSystemMenu(hMenu, FALSE));
3869  }
3870  else if (!strcmp(argv[0].strptr,"SUB"))
3871  {
3872  CHECKARG(3,3);
3873  HMENU hMenu;
3874  GET_HANDLE(argv[1].strptr, hMenu);
3875 
3876  RET_HANDLE(GetSubMenu(hMenu, atoi(argv[2].strptr)));
3877  }
3878  else if (!strcmp(argv[0].strptr,"ID"))
3879  {
3880  CHECKARG(3,3);
3881  HMENU hMenu;
3882  GET_HANDLE(argv[1].strptr, hMenu);
3883 
3884  RETVAL(GetMenuItemID(hMenu, atoi(argv[2].strptr)));
3885  }
3886  else if (!strcmp(argv[0].strptr,"CNT"))
3887  {
3888  CHECKARG(2,2);
3889  HMENU hMenu;
3890  GET_HANDLE(argv[1].strptr, hMenu);
3891 
3892  RETVAL(GetMenuItemCount(hMenu));
3893  }
3894  else if (!strcmp(argv[0].strptr,"TEXT"))
3895  {
3896  HMENU hM;
3897  UINT flag;
3898  CHECKARG(4,4);
3899 
3900  GET_HANDLE(argv[1].strptr, hM);
3901  if (!hM)
3902  {
3903  RETC(0);
3904  }
3905  if (argv[3].strptr[0] == 'C')
3906  {
3907  flag = MF_BYCOMMAND;
3908  }
3909  else
3910  {
3911  flag = MF_BYPOSITION;
3912  }
3913 
3914  retstr->strlength = GetMenuString(hM, atol(argv[2].strptr), retstr->strptr, 255, flag);
3915  return 0;
3916  }
3917  else if (!strcmp(argv[0].strptr,"STATE"))
3918  {
3919  CHECKARG(2,4);
3920 
3921  HMENU hMenu;
3922  GET_HANDLE(argv[1].strptr, hMenu);
3923 
3924  if ( argc == 2 )
3925  {
3926  RETVAL(IsMenu(hMenu));
3927  }
3928 
3929  CHECKARG(4,4);
3930 
3931  UINT flags;
3932  UINT pos = atoi(argv[3].strptr);
3933 
3934  flags = GetMenuState(hMenu, pos, MF_BYPOSITION);
3935 
3936  if ( flags == 0xffffffff )
3937  {
3938  // Error, most likely no such position. Return false.
3939  RETVAL(0);
3940  }
3941 
3942  if ( argv[2].strptr[0] == 'M' )
3943  {
3944  RETVAL((flags & MF_POPUP) ? 1 : 0);
3945  }
3946  else if ( argv[2].strptr[0] == 'C' )
3947  {
3948  RETVAL((flags & MF_CHECKED) ? 1 : 0);
3949  }
3950  else
3951  {
3952  RETVAL(((flags & MF_POPUP) == 0 && (flags & MF_SEPARATOR) != 0) ? 1 : 0);
3953  }
3954  }
3955 
3956  RETC(1) /* in this case 0 is an error */
3957 }
3958 
3959 
3960 
3961 size_t RexxEntry WSClipboard(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
3962 {
3963  BOOL ret = FALSE;
3964 
3965  CHECKARG(1,2);
3966 
3967  if (!strcmp(argv[0].strptr, "COPY"))
3968  {
3969  HGLOBAL hmem;
3970  char * membase;
3971 
3972  CHECKARG(2,2);
3973  hmem = (char *)GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, argv[1].strlength + 1);
3974  membase = (char *) GlobalLock(hmem);
3975 
3976  if (membase)
3977  {
3978  memcpy(membase, argv[1].strptr, argv[1].strlength + 1);
3979  if (OpenClipboard(NULL) && EmptyClipboard())
3980  {
3981  ret = SetClipboardData(CF_TEXT, hmem) != NULL;
3982  }
3983  GlobalUnlock(membase);
3984  CloseClipboard();
3985  }
3986  RETC(!ret)
3987  }
3988  else if (!strcmp(argv[0].strptr, "PASTE"))
3989  {
3990  HGLOBAL hmem;
3991  char * membase;
3992  if (IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL))
3993  {
3994  hmem = GetClipboardData(CF_TEXT);
3995  membase = (char *) GlobalLock(hmem);
3996  if (membase)
3997  {
3998  size_t s = GlobalSize(hmem);
3999  if (s>255)
4000  {
4001  retstr->strptr = (char *)GlobalAlloc(GMEM_FIXED, s);
4002  if (retstr->strptr == NULL)
4003  {
4004  CloseClipboard();
4005  return -1;
4006  }
4007  }
4008  s = strlen(membase);
4009  memcpy(retstr->strptr, membase, s+1);
4010  retstr->strlength = s;
4011  GlobalUnlock(membase);
4012  CloseClipboard();
4013  return 0;
4014  }
4015  else
4016  {
4017  CloseClipboard();
4018  }
4019  }
4020  retstr->strlength = 0;
4021  return 0;
4022  }
4023  else if (!strcmp(argv[0].strptr, "EMPTY"))
4024  {
4025  if (IsClipboardFormatAvailable(CF_TEXT))
4026  {
4027  BOOL ret;
4028  if (!OpenClipboard(NULL))
4029  {
4030  RETC(1)
4031  }
4032  ret = !EmptyClipboard();
4033  CloseClipboard();
4034  RETC(ret)
4035  }
4036  RETC(0)
4037  }
4038  else if (!strcmp(argv[0].strptr, "AVAIL"))
4039  {
4040  RETC(IsClipboardFormatAvailable(CF_TEXT))
4041  }
4042  else
4043  {
4044  RETVAL(-1)
4045  }
4046 }
4047 
4048 
4049 VOID Little2BigEndian(BYTE *pbInt, INT iSize)
4050 {
4051  /* convert any integer number from little endian to big endian or vice versa */
4052  BYTE bTemp[32];
4053  INT i;
4054 
4055  if (iSize <= 32)
4056  {
4057  for (i = 0; i < iSize; i++)
4058  {
4059  bTemp[i] = pbInt[iSize - i - 1];
4060  }
4061 
4062  memcpy(pbInt, bTemp, iSize);
4063  }
4064 }
4065 
4066 /**
4067  * Prior to 4.0.0, this function was documented as a work around to use the
4068  * WindowObject class when no WindowManager object had been instantiated. So
4069  * for now it needs to stay. Does nothing.
4070  */
4071 size_t RexxEntry InstWinSysFuncs(const char *funcname, size_t argc, CONSTRXSTRING *argv, const char *qname, RXSTRING *retstr)
4072 {
4073  RETC(0)
4074 }
4075 
4076 
4077 // now build the actual entry lists
4079 {
4088  REXX_CLASSIC_ROUTINE(InstWinFuncs, InstWinSysFuncs),
4090 };
4091 
4092 
4094  REXX_METHOD(WSRegistry_init, WSRegistry_init),
4095  REXX_METHOD(setCurrent_Key, setCurrent_Key),
4096  REXX_METHOD(getCurrent_Key, getCurrent_Key),
4097  REXX_METHOD(getLocal_Machine, getLocal_Machine),
4098  REXX_METHOD(getCurrent_User, getCurrent_User),
4099  REXX_METHOD(getUsers, getUsers),
4100  REXX_METHOD(getClasses_Root, getClasses_Root),
4101  REXX_METHOD(getPerformance_Data, getPerformance_Data),
4102  REXX_METHOD(getCurrent_Config, getCurrent_Config),
4103  REXX_METHOD(WSRegistry_delete, WSRegistry_delete),
4104  REXX_METHOD(WSRegistry_open, WSRegistry_open),
4105 
4106  REXX_METHOD(WSEventLog_test, WSEventLog_test),
4107  REXX_METHOD(WSEventLog_init, WSEventLog_init),
4108  REXX_METHOD(WSEventLog_uninit, WSEventLog_uninit),
4109  REXX_METHOD(WSEventLog_open, WSEventLog_open),
4110  REXX_METHOD(WSEventLog_close, WSEventLog_close),
4111  REXX_METHOD(WSEventLog_write, WSEventLog_write),
4112  REXX_METHOD(WSEventLog_readRecords, WSEventLog_readRecords),
4113  REXX_METHOD(WSEventLog_minimumReadSet, WSEventLog_minimumReadSet),
4114  REXX_METHOD(WSEventLog_minimumReadGet, WSEventLog_minimumReadGet),
4115  REXX_METHOD(WSEventLog_getNumber, WSEventLog_getNumber),
4116  REXX_METHOD(WSEventLog_getFirst, WSEventLog_getFirst),
4117  REXX_METHOD(WSEventLog_getLast, WSEventLog_getLast),
4118  REXX_METHOD(WSEventLog_isFull, WSEventLog_isFull),
4119  REXX_METHOD(WSEventLog_getLogNames, WSEventLog_getLogNames),
4120  REXX_METHOD(WSEventLog_clear, WSEventLog_clear),
4122 };
4123 
4125 {
4127  REXX_INTERPRETER_4_0_0, // anything after 4.0.0 will work
4128  "RXWINSYS", // name of the package
4129  "4.0", // package information
4130  NULL, // no load/unload functions
4131  NULL,
4132  rxwinsys_functions, // the exported functions
4133  rxwinsys_methods // the exported methods
4134 };
4135 
4136 // package loading stub.
RexxReturnCode RexxEntry RexxVariablePool(PSHVBLOCK pshvblock)
int type
Definition: cmdparse.cpp:383
#define argumentExists(i)
Definition: oorexxapi.h:3777
#define REXX_INTERPRETER_4_0_0
Definition: oorexxapi.h:216
#define REXX_METHOD(n, e)
Definition: oorexxapi.h:211
#define REXX_CLASSIC_ROUTINE(n, e)
Definition: oorexxapi.h:192
#define argumentOmitted(i)
Definition: oorexxapi.h:3778
#define REXX_LAST_ROUTINE()
Definition: oorexxapi.h:193
#define REXX_LAST_METHOD()
Definition: oorexxapi.h:212
RexxArrayObject ARGLIST
Definition: oorexxapi.h:4331
#define STANDARD_PACKAGE_HEADER
Definition: oorexxapi.h:230
#define Rexx_Error_Incorrect_method_noclass
Definition: oorexxerrors.h:506
#define Rexx_Error_Incorrect_method_noarg
Definition: oorexxerrors.h:465
#define Rexx_Error_Invalid_argument_range
Definition: oorexxerrors.h:419
#define Rexx_Error_Execution_user_defined
Definition: oorexxerrors.h:530
#define Rexx_Error_Incorrect_method_list
Definition: oorexxerrors.h:475
#define Rexx_Error_System_service_user_defined
Definition: oorexxerrors.h:404
#define Rexx_Error_Incorrect_method_user_defined
Definition: oorexxerrors.h:461
const char * CSTRING
Definition: rexx.h:78
size_t logical_t
Definition: rexx.h:231
struct _RexxStringObject * RexxStringObject
Definition: rexx.h:128
struct _RexxArrayObject * RexxArrayObject
Definition: rexx.h:130
struct _RexxObjectPtr * RexxObjectPtr
Definition: rexx.h:127
#define NULLOBJECT
Definition: rexx.h:147
void *REXXENTRY RexxAllocateMemory(size_t)
struct _RexxPointerObject * RexxPointerObject
Definition: rexx.h:132
#define RexxEntry
Definition: rexx.h:412
#define RXSHV_BADN
Definition: rexxapidefs.h:116
#define RXAUTOBUFLEN
Definition: rexxapidefs.h:58
#define RXSHV_SYSET
Definition: rexxapidefs.h:100
#define HANDLE_ATTRIBUTE
Definition: rxwinsys.cpp:1799
#define MIN_READ_KB_COUNT
Definition: rxwinsys.cpp:1812
void systemServiceException(RexxMethodContext *context, char *msg)
Definition: rxwinsys.cpp:2165
HKEY getParentKeyHandle(RexxMethodContext *c, CSTRING hkParent)
Definition: rxwinsys.cpp:338
#define END_RECORD_OUT_OF_RANGE_MSG
Definition: rxwinsys.cpp:1819
#define WSEL_DEFAULT_EVENTS_ARRAY_SIZE
Definition: rxwinsys.cpp:1797
LONG HandleArgError(PRXSTRING r, BOOL ToMuch)
Definition: rxwinsys.cpp:102
bool checkReadRecordsArgs(RexxMethodContext *context, HANDLE hLog, CSTRING direction, uint32_t start, uint32_t *count, DWORD *flags, DWORD *rc)
Definition: rxwinsys.cpp:2475
#define RETERR
Definition: rxwinsys.cpp:123
RexxMethod3(RexxObjectPtr, WSRegistry_open, OPTIONAL_CSTRING, hkParent, OPTIONAL_CSTRING, subKeyName, OPTIONAL_CSTRING, access)
Definition: rxwinsys.cpp:472
#define IDS_ALL_9x_DESKTOP
Definition: rxwinsys.cpp:1451
#define RETVAL(retvalue)
Definition: rxwinsys.cpp:131
bool getOpenEventLog(RexxMethodContext *context, const char *server, const char *source, HANDLE *pHandle, DWORD *pRC)
Definition: rxwinsys.cpp:2310
RexxMethod4(int, WSEventLog_test, RexxStringObject, data, OPTIONAL_CSTRING, server, OPTIONAL_CSTRING, source, ARGLIST, args)
Definition: rxwinsys.cpp:3307
#define SET_VARIABLE(varname, data, retc)
Definition: rxwinsys.cpp:165
size_t RexxEntry InstWinSysFuncs(const char *funcname, size_t argc, CONSTRXSTRING *argv, const char *qname, RXSTRING *retstr)
Definition: rxwinsys.cpp:4071
#define BAD_RECORD_ARRAY_MSG
Definition: rxwinsys.cpp:1815
#define IDS_ALL_NT_DESKTOP
Definition: rxwinsys.cpp:1448
#define START_RECORD_OUT_OF_RANGE_MSG
Definition: rxwinsys.cpp:1818
BOOL AddPMItem(const char *lpszCmdLine, const char *lpszCaption, const char *lpszIconPath, WORD wIconIndex, const char *lpszDir, BOOL bLast, const char *lpszHotKey, const char *lpszModifier, BOOL bMin)
Definition: rxwinsys.cpp:1308
BOOL findAndFormatDescription(char *files, DWORD msgID, char **ppInserts, LPVOID *lpMsgBuf)
Definition: rxwinsys.cpp:1894
#define RETC(retcode)
Definition: rxwinsys.cpp:116
size_t RexxEntry WSRegistryFile(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
Definition: rxwinsys.cpp:1029
void lookupMessageFile(char *pValueName, char *logName, char *source, char *fileBuffer)
Definition: rxwinsys.cpp:1838
#define IDS_REGISTRY_KEY_CURRENT_SHELLFOLDER
Definition: rxwinsys.cpp:1395
void setCurrentHandle(RexxMethodContext *context, HANDLE h)
Definition: rxwinsys.cpp:2193
RexxMethod7(uint32_t, WSEventLog_write, OPTIONAL_CSTRING, server, OPTIONAL_CSTRING, source, OPTIONAL_uint16_t, t, OPTIONAL_uint16_t, category, OPTIONAL_uint32_t, id, OPTIONAL_RexxStringObject, rawData, ARGLIST, args)
Definition: rxwinsys.cpp:2849
#define GET_HANDLE(argum, ghk)
Definition: rxwinsys.cpp:162
RexxMethod0(RexxObjectPtr, WSRegistry_init)
Definition: rxwinsys.cpp:370
#define GET_HKEY(argum, ghk)
Definition: rxwinsys.cpp:149
RexxArrayObject getRecordArray(RexxMethodContext *context)
Definition: rxwinsys.cpp:2252
size_t RexxEntry WSProgManager(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
Definition: rxwinsys.cpp:1712
#define INITCODE_ATTRIBUTE
Definition: rxwinsys.cpp:1801
OOREXX_GET_PACKAGE(rxwinsys)
BOOL AddPMGroup(const char *lpszGroup, const char *lpszPath)
Definition: rxwinsys.cpp:1257
size_t RexxEntry WSCtrlSend(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
Definition: rxwinsys.cpp:3668
size_t RexxEntry WSRegistryKey(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
Definition: rxwinsys.cpp:511
BOOL LeavePM(BOOL bSaveGroups)
Definition: rxwinsys.cpp:1367
void pointer2string(PRXSTRING result, void *pointer)
Definition: rxwinsys.cpp:244
char * getEventUserName(PEVENTLOGRECORD pEvLogRecord)
Definition: rxwinsys.cpp:2078
#define MAX_REGISTRY_KEY_SIZE
Definition: rxwinsys.cpp:53
#define MAX_READ_KB_COUNT
Definition: rxwinsys.cpp:1810
DWORD getMinimumReadBufferSize(RexxMethodContext *c)
Definition: rxwinsys.cpp:2227
size_t RexxEntry WSCtrlMenu(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
Definition: rxwinsys.cpp:3850
#define MSG_TIMEOUT_OPTS
Definition: rxwinsys.cpp:59
RexxMethod5(uint32_t, WSEventLog_readRecords, OPTIONAL_CSTRING, direction, OPTIONAL_CSTRING, server, OPTIONAL_CSTRING, source, OPTIONAL_uint32_t, start, OPTIONAL_uint32_t, count)
Definition: rxwinsys.cpp:3010
#define STR_BUFFER
Definition: rxwinsys.cpp:49
#define MAX_TIME_DATE
Definition: rxwinsys.cpp:50
bool isGoodEventType(uint16_t type)
Definition: rxwinsys.cpp:2575
#define RET_HANDLE(retvalue)
Definition: rxwinsys.cpp:138
size_t RexxEntry WSRegistryValue(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
Definition: rxwinsys.cpp:641
RexxPackageEntry rxwinsys_package_entry
Definition: rxwinsys.cpp:4124
size_t getEventBinaryData(PEVENTLOGRECORD pEvLogRecord, char **ppBinData)
Definition: rxwinsys.cpp:2136
#define TOO_SMALLBUFFER_MSG
Definition: rxwinsys.cpp:1817
HDDEDATA CALLBACK DDECallback(UINT wType, UINT wFmt, HCONV hConv, HSZ hsz1, HSZ hsz2, HDDEDATA hDDEData, DWORD dwData1, DWORD dwData2)
Definition: rxwinsys.cpp:1183
BOOL GetAllUserDesktopLocation(LPBYTE szDesktopDir, LPDWORD lpcbData)
Definition: rxwinsys.cpp:1454
bool isHex(CSTRING value)
Definition: rxwinsys.cpp:206
BOOL DeletePMItem(const char *lpszItem)
Definition: rxwinsys.cpp:1352
BOOL AddPMDesktopIcon(const char *lpszName, const char *lpszProgram, const char *lpszIcon, int iIconIndex, const char *lpszWorkDir, const char *lpszLocation, const char *lpszArguments, int iScKey, int iScModifier, const char *lpszRun)
Definition: rxwinsys.cpp:1534
INT DelPMDesktopIcon(const char *lpszName, const char *lpszLocation)
Definition: rxwinsys.cpp:1682
BOOL ShowPMGroup(const char *lpszGroup, WORD wCmd)
Definition: rxwinsys.cpp:1292
RexxMethod1(RexxObjectPtr, setCurrent_Key, RexxStringObject, regKeyHandle)
Definition: rxwinsys.cpp:381
RexxMethod2(uint32_t, WSRegistry_delete, OPTIONAL_CSTRING, hkParent, CSTRING, subKeyName)
Definition: rxwinsys.cpp:436
#define MIN_READ_BUFFER
Definition: rxwinsys.cpp:1813
size_t dwordPtrToRexx(DWORD_PTR val, PRXSTRING r)
Definition: rxwinsys.cpp:95
#define CHECKARG(argexpctl, argexpcth)
Definition: rxwinsys.cpp:111
int32_t getEventLogNumber(RexxMethodContext *context, LogNumberOp numberType, CSTRING server, CSTRING source)
Definition: rxwinsys.cpp:2382
unsigned int wrongClassException(RexxMethodContext *c, int pos, const char *n)
Definition: rxwinsys.cpp:2186
void getEventDescription(PEVENTLOGRECORD pEvLogRecord, const char *pchSource, char **ppMessage)
Definition: rxwinsys.cpp:1944
#define IDS_REGISTRY_KEY_ALL_9x_SHELLFOLDER
Definition: rxwinsys.cpp:1450
#define MSG_TIMEOUT
Definition: rxwinsys.cpp:55
size_t RexxEntry WSClipboard(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
Definition: rxwinsys.cpp:3961
void outOfMemoryException(RexxMethodContext *c)
Definition: rxwinsys.cpp:2170
#define MIN_READ_MAX_ATTRIBUTE
Definition: rxwinsys.cpp:1804
void wrongArgValueException(RexxMethodContext *c, int pos, const char *list, RexxObjectPtr actual)
Definition: rxwinsys.cpp:2175
BOOL DeletePMGroup(const char *lpszGroup)
Definition: rxwinsys.cpp:1278
HANDLE getCurrentHandle(RexxMethodContext *context)
Definition: rxwinsys.cpp:2206
#define WSEL_DEFAULT_SOURCE
Definition: rxwinsys.cpp:1798
BOOL GetCurrentUserDesktopLocation(LPBYTE szDesktopDir, LPDWORD lpcbData)
Definition: rxwinsys.cpp:1398
size_t RexxEntry WSCtrlWindow(const char *funcname, size_t argc, CONSTRXSTRING argv[], const char *qname, PRXSTRING retstr)
Definition: rxwinsys.cpp:3314
#define IDS_REGISTRY_KEY_ALL_NT_SHELLFOLDER
Definition: rxwinsys.cpp:1447
DWORD closeEventLog(RexxMethodContext *context)
Definition: rxwinsys.cpp:2351
#define RECORDS_ATTRIBUTE
Definition: rxwinsys.cpp:1800
#define MAX_READ_BUFFER
Definition: rxwinsys.cpp:1811
#define MIN_READ_MIN_ATTRIBUTE
Definition: rxwinsys.cpp:1803
#define IDS_CURRENT_DESKTOP
Definition: rxwinsys.cpp:1396
BOOL string2pointer(const char *string, void **pointer)
Definition: rxwinsys.cpp:224
#define GET_TYPE_INDEX(type, index)
Definition: rxwinsys.cpp:179
VOID Little2BigEndian(BYTE *pbInt, INT iSize)
Definition: rxwinsys.cpp:4049
LogNumberOp
Definition: rxwinsys.cpp:1821
@ record_count
Definition: rxwinsys.cpp:1821
@ youngest_record
Definition: rxwinsys.cpp:1821
@ oldest_record
Definition: rxwinsys.cpp:1821
RexxRoutineEntry rxwinsys_functions[]
Definition: rxwinsys.cpp:4078
void memupper(char *location, size_t length)
Definition: rxwinsys.cpp:76
#define MIN_READ_BUFFER_ATTRIBUTE
Definition: rxwinsys.cpp:1802
BOOL IsRunningNT()
Definition: rxwinsys.cpp:260
BOOL ProgmanCmd(LPSTR lpszCmd)
Definition: rxwinsys.cpp:1195
RexxMethodEntry rxwinsys_methods[]
Definition: rxwinsys.cpp:4093
const char * strptr
Definition: rexx.h:163
size_t strlength
Definition: rexx.h:162
size_t strlength
Definition: rexx.h:157
char * strptr
Definition: rexx.h:158
Definition: oorexxapi.h:198
Definition: oorexxapi.h:242
Definition: oorexxapi.h:177
size_t shvvaluelen
Definition: rexx.h:209
CONSTANT_RXSTRING shvname
Definition: rexx.h:206
unsigned char shvret
Definition: rexx.h:211
unsigned char shvcode
Definition: rexx.h:210
RXSTRING shvvalue
Definition: rexx.h:207
size_t shvnamelen
Definition: rexx.h:208
struct _SHVBLOCK * shvnext
Definition: rexx.h:205
static PLL pTail
static OSVERSIONINFO version_info
unsigned short uint16_t
int int32_t
unsigned int uint32_t