NativeAPIEmulation.java

/*
** Module   : NativeAPIEmulation.java
** Abstract : Native API emulation.
**
** Copyright (c) 2018-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- --------------------------------------Description----------------------------------------
** 001 HC  20181211 Created initial version
** 002 HC  20190127 Added more emulated Windows API functions.
**     HC  20190206 Added limited emulation of getLocaleInfoA.
**     OM  20190302 Fixed getParent().
**     HC  20190302 Implemented emulation of GetWindowRect and GetClientRect Windows API
**                  functions.
**     HC  20190305 Implemented emulation of GetCursorPos and ScreenToClient Windows API
**                  functions.
**     HC  20190306 Implemented Windows API emulation for functions ShowScrollBar and
**                  GetActiveWindow and limited emulation for GetSystemMetrics function.
** 003 EVL 20190629 Adding getUserNameA, LockWindowUpdate, IsWindow implementations.
** 004 EVL 20190708 Adding wsock32.dll native calls to get the computer name/IP.  Fix for
**                  user32.LockWindowUpdate() unlocking issue.  Adding user32 call to
**                  DisableProcessWindowsGhosting, stubbing.  Fixed native API names camel case
**                  issues that prevent the methods to be properly integrated.  Added new paameter
**                  to process for getLocaleInfoA(), changing error handling approach.
**     HC  20190806 Implemented SetParent Windows native function and the system-level widget
**                  hierarchy.
** 005 HC  20191023 Improved emulation of getSystemMetrics, getWindowRect and getClientRect Win32
**                  functions.
** 006 IAS 20191140 Added ClientToScreen Windows native function emulation.
**     HC  20191204 Improved emulation of setFocus Win32 system function. Plus related changes to
**                  focus handling.
**     HC  20191205 Fixed ShowScrollBar not showing/hiding scroll bars.
** 007 RFB 20200210 Add sleep emulation. Note that there may be a todo to handle the 
**                  InterruptedException.
**     RFB 20200219 Added getComputerNameA emulation. Some questions arise with respect to what
**                  name should be returned. We are returning LogicalTerminal.getHostName()
** 008 EVL 20200206 Fixed locked window issue whan we need to handle several locked windows.
**     EVL 20200219 Adding getForegroundWindow() native API implementation for windows.user32.
**                  Changed setFocus() implementation to force enclosing window activation.
** 009 RFB 20200304 Fixed some minor Javadoc warnings in sleep() method.
** 010 EVL 20200312 Changed getForegroundWindow() implementation.
**     HC  20200331 Implemented support for OPEN-POPUP legacy method, which opens popup menu set
**                  on the widget.
**     EVL 20200409 Improved getForegroundWindow() considering getCurrentWindow() as fallback.
**                  Adding implementation for emulated user32.IsWindowVisible() native API call.
**                  Changing getAciveWindow() for several caseswhen the system is in migration
**                  state from one active window to another.
** 011 GES 20200628 Added kernel32.GetCurrentProcessId().
** 012 EVL 20200630 Fixed method name issue for getCurrentProcessId().
** 013 RFB 20200724 Added getSysColor for finding the RGB from the theme color palatte.
**     EVL 20210622 Fixed issue in client to screen coordinates transformation.
**     EVL 20210701 Added new emulated call for user32.dll, WindowMove() function.  Fix for Javadoc typo.
**     EVL 20210702 Added TODO explanation for inefficent code usage.
**     EVL 20210705 Considering 0 or less windws width or height as implicit hiding command for WindowMove.
**     VVT 20211203 Fixed: getAsyncKeyState: must return its value in bit 16. See #5850.
**     VVT 20211203 Fixed: getAsyncKeyState: a comment added describing unimplemented features. See #5850.
**     VVT 20211206 'state' replaced by 'modifiers' in code related to key modifiers. See #5850-26.
**     VVT 20220318 user32.getKeyboardState() function added. See #6178-15.
**     VVT 20220401 windows.user32.getKeyboardState() implementation fixed based on the
**                  KeyReader.getKeyboardState().
**     HC  20220928 Improved user32.SetParent to ignore parent assignment when parent and child equal.
**     EVL 20221214 Adding implementation for set/get/loadCursor native functions.  Improved usage of the
**                  LogicalTerminal instance from static context.
**     EVL 20221215 Notes resolution, javadoc fixes for cursor related methods.
**     EVL 20230208 Implementing GetScrollPos and SendMessageA native calls to simulate native based container
**                  content scrolling.  Notes resolution.
** 014 EVL 20230216 Need to find closest frame when getting parent for widget with native like getParent()
**                  usage.
**     HC 20230221  Extended GetParent to allow window:hwnd as the argument.
** 015 HC  20230322 Improved user32.getParent to account for the non-client portion of window
**                  widget.
** 016 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
** 017 EVL 20230711 Improved win32 SetFocus implementation.  The enclosing window is not activating if it is
**                  already active.  Fixed the win32 SetParent implementation, for cases when child is
**                  a WindowWidget.
** 018 SVL 20230803 Added postMessageA function.
** 019 AI  20231009 Added sendMessageA and setCursorPos.
**     AI  20231010 Changed sendMessageA and postMessageA to call for applyMessageA.
**     AI  20231012 Updated setCursorPos to emit a partial-implementation warning, as it can not physically
**                  change the cursor position, only sets a temporal variable with the new cursor position.
** 020 EVL 20240626 Added GetKeyState() user32 emulation.  Implementation the same as for GetAsyncKeyState().
** 021 GBB 20240826 Moving LowLevelBuffer to osresource package.
** 022 CA  20241030 Added immutable character constant support.
** 023 SB  20250129 Modified the cursors list to be accessible.
*/

/*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
 ** published by the Free Software Foundation, either version 3 of the
 ** License, or (at your option) any later version.
 **
 ** This program is distributed in the hope that it will be useful,
 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ** GNU Affero General Public License for more details.
 **
 ** You may find a copy of the GNU Affero GPL version 3 at the following
 ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
 **
 ** Additional terms under GNU Affero GPL version 3 section 7:
 **
 **   Under Section 7 of the GNU Affero GPL version 3, the following additional
 **   terms apply to the works covered under the License.  These additional terms
 **   are non-permissive additional terms allowed under Section 7 of the GNU
 **   Affero GPL version 3 and may not be removed by you.
 **
 **   0. Attribution Requirement.
 **
 **     You must preserve all legal notices or author attributions in the covered
 **     work or Appropriate Legal Notices displayed by works containing the covered
 **     work.  You may not remove from the covered work any author or developer
 **     credit already included within the covered work.
 **
 **   1. No License To Use Trademarks.
 **
 **     This license does not grant any license or rights to use the trademarks
 **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
 **     of Golden Code Development Corporation. You are not authorized to use the
 **     name Golden Code, FWD, or the names of any author or contributor, for
 **     publicity purposes without written authorization.
 **
 **   2. No Misrepresentation of Affiliation.
 **
 **     You may not represent yourself as Golden Code Development Corporation or FWD.
 **
 **     You may not represent yourself for publicity purposes as associated with
 **     Golden Code Development Corporation, FWD, or any author or contributor to
 **     the covered work, without written authorization.
 **
 **   3. No Misrepresentation of Source or Origin.
 **
 **     You may not represent the covered work as solely your work.  All modified
 **     versions of the covered work must be marked in a reasonable way to make it
 **     clear that the modified work is not originating from Golden Code Development
 **     Corporation or FWD.  All modified versions must contain the notices of
 **     attribution required in this license.
 */

