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