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;
}
}