package com.goldencode.p2j.util;

import java.awt.event.*;
import java.io.*;
import java.lang.reflect.*;
import java.nio.*;
import java.util.*;

import com.goldencode.p2j.library.*;
import com.goldencode.p2j.main.*;
import com.goldencode.p2j.ui.*;
import com.goldencode.p2j.ui.client.*;
import com.goldencode.p2j.ui.client.driver.*;
import com.goldencode.p2j.util.logging.*;
import com.goldencode.p2j.util.osresource.*;
import com.goldencode.util.*;

/**
 * The class emulates native API calls.
 */
public class NativeAPIEmulation
{
   /**
    * Logger.
    */
   private static CentralLogger LOG = CentralLogger.get(NativeAPIEmulation.class.getSimpleName());

   /**
    * The method returns {@code true} if the supplied os name, library and function are supported by the
    * class. In other words the class must declare function {@code func} in the declared classes
    * {@code os.lib}.
    *
    * @param   os
    *          The target operating system name.
    * @param   lib
    *          The target library name. May contain the file name extension.
    * @param   func
    *          The target function name.
    *
    * @return  {@code true} if the target function is supported, {@code false} otherwise.
    */
   public static boolean emulates(String os, String lib, String func)
   {
      os = removeStringLiteralOptions(os.toLowerCase().trim());

      Class osClass = null;
      for (Class clazz : NativeAPIEmulation.class.getDeclaredClasses())
      {
         if (clazz.getSimpleName().equals(os))
         {
            osClass = clazz;
         }
      }

      if (osClass == null)
      {
         return false;
      }

      lib = removeStringLiteralOptions(lib.toLowerCase().trim());
      lib = lib.replaceFirst("\\.(dll|exe|so|lib)$", "");

      Class libClass = null;
      for (Class clazz : osClass.getDeclaredClasses())
      {
         if (clazz.getSimpleName().equals(lib))
         {
            libClass = clazz;
         }
      }

      if (libClass == null)
      {
         return false;
      }

      // as function name, try the original and then first letter lower case
      func = removeStringLiteralOptions(func.trim());
      String[] funcs = new String[2];
      funcs[0] = func;
      funcs[1] = Character.toLowerCase(func.charAt(0)) + func.substring(1);

      for (Method m : libClass.getDeclaredMethods())
      {
         for (String f : funcs)
         {
            if (m.getName().equals(f))
            {
               return true;
            }
         }
      }

      return false;
   }

   /**
    * Declares windows API symbols and supported libraries.
    */
   public static class windows
   {
      /** NULL constant used in the Windows API. */
      public static final int NULL = 0;

      /** TRUE constant */
      public static final int TRUE = 1;

      /** FALSE constant */
      public static final int FALSE = 0;
      
      /** win32 compatible constatnt for horizontal scrollbar. */
      public static final int SB_HORZ = 0;
      
      /** win32 compatible constatnt for vertical scrollbar. */
      public static final int SB_VERT = 1;
      
      /** win32 compatible constatnt for scrolling messages. */
      public static final int SB_THUMBPOSITION = 4;
      
      /** win32 compatible constatnt for horizontal scrolling message. */
      public static final int WM_HSCROLL = 276;
      
      /** win32 compatible constatnt for vertical scrolling message. */
      public static final int WM_VSCROLL = 277;
      
      /** Mask to extract lo word value from integer. */
      public static final int LO_WORD_MASK = 0x0000FFFF;
      
      /** Mask to extract hi word value from integer. */
      public static final int HI_WORD_MASK = 0xFFFF0000;
      
      /** win32 compatible constatnt for arrow cursor. */
      public static int IDC_ARROW       = 32512;
      
      /** win32 compatible constatnt for hourglass cursor. */
      public static int IDC_WAIT        = 32514;
      
      /** win32 compatible constatnt for small arrow and hourglass cursor. */
      public static int IDC_APPSTARTING = 32650;

      /** win32 compatible constatnt for crosshair cursor. */
      public static int IDC_CROSS       = 32515;
      
      /** win32 compatible constatnt for hand cursor. */
      public static int IDC_HAND        = 32649;
      
      /** win32 compatible constatnt for I-beam cursor. */
      public static int IDC_IBEAM       = 32513;
      
      /** win32 compatible constatnt for crossed circle cursor. */
      public static int IDC_NO          = 32648;
      
      /** win32 compatible constatnt for sizing cursor. */
      public static int IDC_SIZE        = 32640;
      
      /** win32 compatible constatnt for up arrow cursor. */
      public static int IDC_UPARROW     = 32516;
      
      /** win32 compatible constatnt for question mark cursor. */
      public static int IDC_HELP        = 32651;
      
      /** Windows cursors maping table. */
      public final static Map<integer, MousePointer> CURSORS;
      
      /** Init predefined mouse pointers. */
      static
      {
         HashMap<integer, MousePointer> cursors = new HashMap<integer, MousePointer>(16);
         cursors.put(new integer(IDC_ARROW), MousePointer.ARROW); // regular arrow
         cursors.put(new integer(IDC_WAIT), MousePointer.WAIT);   // wait or hourglass
         cursors.put(new integer(IDC_APPSTARTING), MousePointer.APPSTARTING);   // small arrow and hourglass
         cursors.put(new integer(IDC_CROSS), MousePointer.CROSS);   // crosshair
         cursors.put(new integer(IDC_HAND), MousePointer.GLOVE);   // hand
         cursors.put(new integer(IDC_IBEAM), MousePointer.IBEAM);   // I-beam
         cursors.put(new integer(IDC_NO), MousePointer.NO);   // crossed circle
         cursors.put(new integer(IDC_SIZE), MousePointer.SIZE);   // sizing
         cursors.put(new integer(IDC_UPARROW), MousePointer.UPARROW);   // vertical arrow
         cursors.put(new integer(IDC_HELP), MousePointer.HELP);   // question mark
         CURSORS = Collections.unmodifiableMap(cursors);
      }

      /**
       * Declares supported user32.dll functions.
       */
      public static class user32
      {
         /** The currently locked window. */
         private static GenericWidget wndLocked = null;
         
         /**
          * Implementation of GetKeyboardState call.
          * 
          * PARTLY IMPLEMENTED: only several key codes are supported.
          * 
          * @param state
          *        the 256-byte byte array
          */
         public static void getKeyboardState(final memptr state)
         {
            LogicalTerminal.getInstance();
            final int[] keyboardState = LogicalTerminal.getKeyboardState();
            if (keyboardState != null)
            {
               for (int keyCode : keyboardState)
               {
                  switch (keyCode)
                  {
                     case KeyEvent.VK_LEFT:
                        state.setLong(0x80, 38);
                        break;
                     case KeyEvent.VK_UP:
                        state.setLong(0x8000, 38);
                        break;
                     case KeyEvent.VK_RIGHT:
                        state.setLong(0x800000, 38);
                        break;
                     case KeyEvent.VK_DOWN:
                        state.setLong(0x80000000, 38);
                        break;
                     default:
                        // TODO implement other keycodes if and when required
                  }
               }
            }
         }

         /**
          * Returns keyboard state. Currently only Windows virtual keys VK_ALT, VK_CONTROL and VK_SHIFT are
          * supported.
          *
          * The value returned is a 16-bit integer with the bit 16 or
          * bit 0 set if the virtual key was pressed.
          * 
          * Otherwise, 0 is returned.
          *
          * UNIMPLEMENTED:
          *
          * The call should not be idempotent: if this method was
          * called the first time after the key was pressed, the key
          * state is returned in the bit 16, otherwise the key state
          * is returned in the bit 0. Until this feature is
          * implemented, the bit 16 is always used to return the key
          * state.
          *
          * @param   winVKey
          *          Windows virtual key. Must be one of WindowsKey.VK_ALT, WindowsKey.VK_CONTROL
          *          or WindowsKey.VK_SHIFT.
          *
          * @return  see above.
          */
         public static integer getKeyState(integer winVKey)
         {
            if (winVKey == null || winVKey.isUnknown())
            {
               return new integer(NULL);
            }

            final int modifiers = KeyReader.getLastKeyModifiers();

            final boolean result;
            switch (winVKey.intValue())
            {
               case WindowsKey.VK_ALT:
                  result = KeyCode.isAlt(modifiers);
                  break;
               case WindowsKey.VK_CONTROL:
                  result = KeyCode.isCtrl(modifiers);
                  break;
               case WindowsKey.VK_SHIFT:
                  result = KeyCode.isShift(modifiers);
                  break;
               default:
                  LOG.severe("Invalid winKey: " + winVKey);
                  result = false;
            }

            return new integer(result ? 32768 : 0);
         }
         
         /**
          * Returns keyboard state. Currently only Windows virtual keys VK_ALT, VK_CONTROL and VK_SHIFT are
          * supported.
          *
          * Note that the method doesn't behave truly asynchronously, it reads the key state from the last
          * key event.
          * 
          * The value returned is a 16-bit integer with the bit 16 or
          * bit 0 set if the virtual key was pressed.
          * 
          * Otherwise, 0 is returned.
          *
          * UNIMPLEMENTED:
          *
          * The call should not be idempotent: if this method was
          * called the first time after the key was pressed, the key
          * state is returned in the bit 16, otherwise the key state
          * is returned in the bit 0. Until this feature is
          * implemented, the bit 16 is always used to return the key
          * state.
          *
          * @param   winVKey
          *          Windows virtual key. Must be one of WindowsKey.VK_ALT, WindowsKey.VK_CONTROL
          *          or WindowsKey.VK_SHIFT.
          *
          * @return  see above.
          */
         public static integer getAsyncKeyState(integer winVKey)
         {
            return getKeyState(winVKey);
         }

         /**
          * Returns the id of the widget in focus.
          *
          * @return  see above.
          */
         public static integer getFocus()
         {
            handle h = LogicalTerminal.focus();
            if (h.isUnknown())
            {
               return new integer(NULL);
            }

            GenericWidget w = (GenericWidget) h.getResource();
            return w.getHWND();
         }

         /**
          * Emulates <code>PostMessageA</code> Win32 function by posting an event to the inner FWD OS event
          * queue.
          *
          * @param    hWnd
          *           Integer handle value of the window containing the widget (same as widget ID).
          * @param    msg
          *           Message to be posted.
          * @param    wParam
          *           Additional message-specific information.
          * @param    lParam
          *           Additional message-specific information.
          */
         public static void postMessageA(integer hWnd, integer msg, integer wParam, integer lParam)
         {
            ClientExports ce = LogicalTerminal.getClient();
            if (hWnd.isUnknown() || msg.isUnknown() || wParam.isUnknown() || lParam.isUnknown())
            {
               NativeInvoker.genInputUnknownErr(true);
               return;
            }

            ce.applyMessageA(hWnd.intValue(), msg.intValue(), wParam.intValue(), lParam.intValue(), false);
         }

         /**
          * Sets focus to the requested widget hwnd. The method interprets the hwnd value as widget id.
          *
          * @param   hwnd
          *          Widget id.
          */
         public static void setFocus(integer hwnd)
         {
            if (hwnd == null || hwnd.isUnknown())
            {
               return;
            }

            int id = hwnd.intValue();
            WindowWidget wnd = LogicalTerminal.getWindowByNonClientId(id);
            if (wnd != null)
            {
               id = wnd.getId();
            }

            // try to identify if the requested widget ID is already in active window
            // activate by default
            boolean forceActivation = true;
            GenericWidget<?> widget = LogicalTerminal.getWidgetForId(id);
            if (widget != null)
            {
               handle hTarget = widget.getWindow();
               if (hTarget != null && !hTarget.isUnknown())
               {
                  CommonWindow winTarget = hTarget.unwrapWindow();
                  handle hActive = LogicalTerminal.activeWindow();
                  if (hActive != null && !hActive.isUnknown())
                  {
                     CommonWindow winActive = hActive.unwrapWindow();
                     // do not activate target window if it is already active
                     // in this case we should preserve existed Z-order of the windows
                     forceActivation = winTarget != winActive;
                  }
               }
               else
               {
                  forceActivation = false;
               }
            }
            else
            {
               // nothing to be focused
               return;
            }

            ClientExports ce = LogicalTerminal.getClient();
            ce.setFocus(id, forceActivation);
         }
         
         /**
          * Returns parent widget id of the supplied widget native handle. The method interprets
          * the hwnd value as widget id.
          *
          * @param   hwnd
          *          Widget id.
          * @return  see above.
          */
         public static integer getParent(integer hwnd)
         {
            if (hwnd == null || hwnd.isUnknown())
            {
               return new integer(NULL);
            }
            
            GenericWidget w = LogicalTerminal.getWidgetForId(hwnd.intValue());
            if (w == null)
            {
               return new integer(NULL);
            }

            if (w instanceof WindowWidget)
            {
               // this is for the case when window:hwnd is passed to GetParent;
               // window:hwnd will represent the Win32 client window of the legacy top-level legacy window
               // and the result of GetParent will be the actual top-level window, i.e. the Win32 non-client
               // window of the top-level legacy window;
               return new integer(((WindowConfig) w.config()).nonClientWindowId);
            }
            
            CommonWidget parent = w.getParent();
            
            // for field group we need to find closest parent frame
            if (parent instanceof FieldGroup)
            {
               parent = parent.getParent();
            }
            
            return parent == null ? hwnd : parent.getHWND();
         }

         /**
          * Changes the parent window of the specified child window.
          *
          * @param   hWndChild
          *          A handle to the child window.
          * @param   hWndNewParent
          *          A handle to the new parent window.
          *
          * @return  If the function succeeds, the return value is a handle to the previous parent window.
          *          If the function fails, the return value is NULL.
          */
         public static integer setParent(integer hWndChild, integer hWndNewParent)
         {
            if (!BaseDataType.isAllKnown(hWndChild, hWndNewParent))
            {
               return new integer(NULL);
            }

            // user32 SetParent seems to always ignore same handle values
            if (hWndChild.longValue() == hWndNewParent.longValue())
            {
               return new integer(NULL);
            }

            GenericWidget parent = LogicalTerminal.getWidgetForId(hWndNewParent.intValue());
            if (!(parent instanceof FrameWidget))
            {
               UnimplementedFeature.partial("Only frame parents are supported for SetParent");
               return new integer(NULL);
            }

            int childId = hWndChild.intValue();
            GenericWidget child = LogicalTerminal.getWidgetForId(childId);

            // try to search in non-client window registry next if child is a window widget
            if (child == null)
            {
               child = LogicalTerminal.getWindowByNonClientId(childId);
            }

            if (child == null || !(child instanceof BaseEntity))
            {
               return new integer(NULL);
            }
            
            ((BaseEntity) child).setSystemParent(parent);

            GenericFrame oldParent = child.getFrame();
            return oldParent != null ? new integer(oldParent.getFrameWidget().getId()) : new integer(NULL);
         }
         
         /**
          * Loads the specified cursor resource.
          *
          * @param   instance
          *          A handle to an instance of the module whose executable file contains the cursor to be
          *          loaded.
          *
          * @param   cursorName
          *          The name of the cursor resource to be loaded. Alternatively, this parameter can
          *          consist of the resource identifier in the low-order word and zero in the high-order word.
          *
          * @return  If the function succeeds, the return value is the handle to the newly loaded cursor. If
          *          the function fails, the return value is NULL. To get extended error information, call
          *          GetLastError.
          */
         public static integer loadCursorA(integer instance, integer cursorName)
         {
            return cursorName != null && !cursorName.isUnknown() &&
                   CURSORS.containsKey(cursorName) ? cursorName : new integer(NULL);
         }

         /**
          * Retrieves a handle to the current cursor.
          *
          * @return  The return value is the handle to the current cursor. If there is no cursor, the
          *          return value is NULL.
          */
         public static integer getCursor()
         {
            return LogicalTerminal.getCursor();
         }

         /**
          * Sets the cursor shape.
          *
          * @param   cursor
          *          A handle to the cursor. The cursor must have been created by the CreateCursor function
          *          or loaded by the LoadCursor or LoadImage function. If this parameter is NULL, the
          *          cursor is removed from the screen.
          *
          * @return  The return value is the handle to the previous cursor, if there was one. If there was
          *          no previous cursor, the return value is NULL.
          */
         public static integer setCursor(integer cursor)
         {
            // save previous cursor
            integer prevCursor = new integer(getCursor().intValue());
            // for valid incoming cursor change the shape
            if (cursor != null && !cursor.isUnknown())
            {
               // call the client side to change the cursor
               LogicalTerminal.setCursor(cursor, CURSORS.get(cursor));
            }
            
            return prevCursor;
         }

         /**
          * Retrieves the dimensions of the bounding rectangle of the specified window. The dimensions are
          * given in screen coordinates that are relative to the upper-left corner of the screen.
          *
          * @param    hwnd
          *           A handle to the window.
          * @param    lprect
          *           A pointer to a RECT structure that receives the screen coordinates of the upper-left
          *           and lower-right corners of the window.
          *
          * @return   If the function succeeds, the return value is nonzero. If the function fails, the
          *           return value is zero.
          */
         public static integer getWindowRect(integer hwnd, int64 lprect)
         {
            if (!BaseDataType.isAllKnown(hwnd, lprect))
            {
               return new integer(0);
            }

            GenericWidget w = getWidget(hwnd.intValue());
            if (w == null)
            {
               return new integer(0);
            }

            int x;
            int y;
            int width;
            int height;

            if (w instanceof FrameWidget && ((FrameConfig) w.config()).dialog)
            {
               FrameConfig fc = (FrameConfig) w.config();
               x = fc.dialogX;
               y = fc.dialogY;
               width = w.getWidthPixels().intValue();
               height = w.getHeightPixels().intValue();
            }
            else
            {
               handle wnd = w.getWindow();
               if (!BaseDataType.isAllKnown(wnd))
               {
                  return new integer(0);
               }

               WindowWidget window = (WindowWidget) wnd.getResource();
               x = window.getX().intValue();
               y = window.getY().intValue();
               width = window.getWindowWidthPixels().intValue();
               height = window.getWindowHeightPixels().intValue();
            }

            LowLevelBuffer mem = memptr.getMemoryManager();
            byte[] data = mem.read(lprect.longValue(), 0, 16);
            if (data == null)
            {
               return new integer(0);
            }

            IntBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
            buf.put(0, x);
            buf.put(1, y);
            buf.put(2, x + width);
            buf.put(3, y + height);
            mem.write(lprect.longValue(), 0, data);

            return new integer(1);
         }

         /**
          * Retrieves the coordinates of a window's client area. The client coordinates specify the
          * upper-left and lower-right corners of the client area. Because client coordinates are relative
          * to the upper-left corner of a window's client area, the coordinates of the upper-left corner
          * are (0,0).
          *
          * @param    hwnd
          *           A handle to the window.
          * @param    lprect
          *           A pointer to a RECT structure that receives the screen coordinates of the upper-left
          *           and lower-right corners of the window.
          *
          * @return   If the function succeeds, the return value is nonzero. If the function fails, the
          *           return value is zero.
          */
         public static integer getClientRect(integer hwnd, int64 lprect)
         {
            if (!BaseDataType.isAllKnown(hwnd, lprect))
            {
               return new integer(0);
            }

            GenericWidget w = getWidget(hwnd.intValue());
            if (w == null)
            {
               return new integer(0);
            }

            LowLevelBuffer mem = memptr.getMemoryManager();
            byte[] data = mem.read(lprect.longValue(), 0, 16);
            if (data == null)
            {
               return new integer(0);
            }

            int width;
            int height;

            if (w instanceof FrameWidget && ((FrameConfig) w.config()).dialog)
            {
               width = w.getWidthPixels().intValue();
               height = w.getHeightPixels().intValue();
            }
            else
            {
               handle wnd = w.getWindow();
               if (!BaseDataType.isAllKnown(wnd))
               {
                  return new integer(0);
               }

               WindowWidget window = (WindowWidget) wnd.getResource();
               width = window.getWidthPixels().intValue();
               height = window.getHeightPixels().intValue();
            }

            IntBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
            buf.put(0, 0);
            buf.put(1, 0);
            buf.put(2, width);
            buf.put(3, height);
            mem.write(lprect.longValue(), 0, data);

            return new integer(1);
         }

         /**
          * Implements a substitute functionality for Win32 SetCursorPos. Sets a temporal variable for the
          * new mouse position coordinates, as it can not physically move the cursor, position which is 
          * later invalidated.
          * 
          * @param   xPos
          *          X coordinate.
          * @param   yPos
          *          Y coordinate.
          *
          * @return  Returns nonzero if successful or zero otherwise.        
          */
         public static integer setCursorPos(integer xPos, integer yPos)
         {
            UnimplementedFeature.partial("setCursorPos is not implemented to physically move the cursor.");
            LogicalTerminal.getClient().setCursorPos(new NativePoint(xPos.intValue(), yPos.intValue()));
            return new integer(1);
         }
         
         /**
          * Retrieves the position of the mouse cursor, in screen coordinates.
          *
          * @param   lpPoint
          *          A pointer to a POINT structure that receives the screen coordinates of the cursor.
          *
          * @return  Returns nonzero if successful or zero otherwise.
          */
         public static integer getCursorPos(memptr lpPoint)
         {
            return getCursorPos(lpPoint.getPointerValue());
         }

         /**
          * Retrieves the position of the mouse cursor, in screen coordinates.
          *
          * @param   lpPoint
          *          A pointer to a POINT structure that receives the screen coordinates of the cursor.
          *
          * @return  Returns nonzero if successful or zero otherwise.
          */
         public static integer getCursorPos(int64 lpPoint)
         {
            if (!BaseDataType.isAllKnown(lpPoint))
            {
               return new integer(0);
            }

            LowLevelBuffer mem = memptr.getMemoryManager();
            byte[] data = mem.read(lpPoint.longValue(), 0, 16);
            if (data == null)
            {
               return new integer(0);
            }

            NativePoint pos = LogicalTerminal.getClient().getMousePosition();

            IntBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
            buf.put(0, pos.x);
            buf.put(1, pos.y);
            mem.write(lpPoint.longValue(), 0, data);

            return new integer(1);
         }

         /**
          * The ClientToScreen function converts the client-area coordinates of a specified point 
          * to screen coordinates.
          *
          * @param   hWnd
          *          A handle to the window whose client area will be used for the conversion.
          * @param   lpPoint
          *          A pointer to a POINT structure that specifies the client coordinates to be 
          *          converted.
          *
          * @return If the function succeeds, the return value is nonzero. If the function fails, 
          *         the return value is zero.
          */
         public static integer clientToScreen(integer hWnd, memptr lpPoint)
         {
            return clientToScreen(hWnd, lpPoint.getPointerValue());
         }

         /**
          * The ClientToScreen function converts the client-area coordinates of a specified point 
          * to screen coordinates.
          *
          * @param   hWnd
          *          A handle to the window whose client area will be used for the conversion.
          * @param   lpPoint
          *          A pointer to a POINT structure that specifies the client coordinates to be 
          *          converted.
          *
          * @return If the function succeeds, the return value is nonzero. If the function fails, 
          *         the return value is zero.
          */
         public static integer clientToScreen(integer hWnd, int64 lpPoint)
         {
            if (!BaseDataType.isAllKnown(hWnd, lpPoint))
            {
               return new integer(0);
            }

            CommonWidget w = getWidget(hWnd.intValue());
            if (w == null)
            {
               return new integer(0);
            }

            LowLevelBuffer mem = memptr.getMemoryManager();
            byte[] data = mem.read(lpPoint.longValue(), 0, 16);
            if (data == null)
            {
               return new integer(0);
            }

            IntBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
            NativePoint loc = new NativePoint(buf.get(), buf.get());
            NativePoint zero = new NativePoint(0, 0);
            zero = LogicalTerminal.getClient().screenToClient(w.getId(), zero);
            // the zero point coordinates are always negative here, need to invert
            loc = loc.translate(-zero.x, -zero.y);
            buf.put(0, loc.x);
            buf.put(1, loc.y);
            mem.write(lpPoint.longValue(), 0, data);

            return new integer(1);
         }
         /**
          * The ScreenToClient function converts the screen coordinates of a specified point on the screen
          * to client-area coordinates.
          *
          * @param   hWnd
          *          A handle to the window whose client area will be used for the conversion.
          * @param   lpPoint
          *          A pointer to a POINT structure that specifies the screen coordinates to be converted.
          *
          * @return If the function succeeds, the return value is nonzero. If the function fails, the
          *         return value is zero.
          */
         public static integer screenToClient(integer hWnd, memptr lpPoint)
         {
            return screenToClient(hWnd, lpPoint.getPointerValue());
         }

         /**
          * The ScreenToClient function converts the screen coordinates of a specified point on the screen
          * to client-area coordinates.
          *
          * @param   hWnd
          *          A handle to the window whose client area will be used for the conversion.
          * @param   lpPoint
          *          A pointer to a POINT structure that specifies the screen coordinates to be converted.
          *
          * @return If the function succeeds, the return value is nonzero. If the function fails, the
          *         return value is zero.
          */
         public static integer screenToClient(integer hWnd, int64 lpPoint)
         {
            if (!BaseDataType.isAllKnown(hWnd, lpPoint))
            {
               return new integer(0);
            }

            CommonWidget w = getWidget(hWnd.intValue());
            if (w == null)
            {
               return new integer(0);
            }

            LowLevelBuffer mem = memptr.getMemoryManager();
            byte[] data = mem.read(lpPoint.longValue(), 0, 16);
            if (data == null)
            {
               return new integer(0);
            }

            IntBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
            NativePoint loc = new NativePoint(buf.get(), buf.get());
            loc = LogicalTerminal.getClient().screenToClient(w.getId(), loc);
            buf.put(0, loc.x);
            buf.put(1, loc.y);
            mem.write(lpPoint.longValue(), 0, data);

            return new integer(1);
         }

         /**
          * The ShowScrollBar function shows or hides the specified scroll bar.
          *
          * @param   hWnd
          *          Handle to a scroll bar control or a window with a standard scroll bar, depending on
          *          the value of the wBar parameter.
          * @param   wBar
          *          Specifies the scroll bar(s) to be shown or hidden.
          * @param   bShow
          *
          * @return If the function succeeds, the return value is nonzero. If the function fails,
          *         the return value is zero.
          */
         public static integer showScrollBar(integer hWnd, integer wBar, integer bShow)
         {
            if (!BaseDataType.isAllKnown(hWnd, wBar, bShow))
            {
               return new integer(0);
            }

            GenericWidget w = getWidget(hWnd.intValue());
            if (!(w instanceof FrameWidget))
            {
               return new integer(0);
            }

            FrameConfig c = ((FrameWidget) w).config();
            c.scrollbarFlags |= 1;
            byte bitsToSet = 0;
            switch (wBar.intValue())
            {
               // SB_HORZ
               case 0:
                  bitsToSet |= 2;
                  break;
               // SB_VERT
               case 1:
                  bitsToSet |= 4;
                  break;
               // SB_BOTH
               case 3:
                  bitsToSet |= (2 | 4);
                  break;
               default:
                  throw new RuntimeException("Unsupported wBar value given to ShowScrollBar [" +
                                             wBar.intValue() + "]");
            }

            if (bShow.intValue() == 0)
            {
               // FALSE
               c.scrollbarFlags &= ~bitsToSet;
            }
            else
            {
               // TRUE
               c.scrollbarFlags |= bitsToSet;
            }

            w.pushWidgetAttr("scrollbarFlags", c.scrollbarFlags);
            return new integer(1);
         }

         /**
          * Retrieves the window handle to the active window attached to the calling thread's message queue.
          *
          * @return  The return value is the handle to the active window attached to the calling thread's
          *          message queue. Otherwise, the return value is NULL.
          */
         public static integer getActiveWindow()
         {
            handle win = LogicalTerminal.activeWindow();
            if (!win.isUnknown())
            {
               int winId = ((GenericWidget) win.getResource()).getId();
               // this is special case when getActiveWindow() is calling in intermediate state
               // during activation status is on the way to moving from one window to another 
               if (WindowManager.getActiveDriverWindow() == null &&
                   WindowManager.getCurrentWindow() == null &&
                   WindowManager.getFocusWindow() == null)
               {
                  handle winCurr = LogicalTerminal.currentWindow();
                  if (!winCurr.isUnknown())
                  {
                     winId = ((GenericWidget) winCurr.getResource()).getId();
                  }
               }
               return new integer(winId);
            }
            else
            {
               return new integer(NULL);
            }
         }

         /**
          * Retrieves the window handle to the foreground window attached to the calling thread's
          * message queue.
          *
          * @return  The return value is the handle to the foreground window attached to the
          *          calling thread's message queue. Otherwise, the return value is NULL.
          */
         public static integer getForegroundWindow()
         {
            TopLevelWindow<?> tlw = WindowManager.getActiveDriverWindow();
            if (tlw != null)
            {
               return new integer(tlw.getId().asInt());
            }
            else
            {
               // use current window as fallback for foreground window calculation
               handle winCurr = LogicalTerminal.currentWindow();
               if (!winCurr.isUnknown())
               {
                  return new integer(((GenericWidget) winCurr.getResource()).getId());
               }
               else
               {
                  return new integer(NULL);
               }
            }
         }

       /**
          * Retrieves the specified system metric or system configuration setting.
          *
          * @param   nIndex
          *          The system metric or configuration setting to be retrieved.
          *
          * @return  If the function succeeds, the return value is the requested system metric or
          *          configuration setting. If the function fails, the return value is 0.
          */
         public static integer getSystemMetrics(integer nIndex)
         {
            if (nIndex.isUnknown())
            {
               return new integer(0);
            }

            switch (nIndex.intValue())
            {
               // SM_CXVSCROLL
               case 2:
                  return new integer(LogicalTerminal.getClientMetrics().scrollbarWidth);
               // SM_XVIRTUALSCREEN
               // SM_YVIRTUALSCREEN
               case 76:
               case 77:
                  // assume top-left origin of virtual screen is at 0,0
                  return new integer(0);
               // SM_CXVIRTUALSCREEN
               case 78:
                  ClientMetrics cm = LogicalTerminal.getClientMetrics();
                  return new integer(cm.virtualScreenSize.width);
               // SM_CYVIRTUALSCREEN
               case 79:
                  cm = LogicalTerminal.getClientMetrics();
                  return new integer(cm.virtualScreenSize.height);

               default:
                  LOG.severe("Unsupported client metrics index [" + nIndex.intValue() + "]");
                  return new integer(0);
            }
         }
         
         /**
          * Gets the current scrolling position for given scrollbar type.
          *
          * @param    hwnd
          *           A handle of the window to check.  This is a widget ID in terms of FWD.
          * @param    nBar
          *           Type of the scrollbar to check, horizontal (SB_HORZ or 0) or vertical (SB_VERT or 1).
          *
          * @return   The value of the current scroll position.
          */
         public static integer getScrollPos(integer hwnd, integer nBar)
         {
            if (!BaseDataType.isAllKnown(hwnd, nBar))
            {
               // no-op
               LOG.fine("Invalid input parameters for win32 native GetScrollPos() call.");
               return new integer();
            }
            
            GenericWidget w = LogicalTerminal.getWidgetForId(hwnd.intValue());
            boolean isHorizontal = nBar.intValue() == SB_HORZ;

            return w instanceof PaneEntity ? isHorizontal
                                              ? ((PaneEntity)w).getHScrollPosition()
                                              : ((PaneEntity)w).getVScrollPosition()
                                           : new integer(0);
         }
         
         /**
          * Implements the replacement functionality for native Windows messages.
          *
          * @param    hwnd
          *           A handle of the window as a source of the message.  This is a widget ID in terms of FWD.
          * @param    uMsg
          *           The message identifier.
          * @param    wParam
          *           The first parameter of the native Windows message.
          * @param    lParam
          *           The second parameter of the native Windows message.
          */
         public static void sendMessageA(integer hwnd, 
                                         integer uMsg,
                                         integer wParam,
                                         integer lParam)
         {
            if (!BaseDataType.isAllKnown(hwnd, uMsg, wParam, lParam))
            {
               LOG.fine("Invalid input parameters for win32 native SendMessageA() call.");
               return;
            }

            GenericWidget w = LogicalTerminal.getWidgetForId(hwnd.intValue());
            int msgType = uMsg.intValue();
            
            switch (msgType)
            {
               case WM_HSCROLL:
               case WM_VSCROLL:
               {
                  if (w instanceof PaneEntity)
                  {
                     // extract scroll values from message options
                     int fullValue = wParam.intValue();
                     int loValue = fullValue & LO_WORD_MASK;
                     int hiValue = (fullValue & HI_WORD_MASK) >> 16;
                     
                     if (loValue == SB_THUMBPOSITION)
                     {
                        if (msgType == WM_HSCROLL)
                        {
                           ((PaneEntity)w).setHScrollPosition(new integer(hiValue));
                        }
                        else
                        {
                           ((PaneEntity)w).setVScrollPosition(new integer(hiValue));
                        }
                     }
                     else
                     {
                        UnimplementedFeature.unsupported("Scroll type [" + loValue +
                                 "] for native win32 " +
                                 (msgType == WM_HSCROLL ? "WM_HSCROLL" : "WM_VSCROLL" ) + " message.");
                     }
                  }
                  else
                  {
                     UnimplementedFeature.unsupported("Target type [" + w.type() +
                              "] for native win32" +
                              (msgType == WM_HSCROLL ? "WM_HSCROLL" : "WM_VSCROLL" ) + " message.");
                  }
                  break;
               }
               case MsgBlasterPeer.WM_RBUTTONDOWN:
               case MsgBlasterPeer.WM_RBUTTONUP:
               {
                  ClientExports ce = LogicalTerminal.getClient();
                  if (hwnd.isUnknown() || uMsg.isUnknown() || wParam.isUnknown() || lParam.isUnknown())
                  {
                     NativeInvoker.genInputUnknownErr(true);
                     return;
                  }

                  ce.applyMessageA(hwnd.intValue(), uMsg.intValue(), wParam.intValue(), lParam.intValue(), true);
                  break;
               }
               default:
                  UnimplementedFeature.unsupported("Native win32 message type: [" + uMsg.intValue() + "]");
            }
            
            return;
         }
         
         /**
          * Checks if the window for the given handle is already realized.
          *
          * @param    hwnd
          *           A handle of the window to check.
          *
          * @return   If the function succeeds, the return value is nonzero. If the function fails, the
          *           return value is zero.
          */
         public static integer isWindow(integer hwnd)
         {
            if (hwnd == null || hwnd.isUnknown())
            {
               // window does not exist
               return new integer(FALSE);
            }
            else
            {
               GenericWidget w = getWidget(hwnd.intValue());
               if (w == null)
               {
                  return new integer(FALSE);
               }
               else
               {
                  return new integer(w._isRealized() ? TRUE : FALSE);
               }
            }
         }
         
         /**
          * Requests window lock for the given window handle.
          *
          * @param    hwnd
          *           A handle to the window to lock.
          *
          * @return   If the function succeeds, the return value is nonzero. If the function fails, the
          *           return value is zero.
          */
         public static integer lockWindowUpdate(integer hwnd)
         {
            if (hwnd == null || hwnd.isUnknown() || hwnd.intValue() == 0)
            {
               if (wndLocked != null)
               {
                  // unlock previously locked window
                  wndLocked.setDisableRedraw(false);
                  wndLocked = null;
               }
               else
               {
                  // nothing to unlock, operation failed
                  return new integer(FALSE);
               }
            }
            else
            {
               GenericWidget w = getWidget(hwnd.intValue());
               if (w == null)
               {
                  return new integer(FALSE);
               }
               
               // lock the given window
               if (wndLocked == null)
               {
                  w.setDisableRedraw(true);
                  wndLocked = w; 
               }
               else
               {
                  // some window already locked, only single is possible
                  return new integer(FALSE);
               }
            }
            
            return new integer(TRUE);
         }
         
         /**
          * Should disable the ability to minimize, move, or close the main window of an
          * application that is not responding.
          */
         public static void disableProcessWindowsGhosting()
         {
            // Nothing to do in FWD at this time, may be modalize current window
         }

         /**
          * Checks if the window for the given handle is visible or not.
          *
          * @param    hwnd
          *           A handle of the window to check.
          *
          * @return   If the window is visible the return value is <code>1</code>. Otherwise it
          *           is <code>0</code>.
          */
         public static integer isWindowVisible(integer hwnd)
         {
            if (hwnd == null || hwnd.isUnknown())
            {
               // window does not exist
               return new integer(FALSE);
            }
            else
            {
               GenericWidget w = getWidget(hwnd.intValue());
               if (w == null)
               {
                  return new integer(FALSE);
               }
               else
               {
                  return new integer(w._isVisible() ? TRUE : FALSE);
               }
            }
         }
         
         /**
          * Retrieves the current color of the specified display element.
          *
          * @param    nIndex
          *           The display element whose color is to be retrieved.
          *
          * @return   RGB value of the requested display element
          */
         public static integer getSysColor(integer nIndex)
         {
            if (nIndex.isUnknown())
            {
               return new integer(0);
            }

            Map<Integer, ColorRgb> palette = LogicalTerminal.getThemeSystemPalette();
            ColorRgb clr = null;
            
            switch (nIndex.intValue())
            {
               // COLOR_3DFACE
               case 15:
                  clr = palette.get(SysColor.COLOR_3DFACE);
                  break;
               // COLOR_3DSHADOW
               case 16:
                  clr = palette.get(SysColor.COLOR_3DSHADOW);
                  break;
               // COLOR_3DHIGHLIGHT (20 matches with COLOR_BTNHIGHLIGHT)
               case 20:
                  clr = palette.get(SysColor.COLOR_BTNHIGHLIGHT);
                  break;

               default:
                  LOG.severe("Unsupported display element index [" + nIndex.intValue() + "]");
            }
            return new integer((clr != null) ? clr.getRgb() : 0);
         }
         
         /**
          * Moves the window to the specified position.
          *
          * @param    hWnd
          *           A handle of the window to move.
          * @param    x
          *           A new X position.
          * @param    y
          *           A new Y position.
          * @param    w
          *           A new window width.
          * @param    h
          *           A new window height.
          * @param    bRepaint
          *           Flag indicating if we need to repaint window after move.
          *
          * @return   <code>1</code> if success, <code>0</code> otherwise.
          */
         public static integer moveWindow(integer hWnd, integer x, integer y, integer w, integer h,
                                          integer bRepaint)
         {
            if (!BaseDataType.isAllKnown(hWnd, x, y, w, h, bRepaint))
            {
               return new integer(FALSE);
            }

            GenericWidget wndWid = getWidget(hWnd.intValue());
            if (wndWid == null)
            {
               return new integer(FALSE);
            }
            
            if (w.intValue() <= 0 || h.intValue() <= 0)
            {
               // implicit window hide
               wndWid.hide();
            }
            else
            {
               // TODO: The calls below are not optimal because generates 4 server-client calls with repaint
               //       on each. However making single call requires a lot of rework in BaseEntity class or
               //       copy-paste many lines from there which is not the best as well. Considering this is
               //       not a frequently used method it should not be a pefrormance bottleneck for.
               //       application. But we need to keep this in mind for case when it could becom an issue
               //       to get back and provide better solution.
               wndWid.setX(x);
               wndWid.setY(y);
               wndWid.setWidthPixels(w);
               wndWid.setHeightPixels(h);
            }
            
            // the moved window will be repainted automatically so we can ignore bRepaint option now
            
            return new integer(TRUE);
         }

         /**
          * Resolves a widget based on the supplied hwnd value.
          *
          * @param   hwnd
          *          A hwnd value.
          *
          * @return  the widget or {@code null}.
          */
         private static GenericWidget getWidget(int hwnd)
         {
            GenericWidget w = LogicalTerminal.getWidgetForId(hwnd);
            if (w == null)
            {
               w = LogicalTerminal.getWindowByNonClientId(hwnd);
            }

            return w;
         }
      }

      /**
       * Declares supported kernel32.dll functions.
       */
      public static class kernel32
      {
         /** Error number read by {@link #getLastError} */
         private static int errorno = 0;

         /**
          * Retrieves the calling thread's last-error code value.
          *
          * @return  The return value is the calling thread's last-error code.
          */
         public static integer getLastError()
         {
            return new integer(errorno);
         }

         /**
          * Returns the language identifier for the user UI language for the current user.
          *
          * @return  Returns the language identifier for the user UI language for the current user.
          */
         public static integer getUserDefaultUILanguage()
         {
            // TODO: this will require a corresponding configuration entry in the directory
            return new integer(0);
         }

         /**
          * Retrieves a string from the specified section in an initialization file.
          *
          * @param   appName
          *          The name of the section containing the key name.
          * @param   keyName
          *          The name of the key whose associated string is to be retrieved.
          * @param   defaultValue
          *          A default string.
          * @param   returnedString
          *          The retrieved string.
          * @param   size
          *          The size of the buffer pointed to by the returnedString parameter, in characters.
          * @param   fileName
          *          The name of the initialization file.
          *
          * @return  The return value is the number of characters copied to the buffer, not including the
          *          terminating null character.
          */
         public static integer getPrivateProfileStringA(character appName,
                                                        character keyName,
                                                        character defaultValue,
                                                        character returnedString,
                                                        integer   size,
                                                        character fileName)
         {
            errorno = 0;

            if (!BaseDataType.isAllKnown(appName, keyName, returnedString, size, fileName))
            {
               return new integer(0);
            }

            String javaFileName = fileName.toStringMessage().replace('\\', File.pathSeparatorChar);
            IniFile ini = new IniFile(javaFileName);
            try
            {
               ini.load();

               String def = defaultValue == null || defaultValue.isUnknown() ?
               null : defaultValue.toStringMessage();

               returnedString.assign(ini.getString(appName.toStringMessage(),
                                                   keyName.toStringMessage(),
                                                   def));
               return new integer(size.intValue() - 1);
            }
            catch (IOException e)
            {
               errorno = 2;
               return new integer(0);
            }
         }
         
         /**
          * Obtain the process-id (pid) of the current client process.  This returns the client JVM pid
          * (it does NOT provide the pid of the server JVM).
          *
          * @return    The current client process pid or -1 on any error.
          */
         public static integer getCurrentProcessId()
         {
            ClientParameters parms = StandardServer.getClientParameters();
            
            return new integer((parms == null) ? -1 : parms.pid);
         }

         /**
          * Retrieves information about a locale specified by identifier.
          *
          * @param    Locale
          *           Locale identifier for which to retrieve information.
          * @param    LCType
          *           The locale information to retrieve.
          * @param    lpLCData
          *           Pointer to a buffer in which this function retrieves the requested locale information.
          *           This pointer is not used if cchData is set to 0.
          * @param    cchData
          *           Size, in TCHAR values, of the data buffer indicated by lpLCData.
          *
          * @return   Returns the number of characters retrieved in the locale data buffer if successful
          *           and cchData is a nonzero value.
          */
         public static integer getLocaleInfoA(integer Locale,
                                              integer LCType,
                                              character lpLCData,
                                              integer cchData)
         {
            // TODO: put the returned locale info in directory
            switch (LCType.intValue())
            {
               case 12:
                  // LOCALE_SLIST - getting list items separator char
                  lpLCData.assign(",");
                  break;
               case 31:
                  // short date format
                  lpLCData.assign("yyyy/MM/dd/");
                  break;
               case 32:
                  // long date format
                  lpLCData.assign("dddd MMMM yyyy");
                  break;
               default:
                  LOG.severe("Unsupported locale info type " + LCType.toStringMessage() + " .");
            }

            return new integer(0);
         }
         
         /**
          * Sleep for the duration passed in.
          *
          * @param   millis
          *          Number of milliseconds to sleep.
          */
         public static void sleep(int64 millis)
         {
            long unsigned_value = millis.getValue() & 0xFFFFFFFF;
            try
            {
               Thread.sleep(unsigned_value);
            }
            catch (InterruptedException ie)
            {
               // TODO: It is not clear that os-sleep can be interrupted. With 4GL the ON STOP
               //       gets handled, but only *after* the sleep duration completes.
               //       This was tested with testcase uast/windows_emulation/kernel32_sleep.p
               //       Note also that on Windows, Control+Break is received as a STOP, but
               //       Control+C is handled differently. The testcase jumps to "Done." and exits.
               //       Handling of the InterruptedException may still be required, if we can see
               //       the behavior when signalling the kernel32:sleep directly.
            }
         }
         
         /**
          * Retrieves the name of the computer associated with the current thread.
          *
          * @param   lpBuffer
          *          A pointer to the buffer to receive the user's computer name.
          * @param   pcbBuffer
          *          On input, this variable specifies the size of the lpBuffer buffer, in TCHARs. On
          *          output, the variable receives the number of TCHARs copied to the buffer, including
          *          the terminating null character.
          *
          * @return If the function succeeds, the return value is a nonzero value, and the variable pointed
          *         to by lpnSize contains the number of TCHARs copied to the buffer specified by lpBuffer,
          *         including the terminating null character.
          *         If the function fails, the return value is zero. To get extended error information,
          *         call GetLastError.
          */
         public static int getComputerNameA(character lpBuffer, integer pcbBuffer)
         {
            int res = FALSE;
            errorno = 0; // ERROR_BUFFER_OVERFLOW (-2147024785 or 0x8007006F) if name is too long.            
            String currentHostName = LogicalTerminal.getHostName();
            if (currentHostName != null && !currentHostName.isEmpty())
            {
               lpBuffer.assign(currentHostName);
               pcbBuffer.assign(currentHostName.length() + 1);
               res = TRUE;
            }
            else
            {
               lpBuffer.assign(character.EMPTY_STRING);
               pcbBuffer.assign(0);
            }
            // TODO: It may not be entirely accurate to just return the IP address, as 
            //       LogicalTerminal.getHostName() does. However, the complexities of 
            //       whether the client interface is a browser, or swing, and where to actually
            //       resolve the name are being left to an actual requirement specification.
            //       Implementation will ultimately be left to the client-side terminals.
            return res;
         }         
      }

      /**
       * Declares supported advapi32.dll functions.
       */
      public static class advapi32
      {
         /**
          * Retrieves the name of the user associated with the current thread.
          *
          * @param   lpBuffer
          *          A pointer to the buffer to receive the user's logon name.
          * @param   pcbBuffer
          *          On input, this variable specifies the size of the lpBuffer buffer, in TCHARs. On
          *          output, the variable receives the number of TCHARs copied to the buffer, including
          *          the terminating null character.
          *
          * @return If the function succeeds, the return value is a nonzero value, and the variable pointed
          *         to by lpnSize contains the number of TCHARs copied to the buffer specified by lpBuffer,
          *         including the terminating null character.
          *         If the function fails, the return value is zero. To get extended error information,
          *         call GetLastError.
          */
         public static int getUserNameA(character lpBuffer, integer pcbBuffer)
         {
            int res = FALSE;
            String currentUserName = LogicalTerminal.getUserName();
            if (currentUserName != null && !currentUserName.isEmpty())
            {
               lpBuffer.assign(currentUserName);
               pcbBuffer.assign(currentUserName.length() + 1);
               res = TRUE;
            }
            else
            {
               lpBuffer.assign(character.EMPTY_STRING);
               pcbBuffer.assign(0);
            }
            
            return res;
         }
      }

      /**
       * Declares supported wsock32.dll functions.
       */
      public static class wsock32
      {
         /**
          * Prepares the socket library to get inet info.
          *
          * @param   versionReq
          *          A pointer to the version buffer, not used.
          * @param   wsaDataPointer
          *          The pointer to input data buffer, not used.
          *
          * @return If the function succeeds, the return value is a nonzero value, if the function
          *         fails, the return value is zero. To get extended error information, call
          *         GetLastError.
          */
         public static int wSAStartup(integer versionReq, int64 wsaDataPointer)
         {
            // nothing to do here
            return TRUE;
         }

         /**
          * Finalizes the socket library to use.
          *
          * @return If the function succeeds, the return value is a nonzero value, if the function
          *         fails, the return value is zero. To get extended error information, call
          *         GetLastError.
          */
         public static int wSACleanup()
         {
            // nothing to do here
            return TRUE;
         }

         /**
          * Gets the name of the host the client is currently running.
          *
          * @param   hostName
          *          A pointer to the buffer to receive the client side hostname.
          * @param   nbLength
          *          On input, this variable specifies the size of the hostName buffer, in TCHARs.
          *          Currently the value is ignoring.
          *
          * @return If the function succeeds, the return value is a nonzero value, and the variable
          *         pointed to by nbLength contains the number of TCHARs copied to the buffer
          *         specified by hostName, including the terminating null character.
          *         If the function fails, the return value is zero. To get extended error
          *         information, call GetLastError.
          */
         public static int gethostname(character hostName, int64 nbLength)
         {
            int res = FALSE;
            
            // get the computer name from the client side
            String hName = LogicalTerminal.getHostName();
            if (hName != null && !hName.isEmpty())
            {
               res = TRUE;
               hostName.assign(new character(hName));
            }
            
            return res;
         }
         
         /**
          * Gets the host name of the host the client is currently running.
          *
          * @param   hostName
          *          A character based hostname.
          *
          * @return If the function succeeds, the return value is a memptr object containing
          *         hostent data structure with the host address. To get extended error
          *         information, call GetLastError.
          */
         public static int64 gethostbyname(character hostName)
         {
            return new int64(LogicalTerminal.getHostByName(hostName.getValue()));
         }
         
         /**
          * Gets the host name of the host the client is currently running.
          *
          * @param   addrInputStruct
          *          Structure containing digital representation of the Internet address.
          *
          * @return If the function succeeds, the return value is a memptr object containing
          *         IP address in format "0-255.0-255.0-255.0-255". To get extended error
          *         information, call GetLastError.
          */
         public static memptr inet_ntoa(int64 addrInputStruct)
         {
            // get the 32-bit integer address value
            int ipAddr = addrInputStruct.intValue();
            
            // convert big-endian integer address value to
            StringBuilder sb = new StringBuilder();
            sb.append((byte)((ipAddr & 0xFF000000) >> 24));
            sb.append(".");
            sb.append((byte)((ipAddr & 0x00FF0000) >> 16));
            sb.append(".");
            sb.append((byte)((ipAddr & 0x0000FF00) >> 8));
            sb.append(".");
            sb.append((byte)(ipAddr & 0x000000FF));
            
            // pack the String into memptr to return
            return new memptr(new character(sb.toString()));
         }
      }
   }

   /**
    * Removes all options from the supplied 4GL string literal. That is any options bayond the colon char
    * ':' will be removed together with the colon.
    *
    * @param   text
    *          4GL string literal.
    *
    * @return  text with removed 4GL string literal options.
    */
   private static String removeStringLiteralOptions(String text)
   {
      int i = text.indexOf(':');
      if (i >= 0)
      {
         return text.substring(0, i);
      }

      return text;
   }
}