public interface Advapi32 extends StdCallLibrary
{
	Advapi32	INSTANCE	= (Advapi32) Native.loadLibrary("Advapi32", Advapi32.class, Options.UNICODE_OPTIONS);

	/*
	 * BOOL WINAPI LookupAccountName( LPCTSTR lpSystemName, LPCTSTR
	 * lpAccountName, PSID Sid, LPDWORD cbSid, LPTSTR ReferencedDomainName,
	 * LPDWORD cchReferencedDomainName, PSID_NAME_USE peUse );
	 */
	public boolean LookupAccountName(String lpSystemName, String lpAccountName, byte[] Sid, IntByReference cbSid, char[] ReferencedDomainName,
			IntByReference cchReferencedDomainName, PointerByReference peUse);

	/*
	 * BOOL WINAPI LookupAccountSid( LPCTSTR lpSystemName, PSID lpSid, LPTSTR
	 * lpName, LPDWORD cchName, LPTSTR lpReferencedDomainName, LPDWORD
	 * cchReferencedDomainName, PSID_NAME_USE peUse );
	 */
	public boolean LookupAccountSid(String lpSystemName, byte[] Sid, char[] lpName, IntByReference cchName, char[] ReferencedDomainName,
			IntByReference cchReferencedDomainName, PointerByReference peUse);

	/*
	 * BOOL ConvertSidToStringSid( PSID Sid, LPTSTR* StringSid );
	 */
	public boolean ConvertSidToStringSid(byte[] Sid, PointerByReference StringSid);

	/*
	 * BOOL WINAPI ConvertStringSidToSid( LPCTSTR StringSid, PSID* Sid );
	 */
	public boolean ConvertStringSidToSid(String StringSid, PointerByReference Sid);

	/*
	 * SC_HANDLE WINAPI OpenSCManager( LPCTSTR lpMachineName, LPCTSTR
	 * lpDatabaseName, DWORD dwDesiredAccess );
	 */
	public Pointer OpenSCManager(String lpMachineName, WString lpDatabaseName, int dwDesiredAccess);

	/*
	 * BOOL WINAPI CloseServiceHandle( SC_HANDLE hSCObject );
	 */
	public boolean CloseServiceHandle(Pointer hSCObject);

	/*
	 * SC_HANDLE WINAPI OpenService( SC_HANDLE hSCManager, LPCTSTR
	 * lpServiceName, DWORD dwDesiredAccess );
	 */
	public Pointer OpenService(Pointer hSCManager, String lpServiceName, int dwDesiredAccess);

	/*
	 * BOOL WINAPI StartService( SC_HANDLE hService, DWORD dwNumServiceArgs,
	 * LPCTSTR* lpServiceArgVectors );
	 */
	public boolean StartService(Pointer hService, int dwNumServiceArgs, char[] lpServiceArgVectors);

	/*
	 * BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl,
	 * LPSERVICE_STATUS lpServiceStatus );
	 */
	public boolean ControlService(Pointer hService, int dwControl, SERVICE_STATUS lpServiceStatus);

	/*
	 * BOOL WINAPI StartServiceCtrlDispatcher( const SERVICE_TABLE_ENTRY*
	 * lpServiceTable );
	 */
	public boolean StartServiceCtrlDispatcher(Structure[] lpServiceTable);

	/*
	 * SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandler( LPCTSTR
	 * lpServiceName, LPHANDLER_FUNCTION lpHandlerProc );
	 */
	public Pointer RegisterServiceCtrlHandler(String lpServiceName, Handler lpHandlerProc);

	/*
	 * SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerEx( LPCTSTR
	 * lpServiceName, LPHANDLER_FUNCTION_EX lpHandlerProc, LPVOID lpContext );
	 */
	public Pointer RegisterServiceCtrlHandlerEx(String lpServiceName, HandlerEx lpHandlerProc, Pointer lpContext);

	/*
	 * BOOL WINAPI SetServiceStatus( SERVICE_STATUS_HANDLE hServiceStatus,
	 * LPSERVICE_STATUS lpServiceStatus );
	 */
	public boolean SetServiceStatus(Pointer hServiceStatus, SERVICE_STATUS lpServiceStatus);

	/*
	 * SC_HANDLE WINAPI CreateService( SC_HANDLE hSCManager, LPCTSTR
	 * lpServiceName, LPCTSTR lpDisplayName, DWORD dwDesiredAccess, DWORD
	 * dwServiceType, DWORD dwStartType, DWORD dwErrorControl, LPCTSTR
	 * lpBinaryPathName, LPCTSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCTSTR
	 * lpDependencies, LPCTSTR lpServiceStartName, LPCTSTR lpPassword );
	 */
	public Pointer CreateService(Pointer hSCManager, String lpServiceName, String lpDisplayName, int dwDesiredAccess, int dwServiceType,
			int dwStartType, int dwErrorControl, String lpBinaryPathName, String lpLoadOrderGroup, IntByReference lpdwTagId, String lpDependencies,
			String lpServiceStartName, String lpPassword);

	/*
	 * BOOL WINAPI DeleteService( SC_HANDLE hService );
	 */
	public boolean DeleteService(Pointer hService);

	/*
	 * BOOL WINAPI ChangeServiceConfig2( SC_HANDLE hService, DWORD dwInfoLevel,
	 * LPVOID lpInfo );
	 */
	public boolean ChangeServiceConfig2(Pointer hService, int dwInfoLevel, ChangeServiceConfig2Info lpInfo);

	/*
	 * LONG WINAPI RegOpenKeyEx( HKEY hKey, LPCTSTR lpSubKey, DWORD ulOptions,
	 * REGSAM samDesired, PHKEY phkResult );
	 */
	public int RegOpenKeyEx(int hKey, String lpSubKey, int ulOptions, int samDesired, IntByReference phkResult);

	/*
	 * LONG WINAPI RegQueryValueEx( HKEY hKey, LPCTSTR lpValueName, LPDWORD
	 * lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData );
	 */
	public int RegQueryValueEx(int hKey, String lpValueName, IntByReference lpReserved, IntByReference lpType, byte[] lpData, IntByReference lpcbData);

	/*
	 * LONG WINAPI RegCloseKey( HKEY hKey );
	 */
	public int RegCloseKey(int hKey);

	/*
	 * LONG WINAPI RegDeleteValue( HKEY hKey, LPCTSTR lpValueName );
	 */
	public int RegDeleteValue(int hKey, String lpValueName);

	/*
	 * LONG WINAPI RegSetValueEx( HKEY hKey, LPCTSTR lpValueName, DWORD
	 * Reserved, DWORD dwType, const BYTE* lpData, DWORD cbData );
	 */
	public int RegSetValueEx(int hKey, String lpValueName, int Reserved, int dwType, byte[] lpData, int cbData);

	/*
	 * LONG WINAPI RegCreateKeyEx( HKEY hKey, LPCTSTR lpSubKey, DWORD Reserved,
	 * LPTSTR lpClass, DWORD dwOptions, REGSAM samDesired, LPSECURITY_ATTRIBUTES
	 * lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition );
	 */
	public int RegCreateKeyEx(int hKey, String lpSubKey, int Reserved, String lpClass, int dwOptions, int samDesired,
			WINBASE.SECURITY_ATTRIBUTES lpSecurityAttributes, IntByReference phkResult, IntByReference lpdwDisposition);

	/*
	 * LONG WINAPI RegDeleteKey( HKEY hKey, LPCTSTR lpSubKey );
	 */
	public int RegDeleteKey(int hKey, String name);

	/*
	 * LONG WINAPI RegEnumKeyEx( HKEY hKey, DWORD dwIndex, LPTSTR lpName,
	 * LPDWORD lpcName, LPDWORD lpReserved, LPTSTR lpClass, LPDWORD lpcClass,
	 * PFILETIME lpftLastWriteTime );
	 */
	public int RegEnumKeyEx(int hKey, int dwIndex, char[] lpName, IntByReference lpcName, IntByReference reserved, char[] lpClass,
			IntByReference lpcClass, WINBASE.FILETIME lpftLastWriteTime);

	/*
	 * LONG WINAPI RegEnumValue( HKEY hKey, DWORD dwIndex, LPTSTR lpValueName,
	 * LPDWORD lpcchValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE
	 * lpData, LPDWORD lpcbData );
	 */
	public int RegEnumValue(int hKey, int dwIndex, char[] lpValueName, IntByReference lpcchValueName, IntByReference reserved, IntByReference lpType,
			byte[] lpData, IntByReference lpcbData);

	interface SERVICE_MAIN_FUNCTION extends StdCallCallback
	{
		/*
		 * VOID WINAPI ServiceMain( DWORD dwArgc, LPTSTR* lpszArgv );
		 */
		public void callback(int dwArgc, Pointer lpszArgv);
	}

	interface Handler extends StdCallCallback
	{
		/*
		 * VOID WINAPI Handler( DWORD fdwControl );
		 */
		public void callback(int fdwControl);
	}

	interface HandlerEx extends StdCallCallback
	{
		/*
		 * DWORD WINAPI HandlerEx( DWORD dwControl, DWORD dwEventType, LPVOID
		 * lpEventData, LPVOID lpContext );
		 */
		public int callback(int dwControl, int dwEventType, Pointer lpEventData, Pointer lpContext);
	}

	/*
	 * typedef struct _SERVICE_STATUS { DWORD dwServiceType; DWORD
	 * dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD
	 * dwServiceSpecificExitCode; DWORD dwCheckPoint; DWORD dwWaitHint; }
	 * SERVICE_STATUS,LPSERVICE_STATUS;
	 */
	public static class SERVICE_STATUS extends Structure
	{
		public int	dwServiceType;
		public int	dwCurrentState;
		public int	dwControlsAccepted;
		public int	dwWin32ExitCode;
		public int	dwServiceSpecificExitCode;
		public int	dwCheckPoint;
		public int	dwWaitHint;
	}

	/*
	 * typedef struct _SERVICE_TABLE_ENTRY { LPTSTR lpServiceName;
	 * LPSERVICE_MAIN_FUNCTION lpServiceProc; } SERVICE_TABLE_ENTRY,
	 * LPSERVICE_TABLE_ENTRY;
	 */
	public static class SERVICE_TABLE_ENTRY extends Structure
	{
		public String					lpServiceName;
		public SERVICE_MAIN_FUNCTION	lpServiceProc;
	}

	public static class ChangeServiceConfig2Info extends Structure
	{
	}

	/*
	 * YAJSW additions start here
	 */

	/*
	 * BOOL WINAPI QueryServiceConfig( __in SC_HANDLE hService, __out_opt
	 * LPQUERY_SERVICE_CONFIG lpServiceConfig, __in DWORD cbBufSize, __out
	 * LPDWORD pcbBytesNeeded );
	 */
	boolean QueryServiceConfig(Pointer hService, Memory lpServiceConfig, int cbBufSize, IntByReference pcbBytesNeeded);

	/*
	 * BOOL WINAPI QueryServiceStatusEx( __in SC_HANDLE hService, __in
	 * SC_STATUS_TYPE InfoLevel, __out_opt LPBYTE lpBuffer, __in DWORD
	 * cbBufSize, __out LPDWORD pcbBytesNeeded );
	 */
	boolean QueryServiceStatusEx(Pointer hService, short InfoLevel, Memory lpBuffer, int cbBufSize, IntByReference pcbBytesNeeded);

	public static final int	SC_STATUS_PROCESS_INFO	= 0;

	/*
	 * BOOL WINAPI QueryServiceConfig2( __in SC_HANDLE hService, __in DWORD
	 * dwInfoLevel, __out_opt LPBYTE lpBuffer, __in DWORD cbBufSize, __out
	 * LPDWORD pcbBytesNeeded );
	 */
	boolean QueryServiceConfig2(Pointer hService, short InfoLevel, Memory lpBuffer, int cbBufSize, IntByReference pcbBytesNeeded);

	public static final int	SERVICE_CONFIG_DESCRIPTION	= 1;

	/*
	 * typedef struct _ENUM_SERVICE_STATUS_PROCESS { LPTSTR lpServiceName;
	 * LPTSTR lpDisplayName; SERVICE_STATUS_PROCESS ServiceStatusProcess; }
	 * ENUM_SERVICE_STATUS_PROCESS,LPENUM_SERVICE_STATUS_PROCESS;
	 */

	public static class ENUM_SERVICE_STATUS_PROCESS extends Structure
	{
		public Pointer					lpServiceName;
		public Pointer					lpDisplayName;
		public SERVICE_STATUS_PROCESS	ServiceStatusProcess;

		public void init(Pointer pointer)
		{
			useMemory(pointer);
			read();
		}

		public ENUM_SERVICE_STATUS_PROCESS next()
		{
			ENUM_SERVICE_STATUS_PROCESS next = new ENUM_SERVICE_STATUS_PROCESS();
			next.useMemory(getPointer(), size());
			next.read();
			return next;
		}

		public String getServiceName()
		{
			return lpServiceName.getString(0, true);
		}

		public String getDisplayName()
		{
			return lpDisplayName.getString(0, true);
		}

		public int getProcessId()
		{
			return ServiceStatusProcess.dwProcessId;
		}

		public int getCurrentState()
		{
			return ServiceStatusProcess.dwCurrentState;
		}

	}

	/*
	 * typedef struct _SERVICE_STATUS_PROCESS { DWORD dwServiceType; DWORD
	 * dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD
	 * dwServiceSpecificExitCode; DWORD dwCheckPoint; DWORD dwWaitHint; DWORD
	 * dwProcessId; DWORD dwServiceFlags; } SERVICE_STATUS_PROCESS,
	 * LPSERVICE_STATUS_PROCESS;
	 */

	public static class SERVICE_STATUS_PROCESS extends Structure
	{
		public void init(Pointer pointer)
		{
			useMemory(pointer);
			read();
		}

		public int	dwServiceType;
		public int	dwCurrentState;
		public int	dwControlsAccepted;
		public int	dwWin32ExitCode;
		public int	dwServiceSpecificExitCode;
		public int	dwCheckPoint;
		public int	dwWaitHint;
		public int	dwProcessId;
		public int	dwServiceFlags;

	}

	public static final int	SERVICE_RUNNING			= 0x00000004;
	public static final int	SERVICE_STOPPED			= 0x00000001;
	public static final int	SERVICE_PAUSED			= 0x00000007;
	public static final int	SERVICE_START_PENDING	= 0x00000002;
	public static final int	SERVICE_STOP_PENDING	= 0x00000003;

	/*
	 * typedef struct _QUERY_SERVICE_CONFIG { DWORD dwServiceType; DWORD
	 * dwStartType; DWORD dwErrorControl; LPTSTR lpBinaryPathName; LPTSTR
	 * lpLoadOrderGroup; DWORD dwTagId; LPTSTR lpDependencies; LPTSTR
	 * lpServiceStartName; LPTSTR lpDisplayName; } QUERY_SERVICE_CONFIG,
	 * LPQUERY_SERVICE_CONFIG;
	 */

	public static class QUERY_SERVICE_CONFIG extends Structure
	{
		public void init(Pointer pointer)
		{
			useMemory(pointer);
			read();
		}

		public int		dwServiceType;
		public int		dwStartType;
		public int		dwErrorControl;
		public String	lpBinaryPathName;
		public String	lpLoadOrderGroup;
		public int		dwTagId;
		public Pointer	lpDependencies;
		public String	lpServiceStartName;
		public String	lpDisplayName;

		public String[] getDependencies()
		{
			List<String> result = new ArrayList<String>();
			Pointer ptr = lpDependencies;
			int offset = 0;
			String s = "";
			if (ptr != null)
				do
				{
					s = ptr.getString(offset, true);
					if (s != null && !"".equals(s))
					{
						result.add(s);
						offset += s.getBytes().length * 2 + 2;
					}
				}
				while (s != null && !"".equals(s));
			return result.toArray(new String[0]);
		}

	}

	/*
	 * typedef struct _SERVICE_DESCRIPTION { LPTSTR lpDescription; }
	 * SERVICE_DESCRIPTION,LPSERVICE_DESCRIPTION;
	 */
	public static class SERVICE_DESCRIPTION extends ChangeServiceConfig2Info
	{
		public String	lpDescription;

		public void init(Pointer pointer)
		{
			useMemory(pointer);
			read();
		}
	}

	/*
	 * BOOL WINAPI EnumServicesStatusEx( __in SC_HANDLE hSCManager, __in
	 * SC_ENUM_TYPE InfoLevel, __in DWORD dwServiceType, __in DWORD
	 * dwServiceState, __out_opt LPBYTE lpServices, __in DWORD cbBufSize, __out
	 * LPDWORD pcbBytesNeeded, __out LPDWORD lpServicesReturned, __inout_opt
	 * LPDWORD lpResumeHandle, __in_opt LPCTSTR pszGroupName );
	 */
	public boolean EnumServicesStatusExW(Pointer hSCManager, int InfoLevel, int dwServiceType, int dwServiceState, Memory lpServices, int cbBufSize,
			IntByReference pcbBytesNeeded, IntByReference lpServicesReturned, IntByReference lpResumeHandle, String pszGroupName);

	public static final int	SERVICE_DISABLED			= 0x00000004;
	public static final int	SERVICE_INTERACTIVE_PROCESS	= 0x00000100;
	public static final int	SERVICE_AUTO_START			= 0x00000002;
	public static final int	SERVICE_BOOT_START			= 0x00000000;
	public static final int	SERVICE_DEMAND_START		= 0x00000003;
	public static final int	SERVICE_SYSTEM_START		= 0x00000001;

}
 public interface Options
{
	Map<String, Object>	UNICODE_OPTIONS	= new HashMap<String, Object>()
										{
											{
												put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
												put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
											}
											private static final long	serialVersionUID	= 1L;
										};
}
 public class Memory extends Pointer {

    private static Map buffers = new WeakHashMap();

    protected long size; // Size of the malloc'ed space

    /** Force cleanup of memory that has associated NIO Buffers which have
        been GC'd.
    */
    public static void purge() {
        buffers.size();
    }

    /** Provide a view into the original memory. */
    private class SharedMemory extends Memory {
        public SharedMemory(long offset) {
            this.size = Memory.this.size - offset;
            this.peer = Memory.this.peer + offset;
        }
        /** No need to free memory. */
        protected void finalize() { } 
        /** Pass bounds check to parent. */
        protected void boundsCheck(long off, long sz) {
            Memory.this.boundsCheck(this.peer - Memory.this.peer + off, sz);
        }
        public String toString() {
            return super.toString() + " (shared from " + Memory.this.toString() + ")";
        }
    }
    
    /**
     * Allocate space in the native heap via a call to C's <code>malloc</code>.
     *
     * @param size number of <em>bytes</em> of space to allocate
     */
    public Memory(long size) {
        this.size = size;
        if (size <= 0) {
            throw new IllegalArgumentException("Allocation size must be greater than zero");
        } 
        peer = malloc(size);
        if (peer == 0) 
            throw new OutOfMemoryError("Cannot allocate " + size + " bytes");
    }

    protected Memory() { }

    /** Provide a view onto this structure from the given offset.  The
     * returned {@link Pointer} will have the same size as the original,
     * reduced by the offset.
     * @throws IndexOutOfBoundsException if the requested memory is outside
     * the allocated bounds. 
     */
    public Pointer share(long offset) {
        return share(offset, getSize() - offset);
    }
    
    /** Provide a view onto this structure from the given offset. 
     * @throws IndexOutOfBoundsException if the requested memory is outside
     * the allocated bounds. 
     */
    public Pointer share(long offset, long sz) {
        if (offset == 0 && sz == getSize())
            return this;
        boundsCheck(offset, sz);
        return new SharedMemory(offset);
    }
    
    /** Provide a view onto this structure with the given alignment. 
     * @param byteBoundary Align memory to this number of bytes; should be a
     * power of two.
     * @throws IndexOutOfBoundsException if the requested alignment can
     * not be met.
     * @throws IllegalArgumentException if the requested alignment is not
     * a positive power of two.
     */
    public Memory align(int byteBoundary) {
        if (byteBoundary <= 0) {
            throw new IllegalArgumentException("Byte boundary must be positive: " + byteBoundary);
        }
        for (int i=0;i < 32;i++) {
            if (byteBoundary == (1<<i)) {
                long mask = ~((long)byteBoundary - 1);
                
                if ((peer & mask) != peer) {
                    long newPeer = (peer + byteBoundary - 1) & mask;
                    long newSize = peer + size - newPeer;
                    if (newSize <= 0) {
                        throw new IllegalArgumentException("Insufficient memory to align to the requested boundary");
                    }
                    return (Memory)share(newPeer - peer, newSize);
                }
                return this;
            }
        }
        throw new IllegalArgumentException("Byte boundary must be a power of two");
    }

    protected void finalize() {
        free(peer);
        peer = 0;
    }

    /** Zero the full extent of this memory region. */
    public void clear() {
        clear(size);
    }

    /** @deprecated use {@link #valid()} instead. */
    public boolean isValid() {
        return valid();
    }

    /** Returns false if the memory has been freed. */
    public boolean valid() {
        return peer != 0;
    }

    public long size() {
        return size;
    }

    /** @deprecated use {@link #size()} instead. */
    public long getSize() {
        return size();
    }


    /**
     * Check that indirection won't cause us to write outside the 
     * malloc'ed space. 
     *
     */
    protected void boundsCheck(long off, long sz) {
        if (off < 0) {
            throw new IndexOutOfBoundsException("Invalid offset: " + off);
        }
        if (off + sz > size) {
            String msg = "Bounds exceeds available space : size="
                + size + ", offset=" + (off + sz);
            throw new IndexOutOfBoundsException(msg);
        }
    }

    //////////////////////////////////////////////////////////////////////////
    // Raw read methods
    //////////////////////////////////////////////////////////////////////////

    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.read</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#read(long,byte[],int,int) 
     */
    public void read(long bOff, byte[] buf, int index, int length) {
        boundsCheck(bOff, length * 1);
        super.read(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.read</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#read(long,short[],int,int)
     */
    public void read(long bOff, short[] buf, int index, int length) {
        boundsCheck(bOff, length * 2);
        super.read(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.read</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#read(long,char[],int,int) 
     */
    public void read(long bOff, char[] buf, int index, int length) {
        boundsCheck(bOff, length * 2);
        super.read(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.read</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#read(long,int[],int,int)
     */
    public void read(long bOff, int[] buf, int index, int length) {
        boundsCheck(bOff, length * 4);
        super.read(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.read</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#read(long,long[],int,int) 
     */
    public void read(long bOff, long[] buf, int index, int length) {
        boundsCheck(bOff, length * 8);
        super.read(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.read</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#read(long,float[],int,int) 
     */
    public void read(long bOff, float[] buf, int index, int length) {
        boundsCheck(bOff, length * 4);
        super.read(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.read</code>.  But this method performs a bounds checks to
     * ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#read(long,double[],int,int) 
     */
    public void read(long bOff, double[] buf, int index, int length) 
    {
        boundsCheck(bOff, length * 8);
        super.read(bOff, buf, index, length);
    }




    //////////////////////////////////////////////////////////////////////////
    // Raw write methods
    //////////////////////////////////////////////////////////////////////////

    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.write</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#write(long,byte[],int,int) 
     */
    public void write(long bOff, byte[] buf, int index, int length) {
        boundsCheck(bOff, length * 1);
        super.write(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.write</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#write(long,short[],int,int)
     */
    public void write(long bOff, short[] buf, int index, int length) {
        boundsCheck(bOff, length * 2);
        super.write(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.write</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#write(long,char[],int,int)
     */
    public void write(long bOff, char[] buf, int index, int length) {
        boundsCheck(bOff, length * 2);
        super.write(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.write</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#write(long,int[],int,int) 
     */
    public void write(long bOff, int[] buf, int index, int length) {
        boundsCheck(bOff, length * 4);
        super.write(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.write</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#write(long,long[],int,int) 
     */
    public void write(long bOff, long[] buf, int index, int length) {
        boundsCheck(bOff, length * 8);
        super.write(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.write</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#write(long,float[],int,int)
     */
    public void write(long bOff, float[] buf, int index, int length) {
        boundsCheck(bOff, length * 4);
        super.write(bOff, buf, index, length);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.write</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#write(long,double[],int,int) 
     */
    public void write(long bOff, double[] buf, int index, int length) {
        boundsCheck(bOff, length * 8);
        super.write(bOff, buf, index, length);
    }




    //////////////////////////////////////////////////////////////////////////
    // Java type read methods
    //////////////////////////////////////////////////////////////////////////

    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.getByte</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#getByte(long)
     */
    public byte getByte(long offset) {
        boundsCheck(offset, 1);
        return super.getByte(offset);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.getByte</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#getByte(long)
     */
    public char getChar(long offset) {
        boundsCheck(offset, 1);
        return super.getChar(offset);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.getShort</code>.  But this method performs a bounds
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#getShort(long)
     */
    public short getShort(long offset) {
        boundsCheck(offset, 2);
        return super.getShort(offset);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.getInt</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#getInt(long)
     */
    public int getInt(long offset) {
        boundsCheck(offset, 4);
        return super.getInt(offset);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.getLong</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#getLong(long)
     */
    public long getLong(long offset) {
        boundsCheck(offset, 8);
        return super.getLong(offset);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.getFloat</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#getFloat(long)
     */
    public float getFloat(long offset) {
        boundsCheck(offset, 4);
        return super.getFloat(offset);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.getDouble</code>.  But this method performs a 
     * bounds check to ensure that the indirection does not cause memory 
     * outside the <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#getDouble(long)
     */
    public double getDouble(long offset) {
        boundsCheck(offset, 8);
        return super.getDouble(offset);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.getPointer</code>.  But this method performs 
     * a bounds checks to ensure that the indirection does not cause memory 
     * outside the <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#getPointer(long)
     */
    public Pointer getPointer(long offset) {
        boundsCheck(offset, Pointer.SIZE);
        return super.getPointer(offset);
    }

    /**
     * Get a ByteBuffer mapped to a portion of this memory.
     *
     * @param offset byte offset from pointer to start the buffer
     * @param length Length of ByteBuffer
     * @return a direct ByteBuffer that accesses the memory being pointed to, 
     */
    public ByteBuffer getByteBuffer(long offset, long length) {
        boundsCheck(offset, length);
        ByteBuffer b = super.getByteBuffer(offset, length);
        // Ensure this Memory object will not be GC'd (and its memory freed)
        // if the Buffer is still extant.
        buffers.put(b, this);
        return b;
    }

    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.getString</code>.  But this method performs a 
     * bounds checks to ensure that the indirection does not cause memory 
     * outside the <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#getString(long, boolean)
     */
    public String getString(long offset, boolean wide) {
        // NOTE: we only make sure the start of the string is within bounds
        boundsCheck(offset, 0); 
        return super.getString(offset, wide);
    }

    //////////////////////////////////////////////////////////////////////////
    // Java type write methods
    //////////////////////////////////////////////////////////////////////////

    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.setByte</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#setByte
     */
    public void setByte(long offset, byte value) {
        boundsCheck(offset, 1);
        super.setByte(offset, value);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.setChar</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#setChar
     */
    public void setChar(long offset, char value) {
        boundsCheck(offset, Native.WCHAR_SIZE);
        super.setChar(offset, value);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.setShort</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#setShort
     */
    public void setShort(long offset, short value) {
        boundsCheck(offset, 2);
        super.setShort(offset, value);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.setInt</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#setInt
     */
    public void setInt(long offset, int value) {
        boundsCheck(offset, 4);
        super.setInt(offset, value);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.setLong</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#setLong
     */
    public void setLong(long offset, long value) {
        boundsCheck(offset, 8);
        super.setLong(offset, value);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.setFloat</code>.  But this method performs a bounds 
     * checks to ensure that the indirection does not cause memory outside the
     * <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#setFloat
     */
    public void setFloat(long offset, float value) {
        boundsCheck(offset, 4);
        super.setFloat(offset, value);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.setDouble</code>.  But this method performs a 
     * bounds checks to ensure that the indirection does not cause memory 
     * outside the <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#setDouble
     */
    public void setDouble(long offset, double value) {
        boundsCheck(offset, 8);
        super.setDouble(offset, value);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.setPointer</code>.  But this method performs 
     * a bounds checks to ensure that the indirection does not cause memory 
     * outside the <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#setPointer
     */
    public void setPointer(long offset, Pointer value) {
        boundsCheck(offset, Pointer.SIZE);
        super.setPointer(offset, value);
    }


    /**
     * Indirect the native pointer to <code>malloc</code> space, a la
     * <code>Pointer.setString</code>.  But this method performs a 
     * bounds checks to ensure that the indirection does not cause memory 
     * outside the <code>malloc</code>ed space to be accessed.
     *
     * @see Pointer#setString(long,String,boolean)
     */
    public void setString(long offset, String value, boolean wide) {
        if (wide)
            boundsCheck(offset, (value.length() + 1) * Native.WCHAR_SIZE);
        else
            boundsCheck(offset, value.getBytes().length + 1);
        super.setString(offset, value, wide);
    }

    /**
     * Call the real native malloc
     */
    protected static native long malloc(long size);

    /**
     * Call the real native free
     */
    protected static native void free(long ptr);
    
    public String toString() {
        return "allocated@0x" + Long.toHexString(peer) + " ("
            + size + " bytes)";
    }
} public final class Native {

    private static final String VERSION = "3.2.7";

    private static String nativeLibraryPath = null;
    private static boolean unpacked;
    private static Map typeMappers = new WeakHashMap();
    private static Map alignments = new WeakHashMap();
    private static Map options = new WeakHashMap();
    private static Map libraries = new WeakHashMap();
    private static final UncaughtExceptionHandler DEFAULT_HANDLER = 
        new UncaughtExceptionHandler() {
            public void uncaughtException(Callback c, Throwable e) {
                System.err.println("JNA: Callback " + c + " threw the following exception:");
                e.printStackTrace();
            }
        };
    private static UncaughtExceptionHandler callbackExceptionHandler = DEFAULT_HANDLER;
    
    /** The size of a native pointer (<code>void*</code>) on the current 
     * platform, in bytes. 
     */
    public static final int POINTER_SIZE;
    /** Size of a native <code>long</code> type, in bytes. */
    public static final int LONG_SIZE;
    /** Size of a native <code>wchar_t</code> type, in bytes. */
    public static final int WCHAR_SIZE;
    /** Size of a native <code>size_t</code> type, in bytes. */
    public static final int SIZE_T_SIZE;

    private static final int TYPE_VOIDP = 0;
    private static final int TYPE_LONG = 1;
    private static final int TYPE_WCHAR_T = 2;
    private static final int TYPE_SIZE_T = 3;

    static {
        loadNativeLibrary();
        POINTER_SIZE = sizeof(TYPE_VOIDP);
        LONG_SIZE = sizeof(TYPE_LONG);
        WCHAR_SIZE = sizeof(TYPE_WCHAR_T);
        SIZE_T_SIZE = sizeof(TYPE_SIZE_T);
        // Perform initialization of other JNA classes until *after* 
        // initializing the above final fields
        initIDs();
        if (Boolean.getBoolean("jna.protected")) {
            setProtected(true);
        }
    }
    
    /** Ensure our unpacked native library gets cleaned up if this class gets
        garbage-collected.
    */
    private static final Object finalizer = new Object() {
        protected void finalize() {
            deleteNativeLibrary();
        }
    };

    /** Remove any unpacked native library.  Forcing the class loader to
        unload it first is required on Windows, since the temporary native
        library can't be deleted until the native library is unloaded.  Any
        deferred execution we might install at this point would prevent the
        Native class and its class loader from being GC'd, so we instead force
        the native library unload just a little bit prematurely.
     */
    private static boolean deleteNativeLibrary() {
        String path = nativeLibraryPath;
        if (path == null || !unpacked) return true;
        File flib = new File(path);
        if (flib.delete()) {
            nativeLibraryPath = null;
            unpacked = false;
            return true;
        }
        // Reach into the bowels of ClassLoader to force the native
        // library to unload
        // NOTE: this may cause a failure when freeing com.sun.jna.Memory
        // after the library has been unloaded, see
        // https://jna.dev.java.net/issues/show_bug.cgi?id=157
        try {
            ClassLoader cl = Native.class.getClassLoader();
            Field f = ClassLoader.class.getDeclaredField("nativeLibraries");
            f.setAccessible(true);
            List libs = (List)f.get(cl);
            for (Iterator i = libs.iterator();i.hasNext();) {
                Object lib = i.next();
                f = lib.getClass().getDeclaredField("name");
                f.setAccessible(true);
                String name = (String)f.get(lib);
                if (name.equals(path) || name.indexOf(path) != -1
                    || name.equals(flib.getCanonicalPath())) {
                    Method m = lib.getClass().getDeclaredMethod("finalize", new Class[0]);
                    m.setAccessible(true);
                    m.invoke(lib, new Object[0]);
                    nativeLibraryPath = null;
                    if (unpacked) {
                        if (flib.exists()) {
                            if (flib.delete()) {
                                unpacked = false;
                                return true;
                            }
                            return false;
                        }
                    }
                    return true;
                }
            }
        }
        catch(Exception e) {
        }
        return false;
    }

    private Native() { }
    
    private static native void initIDs();

    /** Set whether native memory accesses are protected from invalid
     * accesses.  This should only be set true when testing or debugging,
     * and should not be considered reliable or robust for applications
     * where JNA native calls are occurring on multiple threads.
     * Protected mode will be automatically set if the
     * system property <code>jna.protected</code> has a value of "true"
     * when the JNA library is first loaded.<p>
     * If not supported by the underlying platform, this setting will
     * have no effect.<p>
     * NOTE: On platforms which support signals (non-Windows), JNA uses
     * signals to trap errors.  This may interfere with the JVM's own use of
     * signals.  When protected mode is enabled, you should make use of the
     * jsig library, if available (see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/vm/signal-chaining.html">Signal Chaining</a>).
     * In short, set the environment variable <code>LD_PRELOAD</code> to the
     * path to <code>libjsig.so</code> in your JRE lib directory
     * (usually ${java.home}/lib/${os.arch}/libjsig.so) before launching your
     * Java application.
     */
    public static synchronized native void setProtected(boolean enable);
    
    /** Returns whether protection is enabled.  Check the result of this method
     * after calling {@link #setProtected setProtected(true)} to determine
     * if this platform supports protecting memory accesses.
     */
    public static synchronized native boolean isProtected();

    /** Set whether the system last error result is captured after every
     * native invocation.  Defaults to <code>true</code> (<code>false</code>
     * for direct-mapped calls).<p>
     * @deprecated The preferred method of obtaining the last error result is
     * to declare your mapped method to throw {@link LastErrorException}
     * instead. 
     */
    public static synchronized native void setPreserveLastError(boolean enable);
    
    /** Indicates whether the system last error result is preserved
     * after every invocation.<p>
     * @deprecated The preferred method of obtaining the last error result is
     * to declare your mapped method to throw {@link LastErrorException}
     * instead. 
     */
    public static synchronized native boolean getPreserveLastError();
    
    /** Utility method to get the native window ID for a Java {@link Window}
     * as a <code>long</code> value.
     * This method is primarily for X11-based systems, which use an opaque
     * <code>XID</code> (usually <code>long int</code>) to identify windows.
     * @throws HeadlessException if the current VM is running headless 
     */
    public static long getWindowID(Window w) throws HeadlessException {
        return getComponentID(w);
    }

    /** Utility method to get the native window ID for a heavyweight Java 
     * {@link Component} as a <code>long</code> value.
     * This method is primarily for X11-based systems, which use an opaque
     * <code>XID</code> (usually <code>long int</code>) to identify windows. 
     * @throws HeadlessException if the current VM is running headless 
     */
    public static long getComponentID(Component c) throws HeadlessException {
        if (GraphicsEnvironment.isHeadless()) {
            throw new HeadlessException("No native windows when headless");
        }
        if (c.isLightweight()) {
            throw new IllegalArgumentException("Component must be heavyweight");
        }
        if (!c.isDisplayable()) 
            throw new IllegalStateException("Component must be displayable");
        // On X11 VMs prior to 1.5, the window must be visible
        if (Platform.isX11()
            && System.getProperty("java.version").startsWith("1.4")) {
            if (!c.isVisible()) {
                throw new IllegalStateException("Component must be visible");
            }
        }
        // By this point, we're certain that Toolkit.loadLibraries() has
        // been called, thus avoiding AWT/JAWT link errors
        // (see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6539705).
        return getWindowHandle0(c);
    }
    
    /** Utility method to get the native window pointer for a Java 
     * {@link Window} as a {@link Pointer} value.  This method is primarily for 
     * w32, which uses the <code>HANDLE</code> type (actually 
     * <code>void *</code>) to identify windows. 
     * @throws HeadlessException if the current VM is running headless 
     */
    public static Pointer getWindowPointer(Window w) throws HeadlessException {
        return getComponentPointer(w);
    }
    
    /** Utility method to get the native window pointer for a heavyweight Java 
     * {@link Component} as a {@link Pointer} value.  This method is primarily 
     * for w32, which uses the <code>HWND</code> type (actually 
     * <code>void *</code>) to identify windows. 
     * @throws HeadlessException if the current VM is running headless 
     */
    public static Pointer getComponentPointer(Component c) throws HeadlessException {
        return new Pointer(getComponentID(c));
    }
    
    private static native long getWindowHandle0(Component c);

    /** Convert a direct {@link Buffer} into a {@link Pointer}. 
     * @throws IllegalArgumentException if the buffer is not direct.
     */
    public static native Pointer getDirectBufferPointer(Buffer b);
    
    /** Obtain a Java String from the given native byte array.  If there is
     * no NUL terminator, the String will comprise the entire array.  If the
     * system property <code>jna.encoding</code> is set, its value will 
     * override the platform default encoding (if supported).
     */
    public static String toString(byte[] buf) {
        return toString(buf, System.getProperty("jna.encoding"));
    }

    /** Obtain a Java String from the given native byte array, using the given
     * encoding.  If there is no NUL terminator, the String will comprise the
     * entire array.  If the <code>encoding</code> parameter is null, 
     * the platform default encoding will be used.
     */
    public static String toString(byte[] buf, String encoding) {
        String s = null;
        if (encoding != null) {
            try {
                s = new String(buf, encoding);
            }
            catch(UnsupportedEncodingException e) { }
        }
        if (s == null) {
            s = new String(buf);
        }
        int term = s.indexOf(0);
        if (term != -1)
            s = s.substring(0, term);
        return s;
    }
    
    /** Obtain a Java String from the given native wchar_t array.  If there is
     * no NUL terminator, the String will comprise the entire array.
     */
    public static String toString(char[] buf) {
        String s = new String(buf); 
        int term = s.indexOf(0);
        if (term != -1)
            s = s.substring(0, term);
        return s;
    }
    
    /** Map a library interface to the current process, providing
     * the explicit interface class.
     * @param interfaceClass
     */
    public static Object loadLibrary(Class interfaceClass) {
        return loadLibrary(null, interfaceClass);
    }

    /** Map a library interface to the current process, providing
     * the explicit interface class.
     * @param interfaceClass
     * @param options Map of library options
     */
    public static Object loadLibrary(Class interfaceClass, Map options) {
        return loadLibrary(null, interfaceClass, options);
    }

    /** Map a library interface to the given shared library, providing
     * the explicit interface class.
     * If <code>name</code> is null, attempts to map onto the current process.
     * @param name
     * @param interfaceClass
     */
    public static Object loadLibrary(String name, Class interfaceClass) {
        return loadLibrary(name, interfaceClass, Collections.EMPTY_MAP);
    }

    /** Load a library interface from the given shared library, providing
     * the explicit interface class and a map of options for the library.
     * If no library options are detected the map is interpreted as a map
     * of Java method names to native function names.<p>
     * If <code>name</code> is null, attempts to map onto the current process.
     * @param name
     * @param interfaceClass
     * @param options Map of library options
     */
    public static Object loadLibrary(String name, 
                                     Class interfaceClass,
                                     Map options) {
        Library.Handler handler = 
            new Library.Handler(name, interfaceClass, options);
        ClassLoader loader = interfaceClass.getClassLoader();
        Library proxy = (Library)
            Proxy.newProxyInstance(loader, new Class[] {interfaceClass},
                                   handler);
        cacheOptions(interfaceClass, options, proxy);
        return proxy;
    }

    /** Attempts to force initialization of an instance of the library interface
     * by loading a public static field of the requisite type.
     * Returns whether an instance variable was instantiated. 
     * Expects that lock on libraries is already held
     */
    private static void loadLibraryInstance(Class cls) {
        if (cls != null && !libraries.containsKey(cls)) {
            try {
                Field[] fields = cls.getFields();
                for (int i=0;i < fields.length;i++) {
                    Field field = fields[i];
                    if (field.getType() == cls 
                        && Modifier.isStatic(field.getModifiers())) {
                        // Ensure the field gets initialized by reading it
                        libraries.put(cls, new WeakReference(field.get(null)));
                        break;
                    }
                }
            }
            catch (Exception e) {
                throw new IllegalArgumentException("Could not access instance of " 
                                                   + cls + " (" + e + ")");
            }
        }
    }
    
    /** Find the library interface corresponding to the given class.  Checks
     * all ancestor classes and interfaces for a declaring class which
     * implements {@link Library}.
     */
    static Class findEnclosingLibraryClass(Class cls) {
        if (cls == null) { 
            return null;
        }
        synchronized(libraries) {
            if (options.containsKey(cls)) {
                return cls;
            }
        }
        if (Library.class.isAssignableFrom(cls)) {
            return cls;
        }
        if (Callback.class.isAssignableFrom(cls)) {
            cls = CallbackReference.findCallbackClass(cls);
        }
        Class declaring = cls.getDeclaringClass();
        Class fromDeclaring = findEnclosingLibraryClass(declaring);
        if (fromDeclaring != null) {
            return fromDeclaring;
        }
        return findEnclosingLibraryClass(cls.getSuperclass());
    }
    

    /** Return the preferred native library configuration options for the given 
     * class.
     * @see Library 
     */
    public static Map getLibraryOptions(Class type) {
        synchronized(libraries) {
            Class interfaceClass = findEnclosingLibraryClass(type);
            if (interfaceClass != null) 
                loadLibraryInstance(interfaceClass);
            else
                interfaceClass = type;
            if (!options.containsKey(interfaceClass)) {
                try {
                    Field field = interfaceClass.getField("OPTIONS");
                    field.setAccessible(true);
                    options.put(interfaceClass, field.get(null));
                }
                catch (NoSuchFieldException e) {
                }
                catch (Exception e) {
                    throw new IllegalArgumentException("OPTIONS must be a public field of type java.util.Map (" 
                                                       + e + "): " + interfaceClass);
                }
            }
            return (Map)options.get(interfaceClass);
        }
    }

    /** Return the preferred {@link TypeMapper} for the given native interface.
     * See {@link com.sun.jna.Library#OPTION_TYPE_MAPPER}. 
     */
    public static TypeMapper getTypeMapper(Class cls) {
        synchronized(libraries) {
            Class interfaceClass = findEnclosingLibraryClass(cls);
            if (interfaceClass != null)
                loadLibraryInstance(interfaceClass);
            else
                interfaceClass = cls;

            if (!typeMappers.containsKey(interfaceClass)) {
                try {
                    Field field = interfaceClass.getField("TYPE_MAPPER");
                    field.setAccessible(true);
                    typeMappers.put(interfaceClass, field.get(null));
                }
                catch (NoSuchFieldException e) {
                    Map options = getLibraryOptions(cls);
                    if (options != null
                        && options.containsKey(Library.OPTION_TYPE_MAPPER)) {
                        typeMappers.put(interfaceClass, options.get(Library.OPTION_TYPE_MAPPER));
                    }
                }
                catch (Exception e) {
                    throw new IllegalArgumentException("TYPE_MAPPER must be a public field of type "
                                                       + TypeMapper.class.getName() + " (" 
                                                       + e + "): " + interfaceClass);
                }
            }
            return (TypeMapper)typeMappers.get(interfaceClass);
        }
    }

    /** Return the preferred structure alignment for the given native interface. 
     * See {@link com.sun.jna.Library#OPTION_STRUCTURE_ALIGNMENT}.
     */
    public static int getStructureAlignment(Class cls) {
        synchronized(libraries) {
            Class interfaceClass = findEnclosingLibraryClass(cls);
            if (interfaceClass != null) 
                loadLibraryInstance(interfaceClass);
            else
                interfaceClass = cls;
            if (!alignments.containsKey(interfaceClass)) {
                try {
                    Field field = interfaceClass.getField("STRUCTURE_ALIGNMENT");
                    field.setAccessible(true);
                    alignments.put(interfaceClass, field.get(null));
                }
                catch(NoSuchFieldException e) {
                    Map options = getLibraryOptions(interfaceClass);
                    if (options != null
                        && options.containsKey(Library.OPTION_STRUCTURE_ALIGNMENT)) {
                        alignments.put(interfaceClass, options.get(Library.OPTION_STRUCTURE_ALIGNMENT));
                    }
                }
                catch(Exception e) {
                    throw new IllegalArgumentException("STRUCTURE_ALIGNMENT must be a public field of type int ("
                                                       + e + "): " + interfaceClass);
                }
            }
            Integer value = (Integer)alignments.get(interfaceClass);
            return value != null ? value.intValue() : Structure.ALIGN_DEFAULT;
        }
    }
    
    /** Return a byte array corresponding to the given String.  If the
     * system property <code>jna.encoding</code> is set, its value will override
     * the default platform encoding (if supported).
     */
    static byte[] getBytes(String s) {
        try {
            return getBytes(s, System.getProperty("jna.encoding"));
        }
        catch (UnsupportedEncodingException e) {
            return s.getBytes();
        }
    }

    /** Return a byte array corresponding to the given String, using the given
        encoding.
    */
    static byte[] getBytes(String s, String encoding) throws UnsupportedEncodingException {
        if (encoding != null) {
            return s.getBytes(encoding);
        }
        return s.getBytes();
    }

    /** Obtain a NUL-terminated byte buffer equivalent to the given String,
        using <code>jna.encoding</code> or the default platform encoding if
        that property is not set.
    */
    public static byte[] toByteArray(String s) {
        byte[] bytes = getBytes(s);
        byte[] buf = new byte[bytes.length+1];
        System.arraycopy(bytes, 0, buf, 0, bytes.length);
        return buf;
    }

    /** Obtain a NUL-terminated byte buffer equivalent to the given String,
        using the given encoding.
     */
    public static byte[] toByteArray(String s, String encoding) throws UnsupportedEncodingException {
        byte[] bytes = getBytes(s, encoding);
        byte[] buf = new byte[bytes.length+1];
        System.arraycopy(bytes, 0, buf, 0, bytes.length);
        return buf;
    }

    /** Obtain a NUL-terminated wide character buffer equivalent to the given
        String.
    */ 
    public static char[] toCharArray(String s) {
        char[] chars = s.toCharArray();
        char[] buf = new char[chars.length+1];
        System.arraycopy(chars, 0, buf, 0, chars.length);
        return buf;
    }

    static String getNativeLibraryResourcePath(int osType, String arch, String name) {
        String osPrefix;
        arch = arch.toLowerCase();
        switch(osType) {
        case Platform.WINDOWS:
            if ("i386".equals(arch))
                arch = "x86";
            osPrefix = "win32-" + arch;
            break;
        case Platform.MAC:
            osPrefix = "darwin";
            break;
        case Platform.LINUX:
            if ("x86".equals(arch)) {
                arch = "i386";
            }
            else if ("x86_64".equals(arch)) {
                arch = "amd64";
            }
            osPrefix = "linux-" + arch;
            break;
        case Platform.SOLARIS:
            osPrefix = "sunos-" + arch;
            break;
        default:
            osPrefix = name.toLowerCase();
            if ("x86".equals(arch)) {
                arch = "i386";
            }
            if ("x86_64".equals(arch)) {
                arch = "amd64";
            }
            if ("powerpc".equals(arch)) {
                arch = "ppc";
            }
            int space = osPrefix.indexOf(" ");
            if (space != -1) {
                osPrefix = osPrefix.substring(0, space);
            }
            osPrefix += "-" + arch;
            break;
        }
        return "/com/sun/jna/" + osPrefix;
    }

    /**
     * Loads the JNA stub library.  It will first attempt to load this library
     * from the directories specified in jna.boot.library.path.  If that fails,
     * it will fallback to loading from the system library paths. Finally it will
     * attempt to extract the stub library from from the JNA jar file, and load it.
     * <p>
     * The jna.boot.library.path property is mainly to support jna.jar being
     * included in -Xbootclasspath, where java.library.path and LD_LIBRARY_PATH
     * are ignored.  It might also be useful in other situations.
     * </p>
     */
    private static void loadNativeLibrary() {
        String libName = "jnidispatch";
        String bootPath = System.getProperty("jna.boot.library.path");
        if (bootPath != null) {
            String[] dirs = bootPath.split(File.pathSeparator);
            for (int i = 0; i < dirs.length; ++i) {
                String path = new File(new File(dirs[i]), System.mapLibraryName(libName)).getAbsolutePath();
                try {
                    System.load(path);
                    nativeLibraryPath = path;
                    return;
                } catch (UnsatisfiedLinkError ex) {
                }
                if (Platform.isMac()) {
                    String orig, ext;
                    if (path.endsWith("dylib")) {
                        orig = "dylib";
                        ext = "jnilib";
                    } else {
                        orig = "jnilib";
                        ext = "dylib";
                    }
                    try {
                        path = path.substring(0, path.lastIndexOf(orig)) + ext;
                        System.load(path);
                        nativeLibraryPath = path;
                        return;
                    } catch (UnsatisfiedLinkError ex) {
                    }
                }
            }
        }
        try {
            System.loadLibrary(libName);
            nativeLibraryPath = libName;
        }
        catch(UnsatisfiedLinkError e) {
            loadNativeLibraryFromJar();
        }
    }

    /**
     * Attempts to load the native library resource from the filesystem,
     * extracting the JNA stub library from jna.jar if not already available.
     */
    private static void loadNativeLibraryFromJar() {
        String libname = System.mapLibraryName("jnidispatch");
        String arch = System.getProperty("os.arch");
        String name = System.getProperty("os.name");
        String resourceName = getNativeLibraryResourcePath(Platform.getOSType(), arch, name) + "/" + libname;
        URL url = Native.class.getResource(resourceName);
                
        // Add an ugly hack for OpenJDK (soylatte) - JNI libs use the usual
        // .dylib extension 
        if (url == null && Platform.isMac()
            && resourceName.endsWith(".dylib")) {
            resourceName = resourceName.substring(0, resourceName.lastIndexOf(".dylib")) + ".jnilib";
            url = Native.class.getResource(resourceName);
        }
        if (url == null) {
            throw new UnsatisfiedLinkError("jnidispatch (" + resourceName 
                                           + ") not found in resource path");
        }
    
        File lib = null;
        if (url.getProtocol().toLowerCase().equals("file")) {
            try {
                lib = new File(new URI(url.toString()));
            }
            catch(URISyntaxException e) {
                lib = new File(url.getPath());
            }
            if (!lib.exists()) {
                throw new Error("File URL " + url + " could not be properly decoded");
            }
        }
        else {
            InputStream is = Native.class.getResourceAsStream(resourceName);
            if (is == null) {
                throw new Error("Can't obtain jnidispatch InputStream");
            }
            
            FileOutputStream fos = null;
            try {
                // Suffix is required on windows, or library fails to load
                // Let Java pick the suffix, except on windows, to avoid
                // problems with Web Start.
                lib = File.createTempFile("jna", Platform.isWindows()?".dll":null);
                lib.deleteOnExit();
                ClassLoader cl = Native.class.getClassLoader();
                if (Platform.deleteNativeLibraryAfterVMExit()
                    && (cl == null
                        || cl.equals(ClassLoader.getSystemClassLoader()))) {
                    Runtime.getRuntime().addShutdownHook(new DeleteNativeLibrary(lib));
                }
                fos = new FileOutputStream(lib);
                int count;
                byte[] buf = new byte[1024];
                while ((count = is.read(buf, 0, buf.length)) > 0) {
                    fos.write(buf, 0, count);
                }
            }
            catch(IOException e) {
                throw new Error("Failed to create temporary file for jnidispatch library: " + e);
            }
            finally {
                try { is.close(); } catch(IOException e) { }
                if (fos != null) {
                    try { fos.close(); } catch(IOException e) { }
                }
            }
            unpacked = true;
        }
        System.load(lib.getAbsolutePath());
        nativeLibraryPath = lib.getAbsolutePath();
    }

    /**
     * Initialize field and method IDs for native methods of this class. 
     * Returns the size of a native pointer.
     **/
    private static native int sizeof(int type);

    private static native String getNativeVersion();
    private static native String getAPIChecksum();

    private static final ThreadLocal lastError = new ThreadLocal() {
        protected synchronized Object initialValue() {
            return new Integer(0);
        }
    };
    
    /** Retrieve the last error set by the OS.  This corresponds to
     * <code>GetLastError()</code> on Windows, and <code>errno</code> on
     * most other platforms.  The value is preserved per-thread, but whether 
     * the original value is per-thread depends on the underlying OS.  The 
     * result is undefined if {@link #getPreserveLastError} is 
     * <code>false</code>.<p>
     * The preferred method of obtaining the last error result is
     * to declare your mapped method to throw {@link LastErrorException}
     * instead. 
     */
    public static int getLastError() {
        return ((Integer)lastError.get()).intValue();
    }
    
    /** Set the OS last error code.  Whether the setting is per-thread
     * or global depends on the underlying OS.
     */
    public static native void setLastError(int code);

    /** Update the last error value (called from native code). */
    static void updateLastError(int e) {
        lastError.set(new Integer(e));
    }

    /**
     * Returns a synchronized (thread-safe) library backed by the specified
     * library.  This wrapping will prevent simultaneous invocations of any
     * functions mapped to a given {@link NativeLibrary}.  Note that the 
     * native library may still be sensitive to being called from different
     * threads.
     * <p>
     * @param  library the library to be "wrapped" in a synchronized library.
     * @return a synchronized view of the specified library.
     */
    public static Library synchronizedLibrary(final Library library) {
        Class cls = library.getClass();
        if (!Proxy.isProxyClass(cls)) {
            throw new IllegalArgumentException("Library must be a proxy class");
        }
        InvocationHandler ih = Proxy.getInvocationHandler(library);
        if (!(ih instanceof Library.Handler)) {
            throw new IllegalArgumentException("Unrecognized proxy handler: " + ih);
        }
        final Library.Handler handler = (Library.Handler)ih; 
        InvocationHandler newHandler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                synchronized(handler.getNativeLibrary()) {
                    return handler.invoke(library, method, args);
                }
            }
        };
        return (Library)Proxy.newProxyInstance(cls.getClassLoader(),
                                               cls.getInterfaces(),
                                               newHandler);
    }
    
    /** If running web start, determine the location of a given native 
     * library.  This value may be used to properly set 
     * <code>jna.library.path</code> so that JNA can load libraries identified
     * by the &lt;nativelib&gt; tag in the JNLP configuration file.  Returns 
     * <code>null</code> if the Web Start native library cache location can not 
     * be determined.  Note that the path returned may be different for any
     * given library name.
     * <p>
     * Use <code>System.getProperty("javawebstart.version")</code> to detect
     * whether your code is running under Web Start.
     * @throws UnsatisfiedLinkError if the library can't be found by the
     * Web Start class loader, which usually means it wasn't included as 
     * a <code>&lt;nativelib&gt;</code> resource in the JNLP file.
     * @return null if unable to query the web start loader.
     */
    public static String getWebStartLibraryPath(final String libName) {
        if (System.getProperty("javawebstart.version") == null)
            return null;
        try {

            final ClassLoader cl = Native.class.getClassLoader();
            Method m = (Method)AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    try {
                        Method m = ClassLoader.class.getDeclaredMethod("findLibrary", new Class[] { String.class });
                        m.setAccessible(true);
                        return m;
                    }
                    catch(Exception e) {
                        return null;
                    }
                }
            });
            String libpath = (String)m.invoke(cl, new Object[] { libName });
            if (libpath != null) {
                return new File(libpath).getParent();
            }
            return null;
        }
        catch (Exception e) {
            return null;
        }
    }

    /** For internal use only. */
    public static class DeleteNativeLibrary extends Thread {
        private final File file;
        public DeleteNativeLibrary(File file) {
            this.file = file;
        }
        public void run() {
            // If we can't force an unload/delete, spawn a new process
            // to do so
            if (!deleteNativeLibrary()) {
                try {
                    Runtime.getRuntime().exec(new String[] {
                        System.getProperty("java.home") + "/bin/java",
                        "-cp", System.getProperty("java.class.path"),
                        getClass().getName(),
                        file.getAbsolutePath(),
                    });
                }
                catch(IOException e) { e.printStackTrace(); }
            }
        }
        public static void main(String[] args) {
            if (args.length == 1) {
                File file = new File(args[0]);
                if (file.exists()) { 
                    long start = System.currentTimeMillis();
                    while (!file.delete() && file.exists()) {
                        try { Thread.sleep(10); }
                        catch(InterruptedException e) { }
                        if (System.currentTimeMillis() - start > 5000) {
                            System.err.println("Could not remove temp file: "
                                               + file.getAbsolutePath());
                            break;
                        }
                    }
                }
            }
            System.exit(0);
        }
    }

    /** Returns the native size of the given class, in bytes. 
     * For use with arrays.
     */
    public static int getNativeSize(Class type, Object value) {
        if (type.isArray()) {
            int len = Array.getLength(value);
            if (len > 0) {
                Object o = Array.get(value, 0);
                return len * getNativeSize(type.getComponentType(), o);
            }
            // Don't process zero-length arrays
            throw new IllegalArgumentException("Arrays of length zero not allowed: " + type);
        }
        if (Structure.class.isAssignableFrom(type)
            && !Structure.ByReference.class.isAssignableFrom(type)) {
            if (value == null)
                value = Structure.newInstance(type);
            return ((Structure)value).size();
        }
        try {
            return getNativeSize(type);
        }
        catch(IllegalArgumentException e) {
            throw new IllegalArgumentException("The type \"" + type.getName()
                                               + "\" is not supported: "
                                               + e.getMessage());
        }
    }

    /** Returns the native size for a given Java class.  Structures are
     * assumed to be <code>struct</code> pointers unless they implement
     * {@link Structure.ByValue}.
     */
    public static int getNativeSize(Class cls) {
        if (NativeMapped.class.isAssignableFrom(cls)) {
            cls = NativeMappedConverter.getInstance(cls).nativeType();
        }
        // boolean defaults to 32 bit integer if not otherwise mapped
        if (cls == boolean.class || cls == Boolean.class) return 4;
        if (cls == byte.class || cls == Byte.class) return 1;
        if (cls == short.class || cls == Short.class) return 2; 
        if (cls == char.class || cls == Character.class) return WCHAR_SIZE;
        if (cls == int.class || cls == Integer.class) return 4;
        if (cls == long.class || cls == Long.class) return 8;
        if (cls == float.class || cls == Float.class) return 4;
        if (cls == double.class || cls == Double.class) return 8;
        if (Structure.class.isAssignableFrom(cls)) {
            if (Structure.ByValue.class.isAssignableFrom(cls)) {
                return Structure.newInstance(cls).size();
            }
            return POINTER_SIZE;
        }
        if (Pointer.class.isAssignableFrom(cls)
            || Buffer.class.isAssignableFrom(cls)
            || Callback.class.isAssignableFrom(cls)
            || String.class == cls
            || WString.class == cls) {
            return POINTER_SIZE;
        }
        throw new IllegalArgumentException("Native size for type \"" + cls.getName() 
        								   + "\" is unknown");
    }

    /** Indicate whether the given class is supported as a native argument
     * type.
     */  
    public static boolean isSupportedNativeType(Class cls) {
        if (Structure.class.isAssignableFrom(cls)) {
            return true;
        }
        try {
            return getNativeSize(cls) != 0;
        }
        catch(IllegalArgumentException e) {
            return false;
        }
    }

    /** Set the default handler invoked when a callback throws an uncaught
     * exception.  If the given handler is <code>null</code>, the default
     * handler will be reinstated.
     */
    public static void setCallbackExceptionHandler(UncaughtExceptionHandler eh) {
        callbackExceptionHandler = eh == null ? DEFAULT_HANDLER : eh;
    }

    /** Returns the current handler for callback uncaught exceptions. */
    public static UncaughtExceptionHandler getCallbackExceptionHandler() {
        return callbackExceptionHandler;
    }
    
    /** When called from a class static initializer, maps all native methods
     * found within that class to native libraries via the JNA raw calling
     * interface.
     * @param libName library name to which functions should be bound
     */
    public static void register(String libName) {
        register(getNativeClass(getCallingClass()),
                 NativeLibrary.getInstance(libName));
    }

    /** When called from a class static initializer, maps all native methods
     * found within that class to native libraries via the JNA raw calling
     * interface.
     * @param lib native library to which functions should be bound
     */
    public static void register(NativeLibrary lib) {
        register(getNativeClass(getCallingClass()), lib);
    }

    static Class getNativeClass(Class cls) {
        Method[] methods = cls.getDeclaredMethods();
        for (int i=0;i < methods.length;i++) {
            if ((methods[i].getModifiers() & Modifier.NATIVE) != 0) {
                return cls;
            }
        }
        int idx = cls.getName().lastIndexOf("$");
        if (idx != -1) {
            String name = cls.getName().substring(0, idx);
            try {
                return getNativeClass(Class.forName(name, true, cls.getClassLoader()));
            }
            catch(ClassNotFoundException e) {
            }
        }
        throw new IllegalArgumentException("Can't determine class with native methods from the current context (" + cls + ")");
    }

    static Class getCallingClass() {
        Class[] context = new SecurityManager() {
            public Class[] getClassContext() {
                return super.getClassContext();
            }
        }.getClassContext();
        if (context.length < 4) {
            throw new IllegalStateException("This method must be called from the static initializer of a class");
        }
        return context[3];
    }

    private static Map registeredClasses = new HashMap();
    private static Map registeredLibraries = new HashMap();
    private static Object unloader = new Object() {
        protected void finalize() {
            synchronized(registeredClasses) {
                for (Iterator i=registeredClasses.entrySet().iterator();i.hasNext();) {
                    Map.Entry e = (Map.Entry)i.next();
                    unregister((Class)e.getKey(), (long[])e.getValue());
                    i.remove();
                }
            }
        }
    };

    /** Remove all native mappings for the calling class.
        Should only be called if the class is no longer referenced and about
        to be garbage collected.
     */
    public static void unregister() {
        unregister(getNativeClass(getCallingClass()));
    }

    /** Remove all native mappings for the given class.
        Should only be called if the class is no longer referenced and about
        to be garbage collected.
     */
    public static void unregister(Class cls) {
        synchronized(registeredClasses) {
            if (registeredClasses.containsKey(cls)) {
                unregister(cls, (long[])registeredClasses.get(cls));
                registeredClasses.remove(cls);
                registeredLibraries.remove(cls);
            }
        }
    }

    /** Unregister the native methods for the given class. */
    private static native void unregister(Class cls, long[] handles);

    private static String getSignature(Class cls) {
        if (cls.isArray()) {
            return "[" + getSignature(cls.getComponentType());
        }
        if (cls.isPrimitive()) {
            if (cls == void.class) return "V";
            if (cls == boolean.class) return "Z";
            if (cls == byte.class) return "B";
            if (cls == short.class) return "S";
            if (cls == char.class) return "C";
            if (cls == int.class) return "I";
            if (cls == long.class) return "J";
            if (cls == float.class) return "F";
            if (cls == double.class) return "D";
        }
        return "L" + replace(".", "/", cls.getName()) + ";";
    }

    // No String.replace available in 1.4 
    static String replace(String s1, String s2, String str) {
        StringBuffer buf = new StringBuffer();
        while (true) {
            int idx = str.indexOf(s1);
            if (idx == -1) {
                buf.append(str);
                break;
            }
            else {
                buf.append(str.substring(0, idx));
                buf.append(s2);
                str = str.substring(idx + s1.length());
            }
        }
        return buf.toString();
    }

    private static final int CVT_UNSUPPORTED = -1;
    private static final int CVT_DEFAULT = 0;
    private static final int CVT_POINTER = 1;
    private static final int CVT_STRING = 2;
    private static final int CVT_STRUCTURE = 3;
    private static final int CVT_STRUCTURE_BYVAL = 4;
    private static final int CVT_BUFFER = 5;
    private static final int CVT_ARRAY_BYTE = 6;
    private static final int CVT_ARRAY_SHORT = 7;
    private static final int CVT_ARRAY_CHAR = 8;
    private static final int CVT_ARRAY_INT = 9;
    private static final int CVT_ARRAY_LONG = 10;
    private static final int CVT_ARRAY_FLOAT = 11;
    private static final int CVT_ARRAY_DOUBLE = 12;
    private static final int CVT_ARRAY_BOOLEAN = 13;
    private static final int CVT_BOOLEAN = 14;
    private static final int CVT_CALLBACK = 15;
    private static final int CVT_FLOAT = 16;
    private static final int CVT_NATIVE_MAPPED = 17;
    private static final int CVT_WSTRING = 18;
    private static final int CVT_INTEGER_TYPE = 19;
    private static final int CVT_POINTER_TYPE = 20;
    private static final int CVT_TYPE_MAPPER = 21;

    private static int getConversion(Class type, TypeMapper mapper) {
        if (type == Boolean.class) type = boolean.class;
        else if (type == Byte.class) type = byte.class;
        else if (type == Short.class) type = short.class;
        else if (type == Character.class) type = char.class;
        else if (type == Integer.class) type = int.class;
        else if (type == Long.class) type = long.class;
        else if (type == Float.class) type = float.class;
        else if (type == Double.class) type = double.class;
        else if (type == Void.class) type = void.class;
            
        if (mapper != null
            && (mapper.getFromNativeConverter(type) != null
                || mapper.getToNativeConverter(type) != null)) {
            return CVT_TYPE_MAPPER;
        }

        if (Pointer.class.isAssignableFrom(type)) {
            return CVT_POINTER;
        }
        if (String.class == type) {
            return CVT_STRING;
        }
        if (WString.class.isAssignableFrom(type)) {
            return CVT_WSTRING;
        }
        if (Buffer.class.isAssignableFrom(type)) {
            return CVT_BUFFER;
        }
        if (Structure.class.isAssignableFrom(type)) {
            if (Structure.ByValue.class.isAssignableFrom(type)) {
                return CVT_STRUCTURE_BYVAL;
            }
            return CVT_STRUCTURE;
        }
        if (type.isArray()) {
            switch(type.getName().charAt(1)) {
            case 'Z': return CVT_ARRAY_BOOLEAN;
            case 'B': return CVT_ARRAY_BYTE;
            case 'S': return CVT_ARRAY_SHORT;
            case 'C': return CVT_ARRAY_CHAR;
            case 'I': return CVT_ARRAY_INT;
            case 'J': return CVT_ARRAY_LONG;
            case 'F': return CVT_ARRAY_FLOAT;
            case 'D': return CVT_ARRAY_DOUBLE;
            default: break;
            }
        }
        if (type.isPrimitive()) {
            return type == boolean.class ? CVT_BOOLEAN : CVT_DEFAULT;
        }
        if (Callback.class.isAssignableFrom(type)) {
            return CVT_CALLBACK;
        }
        if (IntegerType.class.isAssignableFrom(type)) {
            return CVT_INTEGER_TYPE;
        }
        if (PointerType.class.isAssignableFrom(type)) {
            return CVT_POINTER_TYPE;
        }
        if (NativeMapped.class.isAssignableFrom(type)) {
            return CVT_NATIVE_MAPPED;
        }
        return CVT_UNSUPPORTED;
    }

    /** When called from a class static initializer, maps all native methods
     * found within that class to native libraries via the JNA raw calling
     * interface.
     * @param lib library to which functions should be bound
     */
    // TODO: derive options from annotations (per-class or per-method)
    // options: read parameter type mapping (long/native long),
    // method name, library name, call conv
    public static void register(Class cls, NativeLibrary lib) {
        Method[] methods = cls.getDeclaredMethods();
        List mlist = new ArrayList();
        TypeMapper mapper = (TypeMapper)
            lib.getOptions().get(Library.OPTION_TYPE_MAPPER);

        for (int i=0;i < methods.length;i++) {
            if ((methods[i].getModifiers() & Modifier.NATIVE) != 0) {
                mlist.add(methods[i]);
            }
        }
        long[] handles = new long[mlist.size()];
        for (int i=0;i < handles.length;i++) {
            Method method = (Method)mlist.get(i);
            String sig = "(";
            Class rclass = method.getReturnType();
            long rtype, closure_rtype;
            Class[] ptypes = method.getParameterTypes();
            long[] atypes = new long[ptypes.length];
            long[] closure_atypes = new long[ptypes.length];
            int[] cvt = new int[ptypes.length];
            ToNativeConverter[] toNative = new ToNativeConverter[ptypes.length];
            FromNativeConverter fromNative = null;
            int rcvt = getConversion(rclass, mapper);
            boolean throwLastError = false;
            switch (rcvt) {
            case CVT_UNSUPPORTED:
                throw new IllegalArgumentException(rclass + " is not a supported return type (in method " + method.getName() + " in " + cls + ")");
            case CVT_TYPE_MAPPER:
                fromNative = mapper.getFromNativeConverter(rclass);
                closure_rtype = FFIType.get(rclass).peer;
                rtype = FFIType.get(fromNative.nativeType()).peer;
                break;
            case CVT_NATIVE_MAPPED:
            case CVT_INTEGER_TYPE:
            case CVT_POINTER_TYPE:
                closure_rtype = FFIType.get(Pointer.class).peer;
                rtype = FFIType.get(NativeMappedConverter.getInstance(rclass).nativeType()).peer;
                break;
            case CVT_STRUCTURE:
                closure_rtype = rtype = FFIType.get(Pointer.class).peer;
                break;
            case CVT_STRUCTURE_BYVAL:
                closure_rtype = FFIType.get(Pointer.class).peer;
                rtype = FFIType.get(rclass).peer;
                break;
            default:
                closure_rtype = rtype = FFIType.get(rclass).peer;
                break;
            }
            for (int t=0;t < ptypes.length;t++) {
                Class type = ptypes[t];
                sig += getSignature(type);
                cvt[t] = getConversion(type, mapper);
                if (cvt[t] == CVT_UNSUPPORTED) {
                    throw new IllegalArgumentException(type + " is not a supported argument type (in method " + method.getName() + " in " + cls + ")");
                }
                if (cvt[t] == CVT_NATIVE_MAPPED
                    || cvt[t] == CVT_INTEGER_TYPE) {
                    type = NativeMappedConverter.getInstance(type).nativeType();
                }
                else if (cvt[t] == CVT_TYPE_MAPPER) {
                    toNative[t] = mapper.getToNativeConverter(type);
                }
                // Determine the type that will be passed to the native
                // function, as well as the type to be passed
                // from Java initially
                switch(cvt[t]) {
                case CVT_STRUCTURE_BYVAL:
                case CVT_INTEGER_TYPE:
                case CVT_POINTER_TYPE:
                case CVT_NATIVE_MAPPED:
                    atypes[t] = FFIType.get(type).peer;
                    closure_atypes[t] = FFIType.get(Pointer.class).peer;
                    break;
                case CVT_TYPE_MAPPER:
                    if (type.isPrimitive())
                        closure_atypes[t] = FFIType.get(type).peer;
                    else
                        closure_atypes[t] = FFIType.get(Pointer.class).peer;
                    atypes[t] = FFIType.get(toNative[t].nativeType()).peer;
                    break;
                case CVT_DEFAULT:
                    closure_atypes[t] = atypes[t] = FFIType.get(type).peer;
                    break;
                default:
                    closure_atypes[t] = atypes[t] = FFIType.get(Pointer.class).peer;
                    break;
                }
            }
            sig += ")";
            sig += getSignature(rclass);
            
            Class[] etypes = method.getExceptionTypes();
            for (int e=0;e < etypes.length;e++) {
                if (LastErrorException.class.isAssignableFrom(etypes[e])) {
                    throwLastError = true;
                    break;
                }
            }

            String name = method.getName();
            FunctionMapper fmapper = (FunctionMapper)lib.getOptions().get(Library.OPTION_FUNCTION_MAPPER);
            if (fmapper != null) {
                name = fmapper.getFunctionName(lib, method);
            }
            Function f = lib.getFunction(name, method);
            try {
                handles[i] = registerMethod(cls, method.getName(),
                                            sig, cvt,
                                            closure_atypes, atypes, rcvt,
                                            closure_rtype, rtype, 
                                            rclass,
                                            f.peer, f.getCallingConvention(),
                                            throwLastError,
                                            toNative, fromNative);
            }
            catch(NoSuchMethodError e) {
                throw new UnsatisfiedLinkError("No method " + method.getName() + " with signature " + sig + " in " + cls);
            }
        }
        synchronized(registeredClasses) {
            registeredClasses.put(cls, handles);
            registeredLibraries.put(cls, lib);
        }
        cacheOptions(cls, lib.getOptions(), null);
    }

    /** Take note of options used for a given library mapping, to facilitate
        looking them up later.
    */
    private static void cacheOptions(Class cls, Map libOptions, Object proxy) {
        synchronized(libraries) {
            if (!libOptions.isEmpty()) 
                options.put(cls, libOptions);
            if (libOptions.containsKey(Library.OPTION_TYPE_MAPPER))
                typeMappers.put(cls, libOptions.get(Library.OPTION_TYPE_MAPPER));
            if (libOptions.containsKey(Library.OPTION_STRUCTURE_ALIGNMENT))
                alignments.put(cls, libOptions.get(Library.OPTION_STRUCTURE_ALIGNMENT));
            if (proxy != null) {
                libraries.put(cls, new WeakReference(proxy));
            }

            // If it's a direct mapping, AND implements a Library interface,
            // cache the library interface as well, so that any nested
            // classes get the appropriate associated options
            if (!cls.isInterface()
                && Library.class.isAssignableFrom(cls)) {
                Class ifaces[] = cls.getInterfaces();
                for (int i=0;i < ifaces.length;i++) {
                    if (Library.class.isAssignableFrom(ifaces[i])) {
                        cacheOptions(ifaces[i], libOptions, proxy);
                        break;
                    }
                }
            }
        }
    }

    private static native long registerMethod(Class cls,
                                              String name,
                                              String signature,
                                              int[] conversions,
                                              long[] closure_arg_types,
                                              long[] arg_types,
                                              int rconversion,
                                              long closure_rtype,
                                              long rtype,
                                              Class rclass,
                                              long fptr,
                                              int callingConvention,
                                              boolean throwLastError,
                                              ToNativeConverter[] toNative,
                                              FromNativeConverter fromNative);
    

    // Called from native code
    private static NativeMapped fromNative(Class cls, Object value) {
        // NOTE: technically should be either CallbackParameterContext or
        // FunctionResultContext 
        return (NativeMapped)NativeMappedConverter.getInstance(cls).fromNative(value, new FromNativeContext(cls));
    }
    // Called from native code
    private static Class nativeType(Class cls) {
        return NativeMappedConverter.getInstance(cls).nativeType();
    }
    // Called from native code
    private static Object toNative(ToNativeConverter cvt, Object o) {
        // NOTE: technically should be either CallbackResultContext or
        // FunctionParameterContext
        return cvt.toNative(o, new ToNativeContext());
    }
    // Called from native code
    private static Object fromNative(FromNativeConverter cvt, Object o, Class cls) {
        // NOTE: technically should be FunctionResultContext 
        return cvt.fromNative(o, new FromNativeContext(cls));
    }

    public static native long ffi_prep_cif(int abi, int nargs, long ffi_return_type, long ffi_types);
    public static native void ffi_call(long cif, long fptr, long resp, long args);
    public static native long ffi_prep_closure(long cif, ffi_callback cb);
    public static native void ffi_free_closure(long closure);
    
    /** Returns the size (calculated by libffi) of the given type. */
    static native int initialize_ffi_type(long type_info);

    public interface ffi_callback {
        void invoke(long cif, long resp, long argp);
    }

    /** Prints JNA library details to the console. */
    public static void main(String[] args) {
        final String DEFAULT_TITLE = "Java Native Access (JNA)";
        final String DEFAULT_VERSION = VERSION;
        final String DEFAULT_BUILD = VERSION + " (package information missing)";
        Package pkg = Native.class.getPackage();
        String title = pkg.getSpecificationTitle();
        if (title == null) title = DEFAULT_TITLE;
        String version = pkg.getSpecificationVersion();
        if (version == null) version = DEFAULT_VERSION;
        title += " API Version " + version;
        System.out.println(title);
        version = pkg.getImplementationVersion();
        if (version == null) version = DEFAULT_BUILD;
        System.out.println("Version: " + version);
        System.out.println(" Native: " + getNativeVersion() + " ("
                           + getAPIChecksum() + ")");
        System.exit(0);
    }
} public class Pointer {

    /** Size of a native pointer, in bytes. */
    public static final int SIZE;
    
    static {
        // Force load of native library
        if ((SIZE = Native.POINTER_SIZE) == 0) {
            throw new Error("Native library not initialized");
        }
    }
    
    /** Convenience constant, same as <code>null</code>. */
    public static final Pointer NULL = null;
    
    /** Convenience constant, equivalent to <code>(void*)CONSTANT</code>. */
    public static final Pointer createConstant(long peer) {
        return new Opaque(peer);
    }
    
    /** Convenience constant, equivalent to <code>(void*)CONSTANT</code>.
        This version will avoid setting any of the high bits on 64-bit
        systems.
     */
    public static final Pointer createConstant(int peer) {
        return new Opaque((long)peer & 0xFFFFFFFF);
    }
    
    /** Pointer value of the real native pointer. Use long to be 64-bit safe. 
     */
    protected long peer;

    /** Derived class must assign peer pointer value. */
    Pointer() { }
    
    /** Create from native pointer.  Don't use this unless you know what
     * you're doing.
     */
    public Pointer(long peer) {
        this.peer = peer;
    }

    public Pointer share(long offset) {
        return share(offset, 0);
    }
    
    /** Provide a view of this pointer with a different peer base. */
    public Pointer share(long offset, long sz) {
        if (offset == 0) return this;
        return new Pointer(peer + offset);
    }

    /** Zero memory for the given number of bytes. */
    public void clear(long size) {
        setMemory(0, size, (byte)0);
    }

    /**
     * Compares this <code>Pointer</code> to the specified object.
     *
     * @param	o 
     *			A <code>Pointer</code> instance
     * @return	True if the other object is a <code>Pointer</code>, 
     *          and the C pointers being pointed to by these objects are also
     *			equal. Returns false otherwise.
     */
    public boolean equals(Object o) {
        if (o == this) return true;
        if (o == null) return false;
        return o instanceof Pointer && ((Pointer)o).peer == peer;
    }

    /**
     * Returns a hashcode for the native pointer represented by this
     * <code>Pointer</code> object
     *
     * @return	A hash code value for the represented native pointer
     */
    public int hashCode() {
        return (int)((peer >>> 32) + (peer & 0xFFFFFFFF));
    }


    //////////////////////////////////////////////////////////////////////////
    // Raw read methods
    //////////////////////////////////////////////////////////////////////////

    /** Returns the offset of the given value in memory from the given offset,
     * or -1 if the value is not found.
     */
    public long indexOf(long offset, byte value) {
        return _indexOf(peer + offset, value);
    }
    
    private static native long _indexOf(long addr, byte value);

    /**
     * Indirect the native pointer, copying <em>from</em> memory pointed to by
     * native pointer, into the specified array.
     *
     * @param offset byte offset from pointer into which data is copied
     * @param buf    <code>byte</code> array into which data is copied
     * @param index  array index from which to start copying
     * @param length number of elements from native pointer that must be copied
     */
    public void read(long offset, byte[] buf, int index, int length) {
        _read(peer + offset, buf, index, length);
    }

    private static native void _read(long addr, byte[] buf, int index, int length);


    /**
     * Indirect the native pointer, copying <em>from</em> memory pointed to by
     * native pointer, into the specified array.
     *
     * @param offset byte offset from pointer from which data is copied
     * @param buf    <code>short</code> array into which data is copied
     * @param index  array index to which data is copied
     * @param length number of elements from native pointer that must be copied
     */
    public void read(long offset, short[] buf, int index, int length) {
        _read(peer + offset, buf, index, length);
    }

    private static native void _read(long addr, short[] buf, int index, int length);


    /**
     * Indirect the native pointer, copying <em>from</em> memory pointed to by
     * native pointer, into the specified array.
     *
     * @param offset byte offset from pointer from which data is copied
     * @param buf    <code>char</code> array into which data is copied
     * @param index  array index to which data is copied
     * @param length number of elements from native pointer that must be copied
     */
    public void read(long offset, char[] buf, int index, int length) {
        _read(peer + offset, buf, index, length);
    }
    
    private static native void _read(long addr, char[] buf, int index, int length);


    /**
     * Indirect the native pointer, copying <em>from</em> memory pointed to by
     * native pointer, into the specified array.
     *
     * @param offset byte offset from pointer from which data is copied
     * @param buf    <code>int</code> array into which data is copied
     * @param index  array index to which data is copied
     * @param length number of elements from native pointer that must be copied
     */
    public void read(long offset, int[] buf, int index, int length) {
        _read(peer + offset, buf, index, length);
    }

    private static native void _read(long addr, int[] buf, int index, int length);

    /**
     * Indirect the native pointer, copying <em>from</em> memory pointed to by
     * native pointer, into the specified array.
     *
     * @param offset byte offset from pointer from which data is copied
     * @param buf    <code>long</code> array into which data is copied
     * @param index  array index to which data is copied
     * @param length number of elements from native pointer that must be copied
     */
    public void read(long offset, long[] buf, int index, int length) {
        _read(peer + offset, buf, index, length);
    }

    private static native void _read(long addr, long[] buf, int index, int length);

    /**
     * Indirect the native pointer, copying <em>from</em> memory pointed to by
     * native pointer, into the specified array.
     *
     * @param offset byte offset from pointer from which data is copied
     * @param buf    <code>float</code> array into which data is copied
     * @param index  array index to which data is copied
     * @param length number of elements from native pointer that must be copied
     */
    public void read(long offset, float[] buf, int index, int length) {
        _read(peer + offset, buf, index, length);
    }

    private static native void _read(long addr, float[] buf, int index, int length);

    /**
     * Indirect the native pointer, copying <em>from</em> memory pointed to by
     * native pointer, into the specified array.
     *
     * @param offset byte offset from pointer from which data is copied
     * @param buf    <code>double</code> array into which data is copied
     * @param index  array index to which data is copied
     * @param length number of elements from native pointer that must be copied
     */
    public void read(long offset, double[] buf, int index, int length) {
        _read(peer + offset, buf, index, length);
    }

    private static native void _read(long addr, double[] buf, int index, int length);

    /**
     * Indirect the native pointer, copying <em>from</em> memory pointed to by
     * native pointer, into the specified array.
     *
     * @param offset byte offset from pointer from which data is copied
     * @param buf    {@link Pointer} array into which data is copied
     * @param index  array index to which data is copied
     * @param length number of elements from native pointer that must be copied
     */
    public void read(long offset, Pointer[] buf, int index, int length) {
        for (int i=0;i < length;i++) {
            Pointer p = getPointer(offset + i*Pointer.SIZE);
            Pointer oldp = buf[i+index];
            // Avoid replacing the original pointer if it hasn't changed
            if (oldp == null || p == null || p.peer != oldp.peer) {
                buf[i+index] = p;
            }
        }
    }


    //////////////////////////////////////////////////////////////////////////
    // Raw write methods
    //////////////////////////////////////////////////////////////////////////

    /**
     * Indirect the native pointer, copying <em>into</em> memory pointed to by
     * native pointer, from the specified array.
     *
     * @param offset byte offset from pointer into which data is copied
     * @param buf    <code>byte</code> array from which to copy
     * @param index  array index from which to start copying
     * @param length number of elements from <code>buf</code> that must be
     *               copied
     */
    public void write(long offset, byte[] buf, int index, int length) {
        _write(peer + offset, buf, index, length);
    }

    private static native void _write(long addr, byte[] buf, int index, int length);


    /**
     * Indirect the native pointer, copying <em>into</em> memory pointed to by
     * native pointer, from the specified array.
     *
     * @param offset byte offset from pointer into which data is copied
     * @param buf    <code>short</code> array from which to copy
     * @param index  array index from which to start copying
     * @param length number of elements from <code>buf</code> that must be
     *               copied
     */
    public void write(long offset, short[] buf, int index, int length) {
        _write(peer + offset, buf, index, length);
    }

    private static native void _write(long addr, short[] buf, int index, int length);

    /**
     * Indirect the native pointer, copying <em>into</em> memory pointed to by
     * native pointer, from the specified array.
     *
     * @param offset byte offset from pointer into which data is copied
     * @param buf    <code>char</code> array from which to copy
     * @param index  array index from which to start copying
     * @param length number of elements from <code>buf</code> that must be
     *               copied
     */
    public void write(long offset, char[] buf, int index, int length) {
        _write(peer + offset, buf, index, length);
    }

    private static native void _write(long addr, char[] buf, int index, int length);

    /**
     * Indirect the native pointer, copying <em>into</em> memory pointed to by
     * native pointer, from the specified array.
     *
     * @param offset byte offset from pointer into which data is copied
     * @param buf    <code>int</code> array from which to copy
     * @param index  array index from which to start copying
     * @param length number of elements from <code>buf</code> that must be
     *               copied
     */
    public void write(long offset, int[] buf, int index, int length) {
        _write(peer + offset, buf, index, length);
    }

    private static native void _write(long addr, int[] buf, int index, int length);

    /**
     * Indirect the native pointer, copying <em>into</em> memory pointed to by 
     * native pointer, from the specified array.
     *
     * @param offset byte offset from pointer into which data is copied
     * @param buf    <code>long</code> array from which to copy
     * @param index  array index from which to start copying
     * @param length number of elements from <code>buf</code> that must be
     *               copied
     */
    public void write(long offset, long[] buf, int index, int length) {
        _write(peer + offset, buf, index, length);
    }

    private static native void _write(long addr, long[] buf, int index, int length);

    /**
     * Indirect the native pointer, copying <em>into</em> memory pointed to by
     * native pointer, from the specified array.
     *
     * @param offset byte offset from pointer into which data is copied
     * @param buf    <code>float</code> array from which to copy
     * @param index  array index from which to start copying
     * @param length number of elements from <code>buf</code> that must be
     *               copied
     */
    public void write(long offset, float[] buf, int index, int length) {
        _write(peer + offset, buf, index, length);
    }

    private static native void _write(long addr, float[] buf, int index, int length);

    /**
     * Indirect the native pointer, copying <em>into</em> memory pointed to by
     * native pointer, from the specified array.
     *
     * @param offset byte offset from pointer into which data is copied
     * @param buf    <code>double</code> array from which to copy
     * @param index  array index from which to start copying
     * @param length number of elements from <code>buf</code> that must be
     *               copied
     */
    public void write(long offset, double[] buf, int index, int length) {
        _write(peer + offset, buf, index, length);
    }

    private static native void _write(long addr, double[] buf, int index, int length);

    /** Write the given array of Pointer to native memory.
     * @param bOff   byte offset from pointer into which data is copied
     * @param buf    <code>Pointer</code> array from which to copy
     * @param index  array index from which to start copying
     * @param length number of elements from <code>buf</code> that must be
     *               copied
    */
    public void write(long bOff, Pointer[] buf, int index, int length) {
        for (int i=0;i < length;i++) {
            setPointer(bOff + i * Pointer.SIZE, buf[index + i]);
        }
    }

    //////////////////////////////////////////////////////////////////////////
    // Java type read methods
    //////////////////////////////////////////////////////////////////////////

    Object getValue(long offset, Class type, Object currentValue) {

        Object result = null;
        if (Structure.class.isAssignableFrom(type)) {
            Structure s = (Structure)currentValue;
            if (Structure.ByReference.class.isAssignableFrom(type)) {
                s = Structure.updateStructureByReference(type, s, getPointer(offset));
            }
            else {
                s.useMemory(this, (int)offset);
                s.read();
            }
            result = s;
        }
        else if (type == boolean.class || type == Boolean.class) {
            result = Function.valueOf(getInt(offset) != 0);
        }
        else if (type == byte.class || type == Byte.class) {
            result = new Byte(getByte(offset));
        }
        else if (type == short.class || type == Short.class) {
            result = new Short(getShort(offset));
        }
        else if (type == char.class || type == Character.class) {
            result = new Character(getChar(offset));
        }
        else if (type == int.class || type == Integer.class) {
            result = new Integer(getInt(offset));
        }
        else if (type == long.class || type == Long.class) {
            result = new Long(getLong(offset));
        }
        else if (type == float.class || type == Float.class) {
            result=new Float(getFloat(offset));
        }
        else if (type == double.class || type == Double.class) {
            result = new Double(getDouble(offset));
        }
        else if (Pointer.class.isAssignableFrom(type)) {
            Pointer p = getPointer(offset);
            if (p != null) {
                Pointer oldp = currentValue instanceof Pointer
                    ? (Pointer)currentValue : null;
                if (oldp == null || p.peer != oldp.peer)
                    result = p;
                else
                    result = oldp;
            }
        }
        else if (type == String.class) {
            Pointer p = getPointer(offset);
            result = p != null ? p.getString(0) : null;
        }
        else if (type == WString.class) {
            Pointer p = getPointer(offset);
            result = p != null ? new WString(p.getString(0, true)) : null;
        }
        else if (Callback.class.isAssignableFrom(type)) {
            // Overwrite the Java memory if the native pointer is a different
            // function pointer.
            Pointer fp = getPointer(offset);
            if (fp == null) {
                result = null;
            }
            else {
                Callback cb = (Callback)currentValue;
                Pointer oldfp = CallbackReference.getFunctionPointer(cb);
                if (!fp.equals(oldfp)) {
                    cb = CallbackReference.getCallback(type, fp);
                }
                result = cb;
            }
        }
        else if (Buffer.class.isAssignableFrom(type)) {
            Pointer bp = getPointer(offset);
            if (bp == null) {
                result = null;
            }
            else {
                Pointer oldbp = currentValue == null ? null
                    : Native.getDirectBufferPointer((Buffer)currentValue);
                if (oldbp == null || !oldbp.equals(bp)) {
                    throw new IllegalStateException("Can't autogenerate a direct buffer on memory read");
                }
                result = currentValue;
            }
        }
        else if (NativeMapped.class.isAssignableFrom(type)) {
            NativeMapped nm = (NativeMapped)currentValue;
            if (nm != null) {
                Object value = getValue(offset, nm.nativeType(), null);
                result = nm.fromNative(value, new FromNativeContext(type));
            }
            else {
                NativeMappedConverter tc = NativeMappedConverter.getInstance(type);
                Object value = getValue(offset, tc.nativeType(), null);
                result = tc.fromNative(value, new FromNativeContext(type));
            }
        }
        else if (type.isArray()) {
            result = currentValue;
            if (result == null) {
                throw new IllegalStateException("Need an initialized array");
            }
            getArrayValue(offset, result, type.getComponentType());
        }
        else {
            throw new IllegalArgumentException("Reading \""
                                               + type + "\" from memory is not supported");
        }
        return result;
    }

    private void getArrayValue(long offset, Object o, Class cls) {
        int length = 0;
        length = Array.getLength(o);
        Object result = o;
        
        if (cls == byte.class) {
            read(offset, (byte[])result, 0, length);
        }
        else if (cls == short.class) {
            read(offset, (short[])result, 0, length);
        }
        else if (cls == char.class) {
            read(offset, (char[])result, 0, length);
        }
        else if (cls == int.class) {
            read(offset, (int[])result, 0, length);
        }
        else if (cls == long.class) {
            read(offset, (long[])result, 0, length);
        }
        else if (cls == float.class) {
            read(offset, (float[])result, 0, length);
        }
        else if (cls == double.class) {
            read(offset, (double[])result, 0, length);
        }
        else if (Pointer.class.isAssignableFrom(cls)) {
            read(offset, (Pointer[])result, 0, length);
        }
        else if (Structure.class.isAssignableFrom(cls)) {
            Structure[] sarray = (Structure[])result;
            if (Structure.ByReference.class.isAssignableFrom(cls)) {
                Pointer[] parray = getPointerArray(offset, sarray.length);
                for (int i=0;i < sarray.length;i++) {
                    sarray[i] = Structure.updateStructureByReference(cls, sarray[i], parray[i]);
                }
            }
            else {
                for (int i=0;i < sarray.length;i++) {
                    if (sarray[i] == null) {
                        sarray[i] = Structure.newInstance(cls);
                    }
                    sarray[i].useMemory(this, (int)(offset + i * sarray[i].size()));
                    sarray[i].read();
                }
            }
        }
        else if (NativeMapped.class.isAssignableFrom(cls)) {
            NativeMapped[] array = (NativeMapped[])result;
            NativeMappedConverter tc = NativeMappedConverter.getInstance(cls);
            int size = Native.getNativeSize(result.getClass(), result) / array.length;
            for (int i=0;i < array.length;i++) {
                Object value = getValue(offset + size*i, tc.nativeType(), array[i]);
                array[i] = (NativeMapped)tc.fromNative(value, new FromNativeContext(cls));
            }
        }
        else {
            throw new IllegalArgumentException("Reading array of "
                                               + cls
                                               + " from memory not supported");
        }
    }

    /**
     * Indirect the native pointer as a pointer to <code>byte</code>.  This is
     * equivalent to the expression
     * <code>*((jbyte *)((char *)Pointer + offset))</code>.
     *
     * @param offset offset from pointer to perform the indirection
     * @return the <code>byte</code> value being pointed to
     */
    public byte getByte(long offset) {
        return _getByte(peer + offset);
    }

    private static native byte _getByte(long addr);

    /**
     * Indirect the native pointer as a pointer to <code>wchar_t</code>.  This
     * is equivalent to the expression
     * <code>*((wchar_t*)((char *)Pointer + offset))</code>.
     *
     * @param offset offset from pointer to perform the indirection
     * @return the <code>wchar_t</code> value being pointed to
     */
    public char getChar(long offset) {
        return _getChar(peer + offset);
    }

    private static native char _getChar(long addr);

    /**
     * Indirect the native pointer as a pointer to <code>short</code>.  This is
     * equivalent to the expression
     * <code>*((jshort *)((char *)Pointer + offset))</code>.
     *
     * @param offset byte offset from pointer to perform the indirection
     * @return the <code>short</code> value being pointed to
     */
    public short getShort(long offset) {
        return _getShort(peer + offset);
    }

    private static native short _getShort(long addr);

    /**
     * Indirect the native pointer as a pointer to <code>int</code>.  This is
     * equivalent to the expression
     * <code>*((jint *)((char *)Pointer + offset))</code>.
     *
     * @param offset byte offset from pointer to perform the indirection
     * @return the <code>int</code> value being pointed to
     */
    public int getInt(long offset) {
        return _getInt(peer + offset);
    }

    private static native int _getInt(long addr);

    /**
     * Indirect the native pointer as a pointer to <code>long</code>.  This is
     * equivalent to the expression
     * <code>*((jlong *)((char *)Pointer + offset))</code>.
     *
     * @param offset byte offset from pointer to perform the indirection
     * @return the <code>long</code> value being pointed to
     */
    public long getLong(long offset) {
        return _getLong(peer + offset);
    }

    private static native long _getLong(long addr);

    /**
     * Indirect the native pointer as a pointer to <code>long</code>.  This is
     * equivalent to the expression
     * <code>*((long *)((char *)Pointer + offset))</code>.
     *
     * @param offset byte offset from pointer to perform the indirection
     * @return the <code>long</code> value being pointed to
     */
    public NativeLong getNativeLong(long offset) {
        return new NativeLong(NativeLong.SIZE == 8 ? getLong(offset) : getInt(offset));
    }

    /**
     * Indirect the native pointer as a pointer to <code>float</code>.  This is
     * equivalent to the expression
     * <code>*((jfloat *)((char *)Pointer + offset))</code>.
     *
     * @param offset byte offset from pointer to perform the indirection
     * @return the <code>float</code> value being pointed to
     */
    public float getFloat(long offset) {
        return _getFloat(peer + offset);
    }

    private native float _getFloat(long addr);

    /**
     * Indirect the native pointer as a pointer to <code>double</code>.  This
     * is equivalent to the expression
     * <code>*((jdouble *)((char *)Pointer + offset))</code>.
     *
     * @param offset byte offset from pointer to perform the indirection
     * @return the <code>double</code> value being pointed to
     */
    public double getDouble(long offset) {
        return _getDouble(peer + offset);
    }

    private static native double _getDouble(long addr);

    /**
     * Indirect the native pointer as a pointer to pointer.  This is equivalent
     * to the expression
     * <code>*((void **)((char *)Pointer + offset))</code>.
     *
     * @param offset byte offset from pointer to perform the indirection
     * @return a {@link Pointer} equivalent of the pointer value
     * being pointed to, or <code>null</code> if the pointer value is
     * <code>NULL</code>;
     */
    public Pointer getPointer(long offset) {
        return _getPointer(peer + offset);
    }

    private static native Pointer _getPointer(long addr);

    /**
     * Get a ByteBuffer mapped to the memory pointed to by the pointer,
     * ensuring the buffer uses native byte order.
     *
     * @param offset byte offset from pointer to start the buffer
     * @param length Length of ByteBuffer
     * @return a direct ByteBuffer that accesses the memory being pointed to,
     */
    public ByteBuffer getByteBuffer(long offset, long length) {
        return _getDirectByteBuffer(peer + offset, length).order(ByteOrder.nativeOrder());
    }

    /**
     * Get a direct ByteBuffer mapped to the memory pointed to by the pointer.
     *
     * @param addr byte offset from pointer to start the buffer
     * @param length Length of ByteBuffer
     * @return a direct ByteBuffer that accesses the memory being pointed to, 
     */
    private native ByteBuffer _getDirectByteBuffer(long addr, long length);

    /**
     * Copy native memory to a Java String.  If <code>wide</code> is true,
     * access the memory as an array of <code>wchar_t</code>, otherwise 
     * as an array of <code>char</code>, using the default platform encoding.
     *
     * @param offset byte offset from pointer to obtain the native string
v     * @param wide whether to convert from a wide or standard C string
     * @return the <code>String</code> value being pointed to 
     */
    public String getString(long offset, boolean wide) {
        return _getString(peer + offset, wide);
    }
    
    private static native String _getString(long addr, boolean wide);

    /**
     * Copy native memory to a Java String.  If the system property 
     * <code>jna.encoding</code> is set, uses it as the native charset
     * when decoding the value, otherwise falls back to the default platform
     * encoding.
     *
     * @param offset byte offset from pointer to obtain the native string
     * @return the <code>String</code> value being pointed to 
     */
    public String getString(long offset) {
        String encoding = System.getProperty("jna.encoding");
        if (encoding != null) {
            long len = indexOf(offset, (byte)0);
            if (len != -1) {
                if (len > Integer.MAX_VALUE) {
                    throw new OutOfMemoryError("String exceeds maximum length: " + len);
                }
                byte[] data = getByteArray(offset, (int)len);
                try {
                    return new String(data, encoding);
                }
                catch(UnsupportedEncodingException e) { 
                }
            }
        }
        return getString(offset, false);
    }

    public byte[] getByteArray(long offset, int arraySize) {
        byte[] buf = new byte[arraySize];
        read(offset, buf, 0, arraySize);
        return buf;
    }
    
    public char[] getCharArray(long offset, int arraySize) {
        char[] buf = new char[arraySize];
        read(offset, buf, 0, arraySize);
        return buf;
    }

    public short[] getShortArray(long offset, int arraySize) {
        short[] buf = new short[arraySize];
        read(offset, buf, 0, arraySize);
        return buf;
    }

    public int[] getIntArray(long offset, int arraySize) {
        int[] buf = new int[arraySize];
        read(offset, buf, 0, arraySize);
        return buf;
    }

    public long[] getLongArray(long offset, int arraySize) {
        long[] buf = new long[arraySize];
        read(offset, buf, 0, arraySize);
        return buf;
    }

    public float[] getFloatArray(long offset, int arraySize) {
        float[] buf = new float[arraySize];
        read(offset, buf, 0, arraySize);
        return buf;
    }

    public double[] getDoubleArray(long offset, int arraySize) {
        double[] buf = new double[arraySize];
        read(offset, buf, 0, arraySize);
        return buf;
    }

    /** Returns an array of {@link Pointer}.  The array length is
     * determined by a NULL-valued terminating element.
     */
    public Pointer[] getPointerArray(long base) {
        List array = new ArrayList();
        int offset = 0;
        Pointer p = getPointer(base);
        while (p != null) {
            array.add(p);
            offset += Pointer.SIZE;
            p = getPointer(base + offset);
        }
        return (Pointer[])array.toArray(new Pointer[array.size()]);
    }

    /** Returns an array of {@link Pointer} of the requested size. */
    public Pointer[] getPointerArray(long offset, int arraySize) {
        Pointer[] buf = new Pointer[arraySize];
        read(offset, buf, 0, arraySize);
        return buf;
    }

    /** Returns an array of <code>String</code> based on a native array
     * of <code>char *</code>.  The array length is determined by a
     * NULL-valued terminating element. 
     */
    public String[] getStringArray(long base) {
        return getStringArray(base, -1, false);
    }

    /** Returns an array of <code>String</code> based on a native array
     * of <code>char *</code>, using the given array length. 
     */
    public String[] getStringArray(long base, int length) {
        return getStringArray(base, length, false);
    }

    /** Returns an array of <code>String</code> based on a native array
     * of <code>char*</code> or <code>wchar_t*</code> based on the
     * <code>wide</code> parameter.  The array length is determined by a
     * NULL-valued terminating element. 
     */
    public String[] getStringArray(long base, boolean wide) {
        return getStringArray(base, -1, wide);
    }

    /** Returns an array of <code>String</code> based on a native array
     * of <code>char*</code> or <code>wchar_t*</code> based on the
     * <code>wide</code> parameter, using the given array length.
     */
    public String[] getStringArray(long base, int length, boolean wide) {
    
        List strings = new ArrayList();
        Pointer p;
        int offset = 0;
        if (length != -1) {
            p = getPointer(base + offset);
            int count = 0;
            while (count++ < length) {
                String s = p == null ? null : p.getString(0, wide);
                strings.add(s);
                if (count < length) {
                    offset += SIZE;
                    p = getPointer(base + offset);
                }
            }
        }
        else {
            while ((p = getPointer(base + offset)) != null) {
                String s = p == null ? null : p.getString(0, wide);
                strings.add(s);
                offset += SIZE;
            }
        }
        return (String[])strings.toArray(new String[strings.size()]);
    }

    //////////////////////////////////////////////////////////////////////////
    // Java type write methods
    //////////////////////////////////////////////////////////////////////////

    void setValue(long offset, Object value, Class type) {

        // Set the value at the offset according to its type
        if (type == boolean.class || type == Boolean.class) {
            setInt(offset, Boolean.TRUE.equals(value) ? -1 : 0);
        }
        else if (type == byte.class || type == Byte.class) {
            setByte(offset, value == null ? 0 : ((Byte)value).byteValue());
        }
        else if (type == short.class || type == Short.class) {
            setShort(offset, value == null ? 0 : ((Short)value).shortValue());
        }
        else if (type == char.class || type == Character.class) {
            setChar(offset, value == null ? 0 : ((Character)value).charValue());
        }
        else if (type == int.class || type == Integer.class) {
            setInt(offset, value == null ? 0 : ((Integer)value).intValue());
        }
        else if (type == long.class || type == Long.class) {
            setLong(offset, value == null ? 0 : ((Long)value).longValue());
        }
        else if (type == float.class || type == Float.class) {
            setFloat(offset, value == null ? 0f : ((Float)value).floatValue());
        }
        else if (type == double.class || type == Double.class) {
            setDouble(offset, value == null ? 0.0 : ((Double)value).doubleValue());
        }
        else if (type == Pointer.class) {
            setPointer(offset, (Pointer)value);
        }
        else if (type == String.class) {
            setPointer(offset, (Pointer)value);
        }
        else if (type == WString.class) {
            setPointer(offset, (Pointer)value);
        }
        else if (Structure.class.isAssignableFrom(type)) {
            Structure s = (Structure)value;
            if (Structure.ByReference.class.isAssignableFrom(type)) {
                setPointer(offset, s == null ? null : s.getPointer());
                if (s != null) {
                    s.autoWrite();
                }
            }
            else {
                s.useMemory(this, (int)offset);
                s.write();
            }
        }
        else if (Callback.class.isAssignableFrom(type)) {
            setPointer(offset, CallbackReference.getFunctionPointer((Callback)value));
        }
        else if (Buffer.class.isAssignableFrom(type)) {
            Pointer p = value == null ? null
                : Native.getDirectBufferPointer((Buffer)value);
            setPointer(offset, p);
        }
        else if (NativeMapped.class.isAssignableFrom(type)) {
            NativeMappedConverter tc = NativeMappedConverter.getInstance(type);
            Class nativeType = tc.nativeType();
            setValue(offset, tc.toNative(value, new ToNativeContext()), nativeType);
        }
        else if (type.isArray()) {
            setArrayValue(offset, value, type.getComponentType());
        }
        else {
            throw new IllegalArgumentException("Writing " + type + " to memory is not supported");
        }
    }

    private void setArrayValue(long offset, Object value, Class cls) {
        if (cls == byte.class) {
            byte[] buf = (byte[])value;
            write(offset, buf, 0, buf.length);
        }
        else if (cls == short.class) {
            short[] buf = (short[])value;
            write(offset, buf, 0, buf.length);
        }
        else if (cls == char.class) {
            char[] buf = (char[])value;
            write(offset, buf, 0, buf.length);
        }
        else if (cls == int.class) {
            int[] buf = (int[])value;
            write(offset, buf, 0, buf.length);
        }
        else if (cls == long.class) {
            long[] buf = (long[])value;
            write(offset, buf, 0, buf.length);
        }
        else if (cls == float.class) {
            float[] buf = (float[])value;
            write(offset, buf, 0, buf.length);
        }
        else if (cls == double.class) {
            double[] buf = (double[])value;
            write(offset, buf, 0, buf.length);
        }
        else if (Pointer.class.isAssignableFrom(cls)) {
            Pointer[] buf = (Pointer[])value;
            write(offset, buf, 0, buf.length);
        }
        else if (Structure.class.isAssignableFrom(cls)) {
            Structure[] sbuf = (Structure[])value;
            if (Structure.ByReference.class.isAssignableFrom(cls)) {
                Pointer[] buf = new Pointer[sbuf.length];
                for (int i=0;i < sbuf.length;i++) {
                    buf[i] = sbuf[i] == null ? null : sbuf[i].getPointer();
                    sbuf[i].write();
                }
                write(offset, buf, 0, buf.length);
            }
            else {
                for (int i=0;i < sbuf.length;i++) {
                    if (sbuf[i] == null) {
                        sbuf[i] = Structure.newInstance(cls);
                    }
                    sbuf[i].useMemory(this, (int)(offset + i * sbuf[i].size()));
                    sbuf[i].write();
                }
            }
        }
        else if (NativeMapped.class.isAssignableFrom(cls)) {
            NativeMapped[] buf = (NativeMapped[])value;
            NativeMappedConverter tc = NativeMappedConverter.getInstance(cls);
            Class nativeType = tc.nativeType();
            int size = Native.getNativeSize(value.getClass(), value) / buf.length;
            for (int i=0;i < buf.length;i++) {
                Object element = tc.toNative(buf[i], new ToNativeContext());
                setValue(offset + i*size, element, nativeType);
            }
        }
        else {
            throw new IllegalArgumentException("Writing array of "
                                               + cls + " to memory not supported");
        }
    }

    /** Write <code>value</code> to the requested bank of memory. 
     * @param offset byte offset from pointer to start
     * @param length number of bytes to write
     * @param value value to be written
     */
    public void setMemory(long offset, long length, byte value) {
        _setMemory(peer + offset, length, value);
    }
    
    static native void _setMemory(long addr, long length, byte value);
    
    /**
     * Set <code>value</code> at location being pointed to. This is equivalent
     * to the expression
     * <code>*((jbyte *)((char *)Pointer + offset)) = value</code>.
     *
     * @param offset byte offset from pointer at which <code>value</code>
     *		     must be set
     * @param value <code>byte</code> value to set
     */
    public void setByte(long offset, byte value) {
        _setByte(peer + offset, value);
    }

    private static native void _setByte(long addr, byte value);

    /**
     * Set <code>value</code> at location being pointed to. This is equivalent
     * to the expression
     * <code>*((jshort *)((char *)Pointer + offset)) = value</code>.
     *
     * @param offset byte offset from pointer at which <code>value</code>
     *		     must be set
     * @param value <code>short</code> value to set
     */
    public void setShort(long offset, short value) {
        _setShort(peer + offset, value);
    }
    
    private static native void _setShort(long addr, short value);

    /**
     * Set <code>value</code> at location being pointed to. This is equivalent
     * to the expression
     * <code>*((wchar_t *)((char *)Pointer + offset)) = value</code>.
     *
     * @param offset byte offset from pointer at which <code>value</code>
     *               must be set
     * @param value <code>char</code> value to set
     */
    public void setChar(long offset, char value) {
        _setChar(peer + offset, value);
    }
    
    private static native void _setChar(long addr, char value);

    /**
     * Set <code>value</code> at location being pointed to. This is equivalent
     * to the expression
     * <code>*((jint *)((char *)Pointer + offset)) = value</code>.
     *
     * @param offset byte offset from pointer at which <code>value</code>
     *		     must be set
     * @param value <code>int</code> value to set
     */
    public void setInt(long offset, int value) {
        _setInt(peer + offset, value);
    }

    private static native void _setInt(long addr, int value);

    /**
     * Set <code>value</code> at location being pointed to. This is equivalent
     * to the expression
     * <code>*((jlong *)((char *)Pointer + offset)) = value</code>.
     *
     * @param offset byte offset from pointer at which <code>value</code>
     *               must be set
     * @param value <code>long</code> value to set
     */
    public void setLong(long offset, long value) {
        _setLong(peer + offset, value);
    }
    
    private static native void _setLong(long addr, long value);

    /**
     * Set <code>value</code> at location being pointed to. This is equivalent
     * to the expression
     * <code>*((long *)((char *)Pointer + offset)) = value</code>.
     *
     * @param offset byte offset from pointer at which <code>value</code>
     *               must be set
     * @param value <code>long</code> value to set
     */
    public void setNativeLong(long offset, NativeLong value) {
        if (NativeLong.SIZE == 8) {
            setLong(offset, value.longValue());
        } else {
            setInt(offset, value.intValue());
        }
    }

    /**
     * Set <code>value</code> at location being pointed to. This is equivalent
     * to the expression
     * <code>*((jfloat *)((char *)Pointer + offset)) = value</code>.
     *
     * @param offset byte offset from pointer at which <code>value</code>
     *               must be set
     * @param value <code>float</code> value to set
     */
    public void setFloat(long offset, float value) {
        _setFloat(peer + offset, value);
    }
    
    private static native void _setFloat(long addr, float value);

    /**
     * Set <code>value</code> at location being pointed to. This is equivalent
     * to the expression
     * <code>*((jdouble *)((char *)Pointer + offset)) = value</code>.
     *
     * @param offset byte offset from pointer at which <code>value</code>
     *               must be set
     * @param value <code>double</code> value to set
     */
    public void setDouble(long offset, double value) {
        _setDouble(peer + offset, value);
    }

    private static native void _setDouble(long addr, double value);

    /**
     * Set <code>value</code> at location being pointed to. This is equivalent
     * to the expression 
     * <code>*((void **)((char *)Pointer + offset)) = value</code>.
     *
     * @param offset byte offset from pointer at which <code>value</code> 
     *               must be set
     * @param value <code>Pointer</code> holding the actual pointer value to 
     * set, which may be <code>null</code> to indicate a <code>NULL</code>
     * pointer.
     */
    public void setPointer(long offset, Pointer value) {
        _setPointer(peer + offset, value != null ? value.peer : 0);
    }

    private static native void _setPointer(long addr, long value);
    
    /**
     * Copy string <code>value</code> to the location being pointed to.  Copy
     * each element in <code>value</code>, converted to native encoding, at an
     * <code>offset</code>from the location pointed to by this pointer.
     *
     * @param offset byte offset from pointer at which characters in
     * 		     <code>value</code> must be set
     * @param value  <code>java.lang.String</code> value to set
     * @param wide whether to write the native string as an array of 
     * <code>wchar_t</code>.  If false, writes as a NUL-terminated array of 
     * <code>char</code> using the default platform encoding. 
     */
    public void setString(long offset, String value, boolean wide) {
        _setString(peer + offset, value, wide);
    }
    
    private static native void _setString(long addr, String value, boolean wide);

    /**
     * Copy string <code>value</code> to the location being pointed to.  Copy
     * each element in <code>value</code>, converted to native encoding, at an
     * <code>offset</code>from the location pointed to by this pointer.
     * Uses the value of the system property <code>jna.encoding</code>, if set, 
     * to determine the appropriate native charset in which to encode the value.  
     * If the property is not set, uses the default platform encoding.
     *
     * @param offset byte offset from pointer at which characters in
     *               <code>value</code> must be set
     * @param value  <code>java.lang.String</code> value to set
     */
    public void setString(long offset, String value) {
        byte[] data = Native.getBytes(value);
        write(offset, data, 0, data.length);
        setByte(offset + data.length, (byte)0);
    }
    
    public String toString() {
        return "native@0x" + Long.toHexString(peer);
    }
    
    /** Read the native peer value.  Use with caution. */
    public static long nativeValue(Pointer p) {
        return p.peer;
    }

    /** Set the native peer value.  Use with caution. */
    public static void nativeValue(Pointer p, long value) {
        p.peer = value;
    }

    /** Pointer which disallows all read/write access. */
    private static class Opaque extends Pointer {
        private Opaque(long peer) { super(peer); }
        private final String MSG = "This pointer is opaque: " + this;
        public long indexOf(long offset, byte value) {
            throw new UnsupportedOperationException(MSG);
        }
        public void read(long bOff, byte[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void read(long bOff, char[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void read(long bOff, short[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void read(long bOff, int[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void read(long bOff, long[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void read(long bOff, float[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void read(long bOff, double[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void write(long bOff, byte[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void write(long bOff, char[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void write(long bOff, short[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void write(long bOff, int[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void write(long bOff, long[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void write(long bOff, float[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public void write(long bOff, double[] buf, int index, int length) { 
            throw new UnsupportedOperationException(MSG); 
        }
        public byte getByte(long bOff) {
            throw new UnsupportedOperationException(MSG); 
        }
        public char getChar(long bOff) {
            throw new UnsupportedOperationException(MSG); 
        }
        public short getShort(long bOff) {
            throw new UnsupportedOperationException(MSG); 
        }
        public int getInt(long bOff) {
            throw new UnsupportedOperationException(MSG); 
        }
        public long getLong(long bOff) {
            throw new UnsupportedOperationException(MSG); 
        }
        public float getFloat(long bOff) {
            throw new UnsupportedOperationException(MSG); 
        }
        public double getDouble(long bOff) {
            throw new UnsupportedOperationException(MSG); 
        }
        public Pointer getPointer(long bOff) {
            throw new UnsupportedOperationException(MSG); 
        }
        public String getString(long bOff, boolean wide) {
            throw new UnsupportedOperationException(MSG); 
        }
        public void setByte(long bOff, byte value) {
            throw new UnsupportedOperationException(MSG); 
        }
        public void setChar(long bOff, char value) {
            throw new UnsupportedOperationException(MSG); 
        }
        public void setShort(long bOff, short value) {
            throw new UnsupportedOperationException(MSG); 
        }
        public void setInt(long bOff, int value) {
            throw new UnsupportedOperationException(MSG); 
        }
        public void setLong(long bOff, long value) {
            throw new UnsupportedOperationException(MSG); 
        }
        public void setFloat(long bOff, float value) {
            throw new UnsupportedOperationException(MSG); 
        }
        public void setDouble(long bOff, double value) {
            throw new UnsupportedOperationException(MSG); 
        }
        public void setPointer(long offset, Pointer value) {
            throw new UnsupportedOperationException(MSG); 
        }
        public void setString(long offset, String value, boolean wide) {
            throw new UnsupportedOperationException(MSG); 
        }
        public String toString() {
            return "opaque@0x" + Long.toHexString(peer);
        }
    }
} public abstract class Structure {
    
    /** Tagging interface to indicate the value of an instance of the
     * <code>Structure</code> type is to be used in function invocations rather
     * than its address.  The default behavior is to treat
     * <code>Structure</code> function parameters and return values as by
     * reference, meaning the address of the structure is used.
     */
    public interface ByValue { }
    /** Tagging interface to indicate the address of an instance of the
     * Structure type is to be used within a <code>Structure</code> definition
     * rather than nesting the full Structure contents.  The default behavior
     * is to inline <code>Structure</code> fields.
     */
    public interface ByReference { }

    private static class MemberOrder {
        private static final String[] FIELDS = {
            "first", "second", "middle", "penultimate", "last",
        };
        public int first;
		public int second;
        public int middle;
        public int penultimate;
        public int last;
    }

    private static final boolean REVERSE_FIELDS;
    private static final boolean REQUIRES_FIELD_ORDER;

    static final boolean isPPC;
    static final boolean isSPARC;

    static {
        // Check for predictable field order; IBM and JRockit store fields in
        // reverse order; Excelsior JET requires explicit order
        Field[] fields = MemberOrder.class.getFields();
        List names = new ArrayList();
        for(int i=0;i < fields.length;i++) {
            names.add(fields[i].getName());
        }
        List expected = Arrays.asList(MemberOrder.FIELDS);
        List reversed = new ArrayList(expected);
        Collections.reverse(reversed);

        REVERSE_FIELDS = names.equals(reversed);
        REQUIRES_FIELD_ORDER = !(names.equals(expected) || REVERSE_FIELDS);
        String arch = System.getProperty("os.arch").toLowerCase();
        isPPC = "ppc".equals(arch) || "powerpc".equals(arch);
        isSPARC = "sparc".equals(arch);
    }

    /** Use the platform default alignment. */
    public static final int ALIGN_DEFAULT = 0;
    /** No alignment, place all fields on nearest 1-byte boundary */
    public static final int ALIGN_NONE = 1;
    /** validated for 32-bit x86 linux/gcc; align field size, max 4 bytes */
    public static final int ALIGN_GNUC = 2;
    /** validated for w32/msvc; align on field size */
    public static final int ALIGN_MSVC = 3;

    /** Align to a 2-byte boundary. */
    //public static final int ALIGN_2 = 4; 
    /** Align to a 4-byte boundary. */
    //public static final int ALIGN_4 = 5;
    /** Align to an 8-byte boundary. */
    //public static final int ALIGN_8 = 6;

    private static final int MAX_GNUC_ALIGNMENT = isSPARC ? 8 : Native.LONG_SIZE;
    protected static final int CALCULATE_SIZE = -1;

    // This field is accessed by native code
    private Pointer memory;
    private int size = CALCULATE_SIZE;
    private int alignType;
    private int structAlignment;
    private final Map structFields = new LinkedHashMap();
    // Keep track of java strings which have been converted to C strings
    private final Map nativeStrings = new HashMap();
    private TypeMapper typeMapper;
    // This field is accessed by native code
    private long typeInfo;
    private List fieldOrder;
    private boolean autoRead = true;
    private boolean autoWrite = true;
    private Structure[] array;

    protected Structure() {
        this((Pointer)null);
    }

    protected Structure(TypeMapper mapper) {
        this((Pointer)null, ALIGN_DEFAULT, mapper);
    }

    /** Create a structure cast onto preallocated memory. */
    protected Structure(Pointer p) {
        this(p, ALIGN_DEFAULT);
    }

    protected Structure(Pointer p, int alignment) {
        this(p, alignment, null);
    }

    protected Structure(Pointer p, int alignment, TypeMapper mapper) {
        setAlignType(alignment);
        setTypeMapper(mapper);
        if (p != null) {
            useMemory(p);
        }
        else {
            allocateMemory(CALCULATE_SIZE);
        }
    }

    /** Return all fields in this structure (ordered). */
    Map fields() {
        return structFields;
    }

    /** Change the type mapping for this structure.  May cause the structure
     * to be resized and any existing memory to be reallocated.
     * If <code>null</code>, the default mapper for the
     * defining class will be used.
     */
    protected void setTypeMapper(TypeMapper mapper) {
        if (mapper == null) {
            Class declaring = getClass().getDeclaringClass();
            if (declaring != null) {
                mapper = Native.getTypeMapper(declaring);
            }
        }
        this.typeMapper = mapper;
        this.size = CALCULATE_SIZE;
        if (this.memory instanceof AutoAllocated) {
            this.memory = null;
        }
    }

    /** Change the alignment of this structure.  Re-allocates memory if
     * necessary.  If alignment is {@link #ALIGN_DEFAULT}, the default
     * alignment for the defining class will be used.
     */
    protected void setAlignType(int alignType) {
        if (alignType == ALIGN_DEFAULT) {
            Class declaring = getClass().getDeclaringClass();
            if (declaring != null)
                alignType = Native.getStructureAlignment(declaring);
            if (alignType == ALIGN_DEFAULT) {
                if (Platform.isWindows())
                    alignType = ALIGN_MSVC;
                else
                    alignType = ALIGN_GNUC;
            }
        }
        this.alignType = alignType;
        this.size = CALCULATE_SIZE;
        if (this.memory instanceof AutoAllocated) {
            this.memory = null;
        }
    }

    protected Memory autoAllocate(int size) {
        return new AutoAllocated(size);
    }

    /** Set the memory used by this structure.  This method is used to
     * indicate the given structure is nested within another or otherwise
     * overlaid on some other memory block and thus does not own its own
     * memory.
     */
    protected void useMemory(Pointer m) {
        useMemory(m, 0);
    }

    /** Set the memory used by this structure.  This method is used to
     * indicate the given structure is nested within another or otherwise
     * overlaid on some other memory block and thus does not own its own
     * memory.
     */
    protected void useMemory(Pointer m, int offset) {
        // Invoking size() here is important when this method is invoked
        // from the ctor, to ensure fields are properly scanned and allocated
        try {
            // Set the structure's memory field temporarily to avoid
            // auto-allocating memory in the call to size()
            this.memory = m;
            if (size == CALCULATE_SIZE) {
                size = calculateSize(false);
            }
            if (size != CALCULATE_SIZE) {
                this.memory = m.share(offset, size);
            }
            this.array = null;
        }
        catch(IndexOutOfBoundsException e) {
            throw new IllegalArgumentException("Structure exceeds provided memory bounds");
        }
    }

    protected void ensureAllocated() {
        if (memory == null) {
            allocateMemory();
        }
    }

    /** Attempt to allocate memory if sufficient information is available.
     * Returns whether the operation was successful.
     */
    protected void allocateMemory() {
        allocateMemory(calculateSize(true));
    }

    /** Provided for derived classes to indicate a different
     * size than the default.  Returns whether the operation was successful.
     * Will leave memory untouched if it is non-null and not allocated
     * by this class.
     */
    protected void allocateMemory(int size) {
        if (size == CALCULATE_SIZE) {
            // Analyze the struct, but don't worry if we can't yet do it
            size = calculateSize(false);
        }
        else if (size <= 0) {
            throw new IllegalArgumentException("Structure size must be greater than zero: " + size);
        }
        // May need to defer size calculation if derived class not fully
        // initialized
        if (size != CALCULATE_SIZE) {
            if (this.memory == null 
                || this.memory instanceof AutoAllocated) {
                this.memory = autoAllocate(size);
            }
            this.size = size;
        }
    }

    public int size() {
        ensureAllocated();
        if (size == CALCULATE_SIZE) {
            size = calculateSize(true);
        }
        return size;
    }

    public void clear() {
        memory.clear(size());
    }

    /** Return a {@link Pointer} object to this structure.  Note that if you
     * use the structure's pointer as a function argument, you are responsible
     * for calling {@link #write()} prior to the call and {@link #read()}
     * after the call.  These calls are normally handled automatically by the
     * {@link Function} object when it encounters a {@link Structure} argument
     * or return value.
     */
    public Pointer getPointer() {
        ensureAllocated();
        return memory;
    }

    //////////////////////////////////////////////////////////////////////////
    // Data synchronization methods
    //////////////////////////////////////////////////////////////////////////

    // Keep track of ByReference reads to avoid creating multiple structures
    // mapped to the same address
    private static final ThreadLocal reads = new ThreadLocal() {
        protected synchronized Object initialValue() {
            return new HashMap();
        }
    };

    // Keep track of what is currently being read/written to avoid redundant
    // reads (avoids problems with circular references).
    private static final ThreadLocal busy = new ThreadLocal() {
        /** Avoid using a hash-based implementation since the hash code
            will change if structure field values change.
        */
        class StructureSet extends AbstractCollection implements Set {
            private Structure[] elements;
            private int count;
            private void ensureCapacity(int size) {
                if (elements == null) {
                    elements = new Structure[size*3/2];
                }
                else if (elements.length < size) {
                    Structure[] e = new Structure[size*3/2];
                    System.arraycopy(elements, 0, e, 0, elements.length);
                    elements = e;
                }
            }
            public int size() { return count; }
            public boolean contains(Object o) {
                return indexOf(o) != -1;
            }
            public boolean add(Object o) {
                if (!contains(o)) {
                    ensureCapacity(count+1);
                    elements[count++] = (Structure)o;
                }
                return true;
            }
            private int indexOf(Object o) {
                Structure s1 = (Structure)o;
                for (int i=0;i < count;i++) {
                    Structure s2 = elements[i];
                    if (s1 == s2
                        || (s1.getClass() == s2.getClass()
                            && s1.size() == s2.size()
                            && s1.getPointer().equals(s2.getPointer()))) {
                        return i;
                    }
                }
                return -1;
            }
            public boolean remove(Object o) {
                int idx = indexOf(o);
                if (idx != -1) {
                    if (--count > 0) {
                        elements[idx] = elements[count];
                        elements[count] = null;
                    }
                    return true;
                }
                return false;
            }
            public Iterator iterator() {
                // never actually used
                return null;
            }
        }
        protected synchronized Object initialValue() {
            return new StructureSet();
        }
    };
    static Set busy() {
        return (Set)busy.get();
    }
    static Map reading() {
        return (Map)reads.get();
    }

    /**
     * Reads the fields of the struct from native memory
     */
    public void read() {
        // convenience: allocate memory if it hasn't been already; this
        // allows structures to do field-based initialization of arrays and not
        // have to explicitly call allocateMemory in a ctor
        ensureAllocated();
        // Avoid redundant reads
        if (busy().contains(this)) {
            return;
        }
        busy().add(this);
        if (this instanceof Structure.ByReference) {
            reading().put(getPointer(), this);
        }
        try {
            for (Iterator i=structFields.values().iterator();i.hasNext();) {
                readField((StructField)i.next());
            }
        }
        finally {
            busy().remove(this);
            if (reading().get(getPointer()) == this) {
                reading().remove(getPointer());
            }
        }
    }

    /** Force a read of the given field from native memory.  The Java field
     * will be updated from the current contents of native memory.
     * @return the new field value, after updating
     * @throws IllegalArgumentException if no field exists with the given name
     */
    public Object readField(String name) {
        ensureAllocated();
        StructField f = (StructField)structFields.get(name);
        if (f == null)
            throw new IllegalArgumentException("No such field: " + name);
        return readField(f);
    }

    /** Obtain the value currently in the Java field.  Does not read from
     * memory.
     */
    Object getField(StructField structField) {
        try {
            return structField.field.get(this);
        }
        catch (Exception e) {
            throw new Error("Exception reading field '"
                            + structField.name + "' in " + getClass()
                            + ": " + e);
        }
    }

    void setField(StructField structField, Object value) {
        try {
            structField.field.set(this, value);
        }
        catch(IllegalAccessException e) {
            throw new Error("Unexpectedly unable to write to field '"
                            + structField.name + "' within " + getClass()
                            + ": " + e);
        }
    }

    /** Only keep the original structure if its native address is unchanged.
     * Otherwise replace it with a new object.
     * @param type Structure subclass
     * @param s Original Structure object
     * @param address the native <code>struct *</code>
     * @return Updated <code>Structure.ByReference</code> object
     */
    static Structure updateStructureByReference(Class type, Structure s, Pointer address) {
        if (address == null) {
            s = null;
        }
        else {
            if (s == null || !address.equals(s.getPointer())) {
                Structure s1 = (Structure)reading().get(address);
                if (s1 != null && type.equals(s1.getClass())) {
                    s = s1;
                }
                else {
                    s = newInstance(type);
                    s.useMemory(address);
                }
            }
            s.autoRead();
        }
        return s;
    }

    /** Read the given field and return its value.  The Java field will be
     * updated from the contents of native memory.
     */
    // TODO: make overridable method with calculated native type, offset, etc
    Object readField(StructField structField) {

        // Get the offset of the field
        int offset = structField.offset;

        // Determine the type of the field
        Class fieldType = structField.type;
        FromNativeConverter readConverter = structField.readConverter;
        if (readConverter != null) {
            fieldType = readConverter.nativeType();
        }
        // Get the current value only for types which might need to be preserved
        Object currentValue = (Structure.class.isAssignableFrom(fieldType)
                               || Callback.class.isAssignableFrom(fieldType)
                               || Buffer.class.isAssignableFrom(fieldType)
                               || Pointer.class.isAssignableFrom(fieldType)
                               || fieldType.isArray())
            ? getField(structField) : null;
        Object result = memory.getValue(offset, fieldType, currentValue);
        // TODO: process against current value here
        
        if (readConverter != null) {
            result = readConverter.fromNative(result, structField.context);
        }

        // Update the value on the field
        setField(structField, result);
        return result;
    }

    /**
     * Writes the fields of the struct to native memory
     */
    public void write() {
        // convenience: allocate memory if it hasn't been already; this
        // allows structures to do field-based initialization of arrays and not
        // have to explicitly call allocateMemory in a ctor
        ensureAllocated();

        // Update native FFI type information, if needed
        if (this instanceof ByValue) {
            getTypeInfo();
        }

        // Avoid redundant writes
        if (busy().contains(this)) {
            return;
        }
        busy().add(this);
        try {
            // Write all fields, except those marked 'volatile'
            for (Iterator i=structFields.values().iterator();i.hasNext();) {
                StructField sf = (StructField)i.next();
                if (!sf.isVolatile) {
                    writeField(sf);
                }
            }
        }
        finally {
            busy().remove(this);
        }
    }

    /** Write the given field to native memory.  The current value in the Java
     * field will be translated into native memory.
     * @throws IllegalArgumentException if no field exists with the given name
     */
    public void writeField(String name) {
        ensureAllocated();
        StructField f = (StructField)structFields.get(name);
        if (f == null)
            throw new IllegalArgumentException("No such field: " + name);
        writeField(f);
    }

    /** Write the given field value to the field and native memory.   The
     * given value will be written both to the Java field and the
     * corresponding native memory.
     * @throws IllegalArgumentException if no field exists with the given name
     */
    public void writeField(String name, Object value) {
        ensureAllocated();
        StructField f = (StructField)structFields.get(name);
        if (f == null)
            throw new IllegalArgumentException("No such field: " + name);
        setField(f, value);
        writeField(f);
    }

    void writeField(StructField structField) {

        if (structField.isReadOnly) 
            return;

        // Get the offset of the field
        int offset = structField.offset;

        // Get the value from the field
        Object value = getField(structField);

        // Determine the type of the field
        Class fieldType = structField.type;
        ToNativeConverter converter = structField.writeConverter;
        if (converter != null) {
            value = converter.toNative(value, new StructureWriteContext(this, structField.field));
            fieldType = converter.nativeType();
        }

        // Java strings get converted to C strings, where a Pointer is used
        if (String.class == fieldType
            || WString.class == fieldType) {

            // Allocate a new string in memory
            boolean wide = fieldType == WString.class;
            if (value != null) {
                NativeString nativeString = new NativeString(value.toString(), wide);
                // Keep track of allocated C strings to avoid
                // premature garbage collection of the memory.
                nativeStrings.put(structField.name, nativeString);
                value = nativeString.getPointer();
            }
            else {
                value = null;
                nativeStrings.remove(structField.name);
            }
        }

        try {
            memory.setValue(offset, value, fieldType);
        }
        catch(IllegalArgumentException e) {
            e.printStackTrace();
            String msg = "Structure field \"" + structField.name
                + "\" was declared as " + structField.type
                + (structField.type == fieldType
                   ? "" : " (native type " + fieldType + ")")
                + ", which is not supported within a Structure";
            throw new IllegalArgumentException(msg);
        }
    }

    private boolean hasFieldOrder() {
        synchronized(this) {
            return fieldOrder != null;
        }
    }

    protected List getFieldOrder() {
        synchronized(this) {
            if (fieldOrder == null) {
                fieldOrder = new ArrayList();
            }
            return fieldOrder;
        }
    }

    /** Provided for VMs where the field order as returned by {@link
     * Class#getFields()} is not predictable.
     */
    protected void setFieldOrder(String[] fields) {
        getFieldOrder().addAll(Arrays.asList(fields));
        // Force recalculation of size/field layout
        this.size = CALCULATE_SIZE;
        if (this.memory instanceof AutoAllocated) {
            this.memory = null;
        }
    }

    /** Sort the structure fields according to the given array of names. */
    protected void sortFields(List fields, List names) {
        for (int i=0;i < names.size();i++) {
            String name = (String)names.get(i);
            for (int f=0;f < fields.size();f++) {
                Field field = (Field)fields.get(f);
                if (name.equals(field.getName())) {
                    Collections.swap(fields, i, f);
                    break;
                }
            }
        }
    }

    protected List getFields(boolean force) {
        // Restrict to valid fields
        List flist = new ArrayList();
        for (Class cls = getClass();
             !cls.equals(Structure.class);
             cls = cls.getSuperclass()) {
            List classFields = new ArrayList();
            Field[] fields = cls.getDeclaredFields();
            for (int i=0;i < fields.length;i++) {
                int modifiers = fields[i].getModifiers();
                if (Modifier.isStatic(modifiers)
                    || !Modifier.isPublic(modifiers))
                    continue;
                classFields.add(fields[i]);
            }
            if (REVERSE_FIELDS) {
                Collections.reverse(classFields);
            }
            flist.addAll(0, classFields);
        }
        if (REQUIRES_FIELD_ORDER || hasFieldOrder()) {
            List fieldOrder = getFieldOrder();
            if (fieldOrder.size() < flist.size()) {
                if (force) {
                    throw new Error("This VM does not store fields in a predictable order; you must use Structure.setFieldOrder to explicitly indicate the field order: " + System.getProperty("java.vendor") + ", " + System.getProperty("java.version"));
                }
                return null;
            }
            sortFields(flist, fieldOrder);
        }
        return flist;
    }

    /** Calculate the amount of native memory required for this structure.
     * May return {@link #CALCULATE_SIZE} if the size can not yet be
     * determined (usually due to fields in the derived class not yet
     * being initialized).
     * <p>
     * If the <code>force</code> parameter is <code>true</code> will throw
     * an {@link IllegalStateException} if the size can not be determined.
     * @throws IllegalStateException an array field is not initialized
     * @throws IllegalArgumentException when an unsupported field type is
     * encountered
     */
    int calculateSize(boolean force) {
        // TODO: maybe cache this information on a per-class basis
        // so that we don't have to re-analyze this static information each
        // time a struct is allocated.

        structAlignment = 1;
        int calculatedSize = 0;
        List fields = getFields(force);
        if (fields == null) {
            return CALCULATE_SIZE;
        }

        boolean firstField = true;
        for (Iterator i=fields.iterator();i.hasNext();firstField=false) {
            Field field = (Field)i.next();
            int modifiers = field.getModifiers();

            Class type = field.getType();
            StructField structField = new StructField();
            structField.isVolatile = Modifier.isVolatile(modifiers);
            structField.isReadOnly = Modifier.isFinal(modifiers);
            if (Modifier.isFinal(modifiers)) {
                field.setAccessible(true);
            }
            structField.field = field;
            structField.name = field.getName();
            structField.type = type;

            // Check for illegal field types
            if (Callback.class.isAssignableFrom(type) && !type.isInterface()) {
                throw new IllegalArgumentException("Structure Callback field '"
                                                   + field.getName()
                                                   + "' must be an interface");
            }
            if (type.isArray()
                && Structure.class.equals(type.getComponentType())) {
                String msg = "Nested Structure arrays must use a "
                    + "derived Structure type so that the size of "
                    + "the elements can be determined";
                throw new IllegalArgumentException(msg);
            }

            int fieldAlignment = 1;
            if (!Modifier.isPublic(field.getModifiers()))
                continue;

            Object value = getField(structField);
            if (value == null) {
                if (Structure.class.isAssignableFrom(type)
                    && !(ByReference.class.isAssignableFrom(type))) {
                    try {
                        value = newInstance(type);
                        setField(structField, value);
                    }
                    catch(IllegalArgumentException e) {
                        String msg = "Can't determine size of nested structure: "
                            + e.getMessage();
                        throw new IllegalArgumentException(msg);
                    }
                }
                else if (type.isArray()) {
                    // can't calculate size yet, defer until later
                    if (force) {
                        throw new IllegalStateException("Array fields must be initialized");
                    }
                    return CALCULATE_SIZE;
                }
            }
            Class nativeType = type;
            if (NativeMapped.class.isAssignableFrom(type)) {
                NativeMappedConverter tc = NativeMappedConverter.getInstance(type);
                if (value == null) {
                    value = tc.defaultValue();
                    setField(structField, value);
                }
                nativeType = tc.nativeType();
                structField.writeConverter = tc;
                structField.readConverter = tc;
                structField.context = new StructureReadContext(this, field);
            }
            else if (typeMapper != null) {
                ToNativeConverter writeConverter = typeMapper.getToNativeConverter(type);
                FromNativeConverter readConverter = typeMapper.getFromNativeConverter(type);
                if (writeConverter != null && readConverter != null) {
                    value = writeConverter.toNative(value,
                                                    new StructureWriteContext(this, structField.field));
                    nativeType = value != null ? value.getClass() : Pointer.class;
                    structField.writeConverter = writeConverter;
                    structField.readConverter = readConverter;
                    structField.context = new StructureReadContext(this, field);
                }
                else if (writeConverter != null || readConverter != null) {
                    String msg = "Structures require bidirectional type conversion for " + type;
                    throw new IllegalArgumentException(msg);
                }
            }
            try {
                structField.size = Native.getNativeSize(nativeType, value);
                fieldAlignment = getNativeAlignment(nativeType, value, firstField);
            }
            catch(IllegalArgumentException e) {
                // Might simply not yet have a type mapper set
                if (!force && typeMapper == null) {
                    return CALCULATE_SIZE;
                }
                String msg = "Invalid Structure field in " + getClass() + ", field name '" + structField.name + "', " + structField.type + ": " + e.getMessage();
                throw new IllegalArgumentException(msg);
            }

            // Align fields as appropriate
            structAlignment = Math.max(structAlignment, fieldAlignment);
            if ((calculatedSize % fieldAlignment) != 0) {
                calculatedSize += fieldAlignment - (calculatedSize % fieldAlignment);
            }
            structField.offset = calculatedSize;
            calculatedSize += structField.size;

            // Save the field in our list
            structFields.put(structField.name, structField);
        }

        if (calculatedSize > 0) {
            int size = calculateAlignedSize(calculatedSize);
            // Update native FFI type information, if needed
            if (this instanceof ByValue) {
                getTypeInfo();
            }
            if (this.memory != null
                && !(this.memory instanceof AutoAllocated)) {
                // Ensure we've set bounds on the memory used
                this.memory = this.memory.share(0, size);
            }
            return size;
        }

        throw new IllegalArgumentException("Structure " + getClass()
                                           + " has unknown size (ensure "
                                           + "all fields are public)");
    }

    int calculateAlignedSize(int calculatedSize) {
        // Structure size must be an integral multiple of its alignment,
        // add padding if necessary.
        if (alignType != ALIGN_NONE) {
            if ((calculatedSize % structAlignment) != 0) {
                calculatedSize += structAlignment - (calculatedSize % structAlignment);
            }
        }
        return calculatedSize;
    }

    protected int getStructAlignment() {
        if (size == CALCULATE_SIZE) {
            // calculate size, but don't allocate memory
            calculateSize(true);
        }
        return structAlignment;
    }

    /** Overridable in subclasses. */
    // TODO: write getNaturalAlignment(stack/alloc) + getEmbeddedAlignment(structs)
    // TODO: move this into a native call which detects default alignment
    // automatically
    protected int getNativeAlignment(Class type, Object value, boolean isFirstElement) {
        int alignment = 1;
        if (NativeMapped.class.isAssignableFrom(type)) {
            NativeMappedConverter tc = NativeMappedConverter.getInstance(type); 
            type = tc.nativeType();
            value = tc.toNative(value, new ToNativeContext());
        }
        int size = Native.getNativeSize(type, value);
        if (type.isPrimitive() || Long.class == type || Integer.class == type
            || Short.class == type || Character.class == type
            || Byte.class == type || Boolean.class == type
            || Float.class == type || Double.class == type) {
            alignment = size;
        }
        else if (Pointer.class == type
                 || Buffer.class.isAssignableFrom(type)
                 || Callback.class.isAssignableFrom(type)
                 || WString.class == type
                 || String.class == type) {
            alignment = Pointer.SIZE;
        }
        else if (Structure.class.isAssignableFrom(type)) {
            if (ByReference.class.isAssignableFrom(type)) {
                alignment = Pointer.SIZE;
            }
            else {
                if (value == null)
                    value = newInstance(type);
                alignment = ((Structure)value).getStructAlignment();
            }
        }
        else if (type.isArray()) {
            alignment = getNativeAlignment(type.getComponentType(), null, isFirstElement);
        }
        else {
            throw new IllegalArgumentException("Type " + type + " has unknown "
                                               + "native alignment");
        }
        if (alignType == ALIGN_NONE) {
            alignment = 1;
        }
        else if (alignType == ALIGN_MSVC) {
            alignment = Math.min(8, alignment);
        }
        else if (alignType == ALIGN_GNUC) {
            // NOTE this is published ABI for 32-bit gcc/linux/x86, osx/x86,
            // and osx/ppc.  osx/ppc special-cases the first element
            if (!isFirstElement || !(Platform.isMac() && isPPC)) {
                alignment = Math.min(MAX_GNUC_ALIGNMENT, alignment);
            }
        }
        return alignment;
    }

    public String toString() {
        return toString(0, true);
    }

    private String format(Class type) {
        String s = type.getName();
        int dot = s.lastIndexOf(".");
        return s.substring(dot + 1);
    }

    private String toString(int indent, boolean showContents) {
        String LS = System.getProperty("line.separator");
        String name = format(getClass()) + "(" + getPointer() + ")";
        if (!(getPointer() instanceof Memory)) {
            name += " (" + size() + " bytes)";
        }
        String prefix = "";
        for (int idx=0;idx < indent;idx++) {
            prefix += "  ";
        }
        String contents = LS;
        if (!showContents) {
            contents = "...}";
        }
        else for (Iterator i=structFields.values().iterator();i.hasNext();) {
            StructField sf = (StructField)i.next();
            Object value = getField(sf);
            String type = format(sf.type);
            String index = "";
            contents += prefix;
            if (sf.type.isArray() && value != null) {
                type = format(sf.type.getComponentType());
                index = "[" + Array.getLength(value) + "]";
            }
            contents += "  " + type + " "
                + sf.name + index + "@" + Integer.toHexString(sf.offset);
            if (value instanceof Structure) {
                value = ((Structure)value).toString(indent + 1, !(value instanceof Structure.ByReference));
            }
            contents += "=";
            if (value instanceof Long) {
                contents += Long.toHexString(((Long)value).longValue());
            }
            else if (value instanceof Integer) {
                contents += Integer.toHexString(((Integer)value).intValue());
            }
            else if (value instanceof Short) {
                contents += Integer.toHexString(((Short)value).shortValue());
            }
            else if (value instanceof Byte) {
                contents += Integer.toHexString(((Byte)value).byteValue());
            }
            else {
                contents += String.valueOf(value).trim();
            }
            contents += LS;
            if (!i.hasNext())
                contents += prefix + "}";
        }
        if (indent == 0 && Boolean.getBoolean("jna.dump_memory")) {
            byte[] buf = getPointer().getByteArray(0, size());
            final int BYTES_PER_ROW = 4;
            contents += LS + "memory dump" + LS;
            for (int i=0;i < buf.length;i++) {
                if ((i % BYTES_PER_ROW) == 0) contents += "[";
                if (buf[i] >=0 && buf[i] < 16)
                    contents += "0";
                contents += Integer.toHexString(buf[i] & 0xFF);
                if ((i % BYTES_PER_ROW) == BYTES_PER_ROW-1 && i < buf.length-1)
                    contents += "]" + LS;
            }
            contents += "]";
        }
        return name + " {" + contents;
    }

    /** Returns a view of this structure's memory as an array of structures.
     * Note that this <code>Structure</code> must have a public, no-arg
     * constructor.  If the structure is currently using auto-allocated
     * {@link Memory} backing, the memory will be resized to fit the entire
     * array. 
     */
    public Structure[] toArray(Structure[] array) {
        ensureAllocated();
        if (this.memory instanceof AutoAllocated) {
            // reallocate if necessary
            Memory m = (Memory)this.memory;
            int requiredSize = array.length * size();
            if (m.size() < requiredSize) {
                useMemory(autoAllocate(requiredSize));
            }
        }
        array[0] = this;
        int size = size();
        for (int i=1;i < array.length;i++) {
            array[i] = Structure.newInstance(getClass());
            array[i].useMemory(memory.share(i*size, size));
            array[i].read();
        }

        if (!(this instanceof ByValue)) {
            // keep track for later auto-read/writes
            this.array = array;
        }

        return array;
    }

    /** Returns a view of this structure's memory as an array of structures.
     * Note that this <code>Structure</code> must have a public, no-arg
     * constructor.  If the structure is currently using auto-allocated
     * {@link Memory} backing, the memory will be resized to fit the entire
     * array. 
     */
    public Structure[] toArray(int size) {
        return toArray((Structure[])Array.newInstance(getClass(), size));
    }

    private Class baseClass() {
        if ((this instanceof Structure.ByReference
             || this instanceof Structure.ByValue)
            && Structure.class.isAssignableFrom(getClass().getSuperclass())) {
            return getClass().getSuperclass();
        }
        return getClass();
    }

    /** This structure is equal to another based on the same data type
     * and visible data fields.
     */
    public boolean equals(Object o) {
        if (o == this) {
            return true;
        }
        if (!(o instanceof Structure)) {
            return false;
        }
        if (o.getClass() != getClass()
            && ((Structure)o).baseClass() != baseClass()) {
            return false;
        }
        Structure s = (Structure)o;
        if (s.size() == size()) {
            clear(); write();
            byte[] buf = getPointer().getByteArray(0, size());
            s.clear(); s.write();
            byte[] sbuf = s.getPointer().getByteArray(0, s.size());
            return Arrays.equals(buf, sbuf);
        }
        return false;
    }

    /** Since {@link #equals} depends on the native address, use that
     * as the hash code.
     */
    public int hashCode() {
        clear(); write();
        return Arrays.hashCode(getPointer().getByteArray(0, size()));
    }

    protected void cacheTypeInfo(Pointer p) {
        typeInfo = p.peer;
    }

    /** Obtain native type information for this structure. */
    Pointer getTypeInfo() {
        Pointer p = getTypeInfo(this);
        cacheTypeInfo(p);
        return p;
    }

    /** Set whether the structure is automatically synchronized to native memory
        before and after a native function call.  Convenience method for
        <pre><code>
        boolean auto = ...;
        setAutoRead(auto);
        setAutoWrite(auto);
        </code></pre>
    */
    public void setAutoSynch(boolean auto) {
        setAutoRead(auto);
        setAutoWrite(auto);
    }

    /** Set whether the structure is read from native memory prior to
        a native function call.
    */
    public void setAutoRead(boolean auto) {
        this.autoRead = auto;
    }

    /** Returns whether the structure is read from native memory prior to
        a native function call.
    */
    public boolean getAutoRead() {
        return this.autoRead;
    }

    /** Set whether the structure is written to native memory after a native
        function call.
    */
    public void setAutoWrite(boolean auto) {
        this.autoWrite = auto;
    }

    /** Returns whether the structure is written to native memory after a native
        function call. 
    */
    public boolean getAutoWrite() {
        return this.autoWrite;
    }

    /** Exposed for testing purposes only. */
    static Pointer getTypeInfo(Object obj) {
        return FFIType.get(obj);
    }

    /** Create a new Structure instance of the given type
     * @param type
     * @return the new instance
     * @throws IllegalArgumentException if the instantiation fails
     */
    public static Structure newInstance(Class type) throws IllegalArgumentException {
        try {
            Structure s = (Structure)type.newInstance();
            if (s instanceof ByValue) {
                s.allocateMemory();
            }
            return s;
        }
        catch(InstantiationException e) {
            String msg = "Can't instantiate " + type + " (" + e + ")";
            throw new IllegalArgumentException(msg);
        }
        catch(IllegalAccessException e) {
            String msg = "Instantiation of " + type
                + " not allowed, is it public? (" + e + ")";
            throw new IllegalArgumentException(msg);
        }
    }

    class StructField extends Object {
        public String name;
        public Class type;
        public Field field;
        public int size = -1;
        public int offset = -1;
        public boolean isVolatile;
        public boolean isReadOnly;
        public FromNativeConverter readConverter;
        public ToNativeConverter writeConverter;
        public FromNativeContext context;
    }
    /** This class auto-generates an ffi_type structure appropriate for a given
     * structure for use by libffi.  The lifecycle of this structure is easier
     * to manage on the Java side than in native code.
     */
    static class FFIType extends Structure {
        public static class size_t extends IntegerType {
            public size_t() { this(0); }
            public size_t(long value) { super(Native.POINTER_SIZE, value); }
        }
        private static Map typeInfoMap = new WeakHashMap();
        // Native.initIDs initializes these fields to their appropriate
        // pointer values.  These are in a separate class from FFIType so that
        // they may be initialized prior to loading the FFIType class
        private static class FFITypes {
            private static Pointer ffi_type_void;
            private static Pointer ffi_type_float;
            private static Pointer ffi_type_double;
            private static Pointer ffi_type_longdouble;
            private static Pointer ffi_type_uint8;
            private static Pointer ffi_type_sint8;
            private static Pointer ffi_type_uint16;
            private static Pointer ffi_type_sint16;
            private static Pointer ffi_type_uint32;
            private static Pointer ffi_type_sint32;
            private static Pointer ffi_type_uint64;
            private static Pointer ffi_type_sint64;
            private static Pointer ffi_type_pointer;
        }
        static {
            if (Native.POINTER_SIZE == 0)
                throw new Error("Native library not initialized");
            if (FFITypes.ffi_type_void == null)
                throw new Error("FFI types not initialized");
            typeInfoMap.put(void.class, FFITypes.ffi_type_void);
            typeInfoMap.put(Void.class, FFITypes.ffi_type_void);
            typeInfoMap.put(float.class, FFITypes.ffi_type_float);
            typeInfoMap.put(Float.class, FFITypes.ffi_type_float);
            typeInfoMap.put(double.class, FFITypes.ffi_type_double);
            typeInfoMap.put(Double.class, FFITypes.ffi_type_double);
            typeInfoMap.put(long.class, FFITypes.ffi_type_sint64);
            typeInfoMap.put(Long.class, FFITypes.ffi_type_sint64);
            typeInfoMap.put(int.class, FFITypes.ffi_type_sint32);
            typeInfoMap.put(Integer.class, FFITypes.ffi_type_sint32);
            typeInfoMap.put(short.class, FFITypes.ffi_type_sint16);
            typeInfoMap.put(Short.class, FFITypes.ffi_type_sint16);
            Pointer ctype = Native.WCHAR_SIZE == 2
                ? FFITypes.ffi_type_uint16 : FFITypes.ffi_type_uint32;
            typeInfoMap.put(char.class, ctype);
            typeInfoMap.put(Character.class, ctype);
            typeInfoMap.put(byte.class, FFITypes.ffi_type_sint8);
            typeInfoMap.put(Byte.class, FFITypes.ffi_type_sint8);
            typeInfoMap.put(boolean.class, FFITypes.ffi_type_uint32);
            typeInfoMap.put(Boolean.class, FFITypes.ffi_type_uint32);
            typeInfoMap.put(Pointer.class, FFITypes.ffi_type_pointer);
            typeInfoMap.put(String.class, FFITypes.ffi_type_pointer);
            typeInfoMap.put(WString.class, FFITypes.ffi_type_pointer);
        }
        // From ffi.h
        private static final int FFI_TYPE_STRUCT = 13;
        // Structure fields
        public size_t size;
        public short alignment;
        public short type = FFI_TYPE_STRUCT;
        public Pointer elements;

        private FFIType(Structure ref) {
            Pointer[] els;
            if (ref instanceof Union) {
                StructField sf = ((Union)ref).biggestField;
                els = new Pointer[] {
                    get(ref.getField(sf), sf.type), null,
                };
            }
            else {
                els = new Pointer[ref.fields().size() + 1];
                int idx = 0;
                for (Iterator i=ref.fields().values().iterator();i.hasNext();) {
                    StructField sf = (StructField)i.next();
                    els[idx++] = get(ref.getField(sf), sf.type);
                }
            }
            init(els);
        }
        // Represent fixed-size arrays as structures of N identical elements
        private FFIType(Object array, Class type) {
            int length = Array.getLength(array);
            Pointer[] els = new Pointer[length+1];
            Pointer p = get(null, type.getComponentType());
            for (int i=0;i < length;i++) {
                els[i] = p;
            }
            init(els);
        }
        private void init(Pointer[] els) {
            elements = new Memory(Pointer.SIZE * els.length);
            elements.write(0, els, 0, els.length);
            write();
        }

        static Pointer get(Object obj) {
            if (obj == null)
                return FFITypes.ffi_type_pointer;
            if (obj instanceof Class)
                return get(null, (Class)obj);
            return get(obj, obj.getClass());
        }

        private static Pointer get(Object obj, Class cls) {
            synchronized(typeInfoMap) {
                Object o = typeInfoMap.get(cls);
                if (o instanceof Pointer) {
                    return (Pointer)o;
                }
                if (o instanceof FFIType) {
                    return ((FFIType)o).getPointer();
                }
                if (Buffer.class.isAssignableFrom(cls)
                    || Callback.class.isAssignableFrom(cls)) {
                    typeInfoMap.put(cls, FFITypes.ffi_type_pointer);
                    return FFITypes.ffi_type_pointer;
                }
                if (Structure.class.isAssignableFrom(cls)) {
                    if (obj == null) obj = newInstance(cls);
                    if (ByReference.class.isAssignableFrom(cls)) {
                        typeInfoMap.put(cls, FFITypes.ffi_type_pointer);
                        return FFITypes.ffi_type_pointer;
                    }
                    FFIType type = new FFIType((Structure)obj);
                    typeInfoMap.put(cls, type);
                    return type.getPointer();
                }
                if (NativeMapped.class.isAssignableFrom(cls)) {
                    NativeMappedConverter c = NativeMappedConverter.getInstance(cls);
                    return get(c.toNative(obj, new ToNativeContext()), c.nativeType());
                }
                if (cls.isArray()) {
                    FFIType type = new FFIType(obj, cls);
                    // Store it in the map to prevent premature GC of type info
                    typeInfoMap.put(obj, type);
                    return type.getPointer();
                }
                throw new IllegalArgumentException("Unsupported type " + cls);
            }
        }
    }
    
    private class AutoAllocated extends Memory {
        public AutoAllocated(int size) {
            super(size);
            // Always clear new structure memory
            clear();
        }
    }

    private static void structureArrayCheck(Structure[] ss) {
        Pointer base = ss[0].getPointer();
        int size = ss[0].size();
        for (int si=1;si < ss.length;si++) {
            if (ss[si].getPointer().peer != base.peer + size*si) {
                String msg = "Structure array elements must use"
                    + " contiguous memory (bad backing address at Structure array index " + si + ")";     
                throw new IllegalArgumentException(msg);
            }
        }
    }

    public static void autoRead(Structure[] ss) {
        structureArrayCheck(ss);
        if (ss[0].array == ss) {
            ss[0].autoRead();
        }
        else {
            for (int si=0;si < ss.length;si++) {
                ss[si].autoRead();
            }
        }
    }

    public void autoRead() {
        if (getAutoRead()) {
            read();
            if (array != null) {
                for (int i=1;i < array.length;i++) {
                    array[i].autoRead();
                }
            }
        }
    }

    public static void autoWrite(Structure[] ss) {
        structureArrayCheck(ss);
        if (ss[0].array == ss) {
            ss[0].autoWrite();
        }
        else {
            for (int si=0;si < ss.length;si++) {
                ss[si].autoWrite();
            }
        }
    }

    public void autoWrite() {
        if (getAutoWrite()) {
            write();
            if (array != null) {
                for (int i=1;i < array.length;i++) {
                    array[i].autoWrite();
                }
            }
        }
    }
} public final class WString implements CharSequence, Comparable {
    private String string;
    public WString(String s){
        if (s == null) throw new NullPointerException("String initializer must be non-null");
        this.string = s;
    }
    public String toString() {
        return string;
    }
    public boolean equals(Object o) {
        return o instanceof WString && toString().equals(o.toString());
    }
    public int hashCode() {
        return toString().hashCode();
    }
    public int compareTo(Object o) {
        return toString().compareTo(o.toString());
    }
    public int length() {
        return toString().length();
    }
    public char charAt(int index) {
        return toString().charAt(index);
    }
    public CharSequence subSequence(int start, int end) {
        return CharBuffer.wrap(toString()).subSequence(start, end);
    }
} public abstract class Kernel32Util {
	
	/**
	 * Get current computer NetBIOS name.
	 * @return 
	 *  Netbios name.
	 */
	public static String getComputerName() {
    	char buffer[] = new char[WinBase.MAX_COMPUTERNAME_LENGTH() + 1];
    	IntByReference lpnSize = new IntByReference(buffer.length);
    	if (! Kernel32.INSTANCE.GetComputerName(buffer, lpnSize)) {
    		throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    	}
    	return Native.toString(buffer);
	}
	
	/**
	 * Format a message from an HRESULT.
	 * @param code
	 *  HRESULT
	 * @return
	 *  Formatted message.
	 */
	public static String formatMessageFromHR(HRESULT code) {
		PointerByReference buffer = new PointerByReference();        
        if (0 == Kernel32.INSTANCE.FormatMessage(
        		WinBase.FORMAT_MESSAGE_ALLOCATE_BUFFER
        		| WinBase.FORMAT_MESSAGE_FROM_SYSTEM 
        		| WinBase.FORMAT_MESSAGE_IGNORE_INSERTS, 
                null,
                code.intValue(), 
                0, // TODO: MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
                buffer, 
                0, 
                null)) {
        	throw new LastErrorException(Kernel32.INSTANCE.GetLastError());
        }	       
    	String s = buffer.getValue().getString(0, ! Boolean.getBoolean("w32.ascii"));
    	s = s.replace(".\r",".").replace(".\n",".");
    	Kernel32.INSTANCE.LocalFree(buffer.getValue());
    	return s;		
	}
	
	/**
	 * Format a system message from an error code.
	 * @param code
	 *  Error code, typically a result of GetLastError.
	 * @return
	 *  Formatted message.
	 */
	public static String formatMessageFromLastErrorCode(int code) {
		return formatMessageFromHR(W32Errors.HRESULT_FROM_WIN32(code));
	}
	
	/**
	 * Return the path designated for temporary files.
	 * @return
	 *  Path.
	 */
	public static String getTempPath() {
		DWORD nBufferLength = new DWORD(WinDef.MAX_PATH);
    	char[] buffer = new char[nBufferLength.intValue()]; 
    	if (Kernel32.INSTANCE.GetTempPath(nBufferLength, buffer).intValue() == 0) {
    		throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    	}
    	return Native.toString(buffer);
	}
	
	/**
	 * Returns valid drives in the system.
	 * @return
	 *  An array of valid drives.
	 */
	public static String[] getLogicalDriveStrings() {
    	DWORD dwSize = Kernel32.INSTANCE.GetLogicalDriveStrings(new DWORD(0), null);
    	if (dwSize.intValue() <= 0) {
    		throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    	}
    	
    	char buf[] = new char[dwSize.intValue()];
    	dwSize = Kernel32.INSTANCE.GetLogicalDriveStrings(dwSize, buf);
    	if (dwSize.intValue() <= 0) {
    		throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    	}

    	List<String> drives = new ArrayList<String>();    	
    	String drive = "";
    	// the buffer is double-null-terminated
    	for(int i = 0; i < buf.length - 1; i++) {
    		if (buf[i] == 0) {
    			drives.add(drive);
    			drive = "";
    		} else {
    			drive += buf[i];
    		}
    	}
    	return drives.toArray(new String[0]);
	}	
}
 public interface User32 extends StdCallLibrary {

	User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class,
			W32APIOptions.DEFAULT_OPTIONS);

	/**
	 * This function retrieves a handle to a display device context (DC) for the
	 * client area of the specified window. The display device context can be
	 * used in subsequent graphics display interface (GDI) functions to draw in
	 * the client area of the window.
	 * @param hWnd
	 *  Handle to the window whose device context is to be retrieved.
	 *  If this value is NULL, GetDC retrieves the device context for
	 *  the entire screen.
	 * @return 
	 *  The handle the device context for the specified window's client
	 *  area indicates success. NULL indicates failure. To get extended
	 *  error information, call GetLastError.
	 */
	HDC GetDC(HWND hWnd);

	/**
	 * This function releases a device context (DC), freeing it for use by other
	 * applications. The effect of ReleaseDC depends on the type of device
	 * context.
	 * 
	 * @param hWnd
	 *  Handle to the window whose device context is to be released.
	 * @param hDC
	 *  Handle to the device context to be released.
	 * @return 
	 *  The return value specifies whether the device context is
	 *  released. 1 indicates that the device context is released. Zero
	 *  indicates that the device context is not released.
	 */
	int ReleaseDC(HWND hWnd, HDC hDC);

	/**
	 * This function retrieves the handle to the top-level window whose class name and 
	 * window name match the specified strings. This function does not search child windows.
	 * @param lpClassName 
	 *  Long pointer to a null-terminated string that specifies the class name or is an atom 
	 *  that identifies the class-name string. If this parameter is an atom, it must be a 
	 *  global atom created by a previous call to the GlobalAddAtom function. The atom, a 
	 *  16-bit value, must be placed in the low-order word of lpClassName; the high-order 
	 *  word must be zero. 
	 * @param lpWindowName
	 *  Long pointer to a null-terminated string that specifies the window name (the window's 
	 *  title). If this parameter is NULL, all window names match. 
	 * @return
	 *  A handle to the window that has the specified class name and window name indicates 
	 *  success. NULL indicates failure. To get extended error information, call GetLastError.
	 */
	HWND FindWindow(String lpClassName, String lpWindowName);

	/**
	 * This function retrieves the name of the class to which the specified window belongs. 
	 * @param hWnd
	 *  Handle to the window and, indirectly, the class to which the window belongs. 
	 * @param lpClassName
	 *  Long pointer to the buffer that is to receive the class name string.
	 * @param nMaxCount
	 *  Specifies the length, in characters, of the buffer pointed to by the lpClassName 
	 *  parameter. The class name string is truncated if it is longer than the buffer.
	 * @return
	 *  The number of characters copied to the specified buffer indicates success. Zero 
	 *  indicates failure. To get extended error information, call GetLastError. 
	 */
	int GetClassName(HWND hWnd, char[] lpClassName, int nMaxCount);

	/**
	 * Retrieves information about the active window or a specified graphical user 
	 * interface (GUI) thread. 
	 * @param idThread
	 *  Identifies the thread for which information is to be retrieved. To retrieve 
	 *  this value, use the GetWindowThreadProcessId function. If this parameter is NULL,
	 *  the function returns information for the foreground thread. 
	 * @param lpgui
	 *  Pointer to a GUITHREADINFO structure that receives information describing the thread. 
	 *  Note that you must set GUITHREADINFO.cbSize to sizeof(GUITHREADINFO) before calling this function. 
	 * @return
	 *  If the function succeeds, the return value is nonzero.
	 *  If the function fails, the return value is zero. To get extended error information, call GetLastError. 
	 */
	boolean GetGUIThreadInfo(int idThread, GUITHREADINFO lpgui);

	/**
	 * The GetWindowInfo function retrieves information about the specified window.
	 * @param hWnd
	 *  Handle to the window whose information is to be retrieved.
	 * @param pwi
	 *  Pointer to a WINDOWINFO structure to receive the information. Note that you must set WINDOWINFO.cbSize 
	 *  to sizeof(WINDOWINFO) before calling this function.
	 * @return
	 *  If the function succeeds, the return value is nonzero.
	 *  If the function fails, the return value is zero. 
	 */
	boolean GetWindowInfo(HWND hWnd, WINDOWINFO pwi);

	/**
	 * This function 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
	 *  Handle to the window. 
	 * @param rect
	 *  Long pointer to a RECT structure that receives the screen coordinates of the upper-left and lower-right 
	 *  corners of the window. 
	 * @return
	 *  Nonzero indicates success. Zero indicates failure. To get extended error information, call GetLastError.
	 */
	boolean GetWindowRect(HWND hWnd, RECT rect);

	/**
	 * This function copies the text of the specified window's title bar - if it has one - into a buffer. If 
	 * the specified window is a control, the text of the control is copied.
	 * @param hWnd
	 *  Handle to the window or control containing the text. 
	 * @param lpString
	 *  Long pointer to the buffer that will receive the text. 
	 * @param nMaxCount
	 *  Specifies the maximum number of characters to copy to the buffer, including the NULL character. 
	 *  If the text exceeds this limit, it is truncated. 
	 * @return
	 *  The length, in characters, of the copied string, not including the terminating null character, 
	 *  indicates success. Zero indicates that the window has no title bar or text, if the title bar is
	 *  empty, or if the window or control handle is invalid. To get extended error information, call 
	 *  GetLastError. This function cannot retrieve the text of an edit control in another application.
	 */
	int GetWindowText(HWND hWnd, char[] lpString, int nMaxCount);

	/**
	 * This function retrieves the length, in characters, of the specified window's title bar text -
	 * if the window has a title bar. If the specified window is a control, the function retrieves the
	 * length of the text within the control.
	 * @param hWnd
	 *  Handle to the window or control. 
	 * @return
	 *  The length, in characters, of the text indicates success. Under certain conditions, this value 
	 *  may actually be greater than the length of the text. Zero indicates that the window has no text. 
	 *  To get extended error information, call GetLastError.
	 */
	int GetWindowTextLength(HWND hWnd);

	/**
	 * The GetWindowModuleFileName function retrieves the full path and file name of the module associated
	 * with the specified window handle.
	 * @param hWnd
	 *  Handle to the window whose module file name will be retrieved.
	 * @param lpszFileName
	 *  Pointer to a buffer that receives the path and file name.
	 * @param cchFileNameMax
	 *  Specifies the maximum number of TCHARs that can be copied into the lpszFileName buffer.
	 * @return
	 *  The return value is the total number of TCHARs copied into the buffer.
	 */
	int GetWindowModuleFileName(HWND hWnd, char[] lpszFileName,
			int cchFileNameMax);

	/**
	 * This function retrieves the identifier of the thread that created the specified window and, optionally,
	 * the identifier of the process that created the window.
	 * @param hWnd
	 *  Handle to the window.
	 * @param lpdwProcessId
	 *  Pointer to a 32-bit value that receives the process identifier. If this parameter is not NULL, 
	 *  GetWindowThreadProcessId copies the identifier of the process to the 32-bit value; otherwise, 
	 *  it does not. 
	 * @return
	 *  The return value is the identifier of the thread that created the window.
	 */
	int GetWindowThreadProcessId(HWND hWnd, IntByReference lpdwProcessId);

	/**
	 * This function enumerates all top-level windows on the screen by passing the handle to each window,
	 * in turn, to an application-defined callback function. EnumWindows continues until the last top-level
	 * window is enumerated or the callback function returns FALSE.
	 * @param lpEnumFunc
	 *  Long pointer to an application-defined callback function.
	 * @param data
	 *  Specifies an application-defined value to be passed to the callback function.
	 * @return
	 *  Nonzero indicates success. Zero indicates failure. To get extended error information, call GetLastError.
	 */
	boolean EnumWindows(WNDENUMPROC lpEnumFunc, Pointer data);

	/**
	 * The EnumChildWindows function enumerates the child windows that belong to the specified parent window
	 * by passing the handle to each child window, in turn, to an application-defined callback function. 
	 * EnumChildWindows continues until the last child window is enumerated or the callback function returns FALSE.
	 * @param hWnd
	 *  Handle to the parent window whose child windows are to be enumerated. If this parameter is NULL, this 
	 *  function is equivalent to EnumWindows.
	 * @param lpEnumFunc
	 *  Pointer to an application-defined callback function.
	 * @param data
	 *  Specifies an application-defined value to be passed to the callback function.
	 * @return
	 *  If the function succeeds, the return value is nonzero.
	 *  If the function fails, the return value is zero. To get extended error information, call GetLastError. 
	 *  If EnumChildProc returns zero, the return value is also zero. In this case, the callback function 
	 *  should call SetLastError to obtain a meaningful error code to be returned to the caller of 
	 *  EnumChildWindows.
	 */
	boolean EnumChildWindows(HWND hWnd, WNDENUMPROC lpEnumFunc, Pointer data);

	/**
	 * The EnumThreadWindows function enumerates all nonchild windows associated with a thread by passing 
	 * the handle to each window, in turn, to an application-defined callback function. EnumThreadWindows
	 * continues until the last window is enumerated or the callback function returns FALSE. To enumerate
	 * child windows of a particular window, use the EnumChildWindows function.
	 * @param dwThreadId
	 *  Identifies the thread whose windows are to be enumerated.
	 * @param lpEnumFunc
	 *  Pointer to an application-defined callback function. 
	 * @param data
	 *  Specifies an application-defined value to be passed to the callback function.
	 * @return
	 *  If the callback function returns TRUE for all windows in the thread specified by dwThreadId, the 
	 *  return value is TRUE. If the callback function returns FALSE on any enumerated window, or if there
	 *  are no windows found in the thread specified by dwThreadId, the return value is FALSE.
	 */
	boolean EnumThreadWindows(int dwThreadId, WNDENUMPROC lpEnumFunc,
			Pointer data);

	/**
	 * The FlashWindowEx function flashes the specified window. It does not change the active state of the window.
	 * @param pfwi
	 *  Pointer to the FLASHWINFO structure. 
	 * @return
	 *  The return value specifies the window's state before the call to the FlashWindowEx function. If the window
	 *  caption was drawn as active before the call, the return value is nonzero. Otherwise, the return value is zero.
	 */
	boolean FlashWindowEx(FLASHWINFO pfwi);

	/**
	 * This function loads the specified icon resource from the executable (.exe) file associated with an 
	 * application instance.
	 * @param hInstance
	 *  Handle to an instance of the module whose executable file contains the icon to be loaded. 
	 *  This parameter must be NULL when a standard icon is being loaded.
	 * @param iconName
	 *  Long pointer to a null-terminated string that contains the name of the icon resource to be loaded. 
	 *  Alternatively, this parameter can contain the resource identifier in the low-order word and zero 
	 *  in the high-order word. Use the MAKEINTRESOURCE macro to create this value.
	 * @return
	 *  A handle to the newly loaded icon indicates success. NULL indicates failure. To get extended 
	 *  error information, call GetLastError. 
	 */
	HICON LoadIcon(HINSTANCE hInstance, String iconName);

	/**
	 * This function loads an icon, cursor, or bitmap.
	 * @param hinst
	 *  Handle to an instance of the module that contains the image to be loaded.
	 * @param name
	 *  Pointer to a null-terminated string that contains the name of the image resource 
	 *  in the hinst module that identifies the image to load.
	 * @param type
	 *  Specifies the type of image to be loaded.
	 * @param xDesired
	 *  Specifies the width, in pixels, of the icon or cursor. If this parameter is zero, the function uses 
	 *  the SM_CXICON or SM_CXCURSOR system metric value to set the width. If uType is IMAGE_BITMAP, this 
	 *  parameter must be zero.
	 * @param yDesired
	 *  Specifies the height, in pixels, of the icon or cursor. If this parameter is zero, the function uses 
	 *  the SM_CYICON or SM_CYCURSOR system metric value to set the height. If uType is IMAGE_BITMAP, this 
	 *  parameter must be zero.
	 * @param load
	 *  Set to zero.
	 * @return
	 *  The handle of the newly loaded image indicates success. NULL indicates failure. To get extended error information, call GetLastError.
	 */
	HANDLE LoadImage(HINSTANCE hinst, String name, int type, int xDesired,
			int yDesired, int load);

	/**
	 * This function destroys an icon and frees any memory the icon occupied.
	 * @param hicon
	 *  Handle to the icon to be destroyed. The icon must not be in use. 
	 * @return
	 *  Nonzero indicates success. Zero indicates failure. To get extended error information, call GetLastError.
	 */
	boolean DestroyIcon(HICON hicon);

	/**
	 * This function retrieves information about the specified window. GetWindowLong also retrieves 
	 * the 32-bit (long) value at the specified offset into the extra window memory of a window. 
	 * @param hWnd
	 *  Handle to the window and, indirectly, the class to which the window belongs. 
	 * @param nIndex
	 *  Specifies the zero-based offset to the value to be retrieved.
	 * @return
	 *  The requested 32-bit value indicates success. Zero indicates failure. To get extended error 
	 *  information, call GetLastError.
	 */
	int GetWindowLong(HWND hWnd, int nIndex);

	/**
	 * This function changes an attribute of the specified window. SetWindowLong also sets a 32-bit (LONG) 
	 * value at the specified offset into the extra window memory of a window.
	 * @param hWnd
	 *  Handle to the window and, indirectly, the class to which the window belongs. 
	 * @param nIndex
	 *  Specifies the zero-based offset to the value to be set.
	 * @param dwNewLong
	 *  Specifies the replacement value.
	 * @return
	 *  The previous value of the specified 32-bit integer indicates success. Zero indicates failure. 
	 *  To get extended error information, call GetLastError.
	 */
	int SetWindowLong(HWND hWnd, int nIndex, int dwNewLong);

	/**
	 * This function changes an attribute of the specified window. SetWindowLong also sets a 
	 * 32-bit (LONG) value at the specified offset into the extra window memory of a window.
	 * Do not use this version on Windows-64.
	 * @param hWnd
	 *  Handle to the window and, indirectly, the class to which the window belongs.
	 * @param nIndex
	 *  Specifies the zero-based offset to the value to be set.
	 * @param dwNewLong
	 *  Specifies the replacement value. 
	 * @return
	 *  The previous value of the specified 32-bit integer indicates success. Zero indicates failure.
	 *  To get extended error information, call GetLastError. 
	 */
	Pointer SetWindowLong(HWND hWnd, int nIndex, Pointer dwNewLong);

	/**
	 * The GetWindowLongPtr function retrieves information about the specified window. 
	 * The function also retrieves the value at a specified offset into the extra window memory.
	 * @param hWnd
	 *  Handle to the window and, indirectly, the class to which the window belongs.
	 * @param nIndex
	 *  Specifies the zero-based offset to the value to be retrieved.
	 * @return
	 *  If the function succeeds, the return value is the requested value.
	 *  If the function fails, the return value is zero. To get extended error information, call GetLastError. 
	 *  If SetWindowLong or SetWindowLongPtr has not been called previously, GetWindowLongPtr returns zero for
	 *  values in the extra window or class memory.
	 */
	LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex);

	/**
	 * The SetWindowLongPtr function changes an attribute of the specified window. The function also
	 * sets a value at the specified offset in the extra window memory.
	 * @param hWnd
	 *  Handle to the window and, indirectly, the class to which the window belongs.
	 * @param nIndex
	 *  Specifies the zero-based offset to the value to be set.
	 * @param dwNewLongPtr
	 *  Specifies the replacement value.
	 * @return
	 *  If the function succeeds, the return value is the previous value of the specified offset.
	 *  If the function fails, the return value is zero. To get extended error information, call GetLastError. 
	 *  If the previous value is zero and the function succeeds, the return value is zero, but the function 
	 *  does not clear the last error information. To determine success or failure, clear the last error 
	 *  information by calling SetLastError(0), then call SetWindowLongPtr. Function failure will be indicated
	 *  by a return value of zero and a GetLastError result that is nonzero.
	 */
	LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLongPtr);

	/**
	 * The SetWindowLongPtr function changes an attribute of the specified window. The function also
	 * sets a value at the specified offset in the extra window memory.
	 * @param hWnd
	 *  Handle to the window and, indirectly, the class to which the window belongs.
	 * @param nIndex
	 *  Specifies the zero-based offset to the value to be set.
	 * @param dwNewLongPtr
	 *  Specifies the replacement value.
	 * @return
	 *  If the function succeeds, the return value is the previous value of the specified offset.
	 *  If the function fails, the return value is zero. To get extended error information, call GetLastError. 
	 *  If the previous value is zero and the function succeeds, the return value is zero, but the function 
	 *  does not clear the last error information. To determine success or failure, clear the last error 
	 *  information by calling SetLastError(0), then call SetWindowLongPtr. Function failure will be indicated
	 *  by a return value of zero and a GetLastError result that is nonzero.
	 */
	Pointer SetWindowLongPtr(HWND hWnd, int nIndex, Pointer dwNewLongPtr);

	/**
	 * The SetLayeredWindowAttributes function sets the opacity and transparency color key of a layered window.
	 * @param hwnd
	 *  Handle to the layered window.
	 * @param crKey
	 *  COLORREF structure that specifies the transparency color key to be used when composing the layered window.
	 * @param bAlpha
	 *  Alpha value used to describe the opacity of the layered window. 
	 * @param dwFlags
	 *  Specifies an action to take.
	 * @return
	 *  If the function succeeds, the return value is nonzero. 
	 *  If the function fails, the return value is zero. To get extended error information, call GetLastError.
	 */
	boolean SetLayeredWindowAttributes(HWND hwnd, int crKey, byte bAlpha,
			int dwFlags);

	/**
	 * The GetLayeredWindowAttributes function retrieves the opacity and transparency color 
	 * key of a layered window.
	 * @param hwnd
	 *  Handle to the layered window. A layered window is created by specifying WS_EX_LAYERED 
	 *  when creating the window with the CreateWindowEx function or by setting WS_EX_LAYERED
	 *  via SetWindowLong after the window has been created.
	 * @param pcrKey
	 *  Pointer to a COLORREF value that receives the transparency color key to be used when 
	 *  composing the layered window. All pixels painted by the window in this color will be 
	 *  transparent. This can be NULL if the argument is not needed.
	 * @param pbAlpha
	 *  Pointer to a BYTE that receives the Alpha value used to describe the opacity of the 
	 *  layered window. Similar to the SourceConstantAlpha member of the BLENDFUNCTION structure.
	 *  When the variable referred to by pbAlpha is 0, the window is completely transparent. 
	 *  When the variable referred to by pbAlpha is 255, the window is opaque. This can be NULL 
	 *  if the argument is not needed.
	 * @param pdwFlags
	 *  Pointer to a DWORD that receives a layering flag. This can be NULL if the argument is not needed.
	 * @return
	 *  If the function succeeds, the return value is nonzero.
	 *  If the function fails, the return value is zero. To get extended error information, call GetLastError. 
	 */
	boolean GetLayeredWindowAttributes(HWND hwnd, IntByReference pcrKey,
			ByteByReference pbAlpha, IntByReference pdwFlags);

	/**
	 * The UpdateLayeredWindow function updates the position, size, shape, content, and 
	 * translucency of a layered window. 
	 * @param hwnd
	 *  Handle to a layered window. A layered window is created by specifying WS_EX_LAYERED 
	 *  when creating the window with the CreateWindowEx function. 
	 * @param hdcDst
	 *  Handle to a device context (DC) for the screen. This handle is obtained by specifying NULL 
	 *  when calling the function. It is used for palette color matching when the window contents 
	 *  are updated. If hdcDst isNULL, the default palette will be used. If hdcSrc is NULL, hdcDst must be NULL.
	 * @param pptDst
	 *  Pointer to a POINT structure that specifies the new screen position of the layered window. 
	 *  If the current position is not changing, pptDst can be NULL. 
	 * @param psize
	 *  Pointer to a SIZE structure that specifies the new size of the layered window. If the size of the window
	 *  is not changing, psize can be NULL. If hdcSrc is NULL, psize must be NULL. 
	 * @param hdcSrc
	 *  Handle to a DC for the surface that defines the layered window. This handle can be obtained by calling 
	 *  the CreateCompatibleDC function. If the shape and visual context of the window are not changing, hdcSrc 
	 *  can be NULL. 
	 * @param pptSrc
	 *  Pointer to a POINT structure that specifies the location of the layer in the device context. 
	 *  If hdcSrc is NULL, pptSrc should be NULL.
	 * @param crKey
	 *  Pointer to a COLORREF value that specifies the color key to be used when composing the layered window.
	 *  To generate a COLORREF, use the RGB macro.
	 * @param pblend
	 *  Pointer to a BLENDFUNCTION structure that specifies the transparency value to be used when composing 
	 *  the layered window. 
	 * @param dwFlags
	 *  ULW_* flags.
	 * @return
	 *  If the function succeeds, the return value is nonzero.
	 *  If the function fails, the return value is zero. To get extended error information, call GetLastError. 
	 */
	boolean UpdateLayeredWindow(HWND hwnd, HDC hdcDst, POINT pptDst,
			SIZE psize, HDC hdcSrc, POINT pptSrc, int crKey,
			BLENDFUNCTION pblend, int dwFlags);

	/**
	 * This function sets the window region of a window. The window region determines the area within the 
	 * window where the system permits drawing. The system does not display any portion of a window that lies
	 * outside of the window region. 
	 * @param hWnd
	 *  Handle to the window whose window region is to be set.
	 * @param hRgn
	 *  Handle to a region.  The function sets the window region of the window to this region. 
	 *  If hRgn is NULL, the function sets the window region to NULL. 
	 * @param bRedraw
	 *  Specifies whether the system redraws the window after setting the window region.  
	 *  If bRedraw is TRUE, the system does so; otherwise, it does not. 
	 *  Typically, you set bRedraw to TRUE if the window is visible. 
	 * @return
	 *  Nonzero indicates success. 
	 *  Zero indicates failure. 
	 *  To get extended error information, call GetLastError.
	 */
	int SetWindowRgn(HWND hWnd, HRGN hRgn, boolean bRedraw);

	/**
	 * The GetKeyboardState function copies the status of the 256 virtual keys to the specified buffer. 
	 * @param lpKeyState
	 *  Pointer to the 256-byte array that receives the status data for each virtual key.
	 * @return
	 *  If the function succeeds, the return value is nonzero.
	 *  If the function fails, the return value is zero. To get extended error information, call GetLastError. 
	 */
	boolean GetKeyboardState(byte[] lpKeyState);

	/**
	 * This function determines whether a key is up or down at the time the function is called, 
	 * and whether the key was pressed after a previous call to GetAsyncKeyState.
	 * @param vKey
	 *  Specifies one of 256 possible virtual-key codes.
	 * @return
	 *  If the function succeeds, the return value specifies whether the key was pressed since the last 
	 *  call to GetAsyncKeyState, and whether the key is currently up or down. If the most significant 
	 *  bit is set, the key is down.
	 */
	short GetAsyncKeyState(int vKey);

	/**
	 * The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. 
	 * You would install a hook procedure to monitor the system for certain types of events. These 
	 * events are associated either with a specific thread or with all threads in the same desktop 
	 * as the calling thread.
	 * @param idHook
	 *  Specifies the type of hook procedure to be installed.
	 * @param lpfn
	 *  Pointer to the hook procedure.
	 * @param hMod
	 *  Handle to the DLL containing the hook procedure pointed to by the lpfn parameter.
	 * @param dwThreadId
	 *   Specifies the identifier of the thread with which the hook procedure is to be associated.
	 * @return
	 *  If the function succeeds, the return value is the handle to the hook procedure. 
	 *  If the function fails, the return value is NULL. To get extended error information, call GetLastError.
	 */
	HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod,
			int dwThreadId);

	/**
	 * The CallNextHookEx function passes the hook information to the next hook procedure
	 * in the current hook chain. A hook procedure can call this function either before or
	 * after processing the hook information.
	 * @param hhk
	 *  Ignored.
	 * @param nCode
	 *  Specifies the hook code passed to the current hook procedure. The next hook procedure 
	 *  uses this code to determine how to process the hook information.
	 * @param wParam
	 *  Specifies the wParam value passed to the current hook procedure. The meaning of this
	 *  parameter depends on the type of hook associated with the current hook chain.
	 * @param lParam
	 *  Specifies the lParam value passed to the current hook procedure. The meaning of this 
	 *  parameter depends on the type of hook associated with the current hook chain.
	 * @return
	 *  This value is returned by the next hook procedure in the chain. The current hook procedure
	 *  must also return this value. The meaning of the return value depends on the hook type. 
	 */
	LRESULT CallNextHookEx(HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam);

	/**
	 * The CallNextHookEx function passes the hook information to the next hook procedure
	 * in the current hook chain. A hook procedure can call this function either before or
	 * after processing the hook information.
	 * @param hhk
	 *  Ignored.
	 * @param nCode
	 *  Specifies the hook code passed to the current hook procedure. The next hook procedure 
	 *  uses this code to determine how to process the hook information.
	 * @param wParam
	 *  Specifies the wParam value passed to the current hook procedure. The meaning of this
	 *  parameter depends on the type of hook associated with the current hook chain.
	 * @param lParam
	 *  Specifies the lParam value passed to the current hook procedure. The meaning of this 
	 *  parameter depends on the type of hook associated with the current hook chain.
	 * @return
	 *  This value is returned by the next hook procedure in the chain. The current hook procedure
	 *  must also return this value. The meaning of the return value depends on the hook type. 
	 */
	LRESULT CallNextHookEx(HHOOK hhk, int nCode, WPARAM wParam, Pointer lParam);

	/**
	 * The UnhookWindowsHookEx function removes a hook procedure installed in 
	 * a hook chain by the SetWindowsHookEx function. 
	 * @param hhk
	 *  Handle to the hook to be removed. This parameter is a hook handle obtained 
	 *  by a previous call to SetWindowsHookEx.
	 * @return
	 *  If the function succeeds, the return value is nonzero.
	 *  If the function fails, the return value is zero. To get extended error information, call GetLastError.
	 */
	boolean UnhookWindowsHookEx(HHOOK hhk);

	/**
	 * This function retrieves a message from the calling thread's message queue 
	 * and places it in the specified structure. 
	 * @param lpMsg
	 *  Pointer to an MSG structure that receives message information from the thread's message queue.
	 * @param hWnd
	 *  Handle to the window whose messages are to be retrieved. One value has a special meaning.
	 * @param wMsgFilterMin
	 *  Specifies the integer value of the lowest message value to be retrieved.
	 * @param wMsgFilterMax
	 *  Specifies the integer value of the highest message value to be retrieved.
	 * @return
	 *  Nonzero indicates that the function retrieves a message other than WM_QUIT. Zero indicates 
	 *  that the function retrieves the WM_QUIT message, or that lpMsg is an invalid pointer. To 
	 *  get extended error information, call GetLastError.
	 */
	int GetMessage(MSG lpMsg, HWND hWnd, int wMsgFilterMin, int wMsgFilterMax);

	/**
	 * This function checks a thread message queue for a message and places the 
	 * message (if any) in the specified structure.
	 * @param lpMsg
	 *  Pointer to an MSG structure that receives message information.
	 * @param hWnd
	 *  Handle to the window whose messages are to be examined. 
	 * @param wMsgFilterMin
	 *  Specifies the value of the first message in the range of messages to be examined.
	 * @param wMsgFilterMax
	 *  Specifies the value of the last message in the range of messages to be examined. 
	 * @param wRemoveMsg
	 *  Specifies how messages are handled. This parameter can be one of the following values. 
	 * @return
	 *  Nonzero indicates success. Zero indicates failure.
	 */
	boolean PeekMessage(MSG lpMsg, HWND hWnd, int wMsgFilterMin,
			int wMsgFilterMax, int wRemoveMsg);

	/**
	 * This function translates virtual-key messages into character messages. The character messages 
	 * are posted to the calling thread's message queue, to be read the next time the thread calls the 
	 * GetMessage or PeekMessage function. 
	 * @param lpMsg
	 *  Pointer to an MSG structure that contains message information retrieved from the calling thread's 
	 *  message queue by using the GetMessage or PeekMessage function. 
	 * @return
	 *  Nonzero indicates that the message is translated, that is, a character message is posted to the 
	 *  thread's message queue. If the message is WM_KEYDOWN or WM_SYSKEYDOWN, the return value is nonzero, 
	 *  regardless of the translation. Zero indicates that the message is not translated, that is, a 
	 *  character message is not posted to the thread's message queue.
	 */
	boolean TranslateMessage(MSG lpMsg);

	/**
	 * This function dispatches a message to a window procedure. It is typically used
	 * to dispatch a message retrieved by the GetMessage function.
	 * @param lpMsg
	 *  Pointer to an MSG structure that contains the message. 
	 * @return
	 *  The return value specifies the value returned by the window procedure. Although its meaning 
	 *  depends on the message being dispatched, the return value generally is ignored. 
	 */
	LRESULT DispatchMessage(MSG lpMsg);

	/**
	 * This function places a message in the message queue associated with the thread that
	 * created the specified window and then returns without waiting for the thread to process
	 * the message. Messages in a message queue are retrieved by calls to the GetMessage
	 * or PeekMessage function.
	 * @param hWnd
	 *  Handle to the window whose window procedure is to receive the message.
	 * @param msg
	 *  Specifies the message to be posted. 
	 * @param wParam
	 *  Specifies additional message-specific information.
	 * @param lParam
	 *  Specifies additional message-specific information.
	 */
	void PostMessage(HWND hWnd, int msg, WPARAM wParam, LPARAM lParam);

	/**
	 * This function indicates to Windows that a thread has made a request to terminate (quit). 
	 * It is typically used in response to a WM_DESTROY message.
	 * @param nExitCode
	 *  Specifies an application exit code. This value is used as the wParam parameter of 
	 *  the WM_QUIT message.
	 */
	void PostQuitMessage(int nExitCode);

	/**
	 * The GetSystemMetrics function retrieves various system metrics (widths
	 * and heights of display elements) and system configuration settings. All
	 * dimensions retrieved by GetSystemMetrics are in pixels.
	 * @param nIndex
	 *  System metric or configuration setting to retrieve. This
	 *  parameter can be one of the following values. Note that all
	 *  SM_CX* values are widths and all SM_CY* values are heights.
	 *  Also note that all settings designed to return Boolean data
	 *  represent TRUE as any nonzero value, and FALSE as a zero
	 *  value.
	 * @return 
	 *  If the function succeeds, the return value is the requested
	 *  system metric or configuration setting. If the function fails,
	 *  the return value is zero. GetLastError does not provide extended
	 *  error information.
	 */
	public int GetSystemMetrics(int nIndex);
} public static class HANDLE extends PointerType {
    private boolean immutable;
    public HANDLE() { }
    public HANDLE(Pointer p) { setPointer(p); immutable = true; }
        
    /** Override to the appropriate object for INVALID_HANDLE_VALUE. */
    public Object fromNative(Object nativeValue, FromNativeContext context) {
        Object o = super.fromNative(nativeValue, context);
        if (WinBase.INVALID_HANDLE_VALUE.equals(o))
            return WinBase.INVALID_HANDLE_VALUE;
        return o;
    }
        
    public void setPointer(Pointer p) {
        if (immutable) {
            throw new UnsupportedOperationException("immutable reference");
        }
 
        super.setPointer(p);
    }
}
 public class IntByReference extends ByReference {

    public IntByReference() {
        this(0);
    }
    
    public IntByReference(int value) {
        super(4);
        setValue(value);
    }
    
    public void setValue(int value) {
        getPointer().setInt(0, value);
    }
    
    public int getValue() {
        return getPointer().getInt(0);
    }
} public class LongByReference extends ByReference {
    public LongByReference() {
        this(0L);
    }
    
    public LongByReference(long value) {
        super(8);
        setValue(value);
    }
    
    public void setValue(long value) {
        getPointer().setLong(0, value);
    }
    
    public long getValue() {
        return getPointer().getLong(0);
    }
} public class PointerByReference extends ByReference {

    public PointerByReference() {
        this(null);
    }
    
    public PointerByReference(Pointer value) {
        super(Pointer.SIZE);
        setValue(value);
    }
    
    public void setValue(Pointer value) {
        getPointer().setPointer(0, value);
    }
    
    public Pointer getValue() {
        return getPointer().getPointer(0);
    }
} public interface StdCallLibrary extends Library, StdCall {
    /** Constant identifying the w32 stdcall calling convention. */
    int STDCALL_CONVENTION = Function.ALT_CONVENTION;
    /** Provides auto-lookup of stdcall-decorated names. */
    FunctionMapper FUNCTION_MAPPER = new StdCallFunctionMapper();
    /** Interface defining a callback using the w32 stdcall calling convention. 
     */
    interface StdCallCallback extends Callback, StdCall { }
}
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import jnacontrib.jna.Advapi32;
import jnacontrib.jna.Options;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.Kernel32Util;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.LongByReference;
import com.sun.jna.ptr.PointerByReference;
import com.sun.jna.win32.StdCallLibrary;

package org.rzo.yajsw;
/* This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * <p/>
 * This library 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
 * Lesser General Public License for more details.
 */




// TODO: Auto-generated Javadoc
/**
 * The Class WindowsXPProcess.
 */
public class WindowsXPProcess extends AbstractProcess
{

	/**
	 * The Interface MyUser32.
	 */
	public interface MyUser32 extends User32
	{
		// Method declarations, constant and structure definitions go here

		/** The INSTANCE. */
		MyUser32	INSTANCE	= (MyUser32) Native.loadLibrary("User32", MyUser32.class);

		/*
		 * HWND GetForegroundWindow(VOID);
		 */
		/**
		 * Gets the foreground window.
		 *
		 * @return the pointer
		 */
		Pointer GetForegroundWindow();

		/*
		 * DWORD GetWindowThreadProcessId( HWND hWnd, LPDWORD lpdwProcessId );
		 */
		/**
		 * Gets the window thread process id.
		 *
		 * @param hWnd
		 *            the h wnd
		 * @param lpdwProcessId
		 *            the lpdw process id
		 *
		 * @return the int
		 */
		int GetWindowThreadProcessId(Pointer hWnd, IntByReference lpdwProcessId);

		/** The W m_ close. */
		int	WM_CLOSE	= 16;
		int	WM_QUIT		= 18;
		int	WM_DESTROY	= 2;

		/*
		 * BOOL PostThreadMessage( DWORD idThread, UINT Msg, WPARAM wParam,
		 * LPARAM lParam );
		 */
		/**
		 * Post thread message a.
		 *
		 * @param idThread
		 *            the id thread
		 * @param Msg
		 *            the msg
		 * @param wParam
		 *            the w param
		 * @param lParam
		 *            the l param
		 *
		 * @return true, if successful
		 */
		boolean PostThreadMessageA(int idThread, int Msg, int wParam, int lParam);

		/*
		 * DWORD WINAPI WaitForInputIdle( __in HANDLE hProcess, __in DWORD
		 * dwMilliseconds );
		 */
		/**
		 * Wait for input idle.
		 *
		 * @param hProcess
		 *            the h process
		 * @param dwMilliseconds
		 *            the dw milliseconds
		 *
		 * @return the int
		 */
		int WaitForInputIdle(HANDLE hProcess, int dwMilliseconds);

		void PostMessageA(Pointer hWnd, int msg, Pointer wParam, Pointer lParam);

		public interface WNDENUMPROC extends StdCallCallback
		{
			/** Return whether to continue enumeration. */
			boolean callback(Pointer hWnd, int data);
		}

		boolean EnumWindows(WNDENUMPROC lpEnumFunc, int data);

	}

	/**
	 * The Interface MyKernel32.
	 */
	public interface MyKernel32 extends com.sun.jna.platform.win32.Kernel32
	{

		// Method declarations, constant and structure definitions go here

		/** The INSTANCE. */
		MyKernel32	INSTANCE	= (MyKernel32) Native.loadLibrary("kernel32", MyKernel32.class);

		/*
		 * BOOL WINAPI ReadFile( __in HANDLE hFile, __out LPVOID lpBuffer, __in
		 * DWORD nNumberOfBytesToRead, __out_opt LPDWORD lpNumberOfBytesRead,
		 * __inout_opt LPOVERLAPPED lpOverlapped );
		 */

		boolean ReadFile(Pointer hFile, Memory lpBuffer, int nNumberOfBytesToRead, IntByReference lpNumberOfBytesRead, Structure lpOverlapped);

		/*
		 * DWORD WINAPI GetCurrentProcessId(void);
		 */
		/*
		 * (non-Javadoc)
		 *
		 * @see com.sun.jna.examples.win32.Kernel32#GetCurrentProcessId()
		 */
		int GetCurrentProcessId();

		/*
		 * DWORD WINAPI GetProcessIdOfThread( __in HANDLE Thread );
		 */
		/**
		 * Gets the process id of thread.
		 *
		 * @param Thread
		 *            the thread
		 *
		 * @return the int
		 */
		int GetProcessIdOfThread(Pointer Thread);

		/*
		 * BOOL WINAPI CreateProcess( LPCTSTR lpApplicationName, LPTSTR
		 * lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes,
		 * LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD
		 * dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory,
		 * LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION
		 * lpProcessInformation );
		 */
		/**
		 * Creates the process a.
		 *
		 * @param lpApplicationName
		 *            the lp application name
		 * @param lpCommandLine
		 *            the lp command line
		 * @param lpProcessAttributes
		 *            the lp process attributes
		 * @param lpThreadAttributes
		 *            the lp thread attributes
		 * @param bInheritHandles
		 *            the b inherit handles
		 * @param dwCreationFlags
		 *            the dw creation flags
		 * @param lpEnvironment
		 *            the lp environment
		 * @param lpCurrentDirectory
		 *            the lp current directory
		 * @param lpStartupInfo
		 *            the lp startup info
		 * @param lpProcessInformation
		 *            the lp process information
		 *
		 * @return true, if successful
		 */
		boolean CreateProcessA(String lpApplicationName, String lpCommandLine, Structure lpProcessAttributes, Structure lpThreadAttributes,
				boolean bInheritHandles, int dwCreationFlags, Structure lpEnvironment, String lpCurrentDirectory, Structure lpStartupInfo,
				Structure lpProcessInformation);

		boolean CreateProcessW(WString lpApplicationName, WString lpCommandLine, Structure lpProcessAttributes, Structure lpThreadAttributes,
				boolean bInheritHandles, int dwCreationFlags, Memory lpEnvironment, WString lpCurrentDirectory, Structure lpStartupInfo,
				Structure lpProcessInformation);

		/** The CREAT e_ n o_ window. */
		int	CREATE_NO_WINDOW			= 0x08000000;

		/** The CREAT e_ unicod e_ environment. */
		int	CREATE_UNICODE_ENVIRONMENT	= 0x00000400;

		/** The CREAT e_ ne w_ console. */
		int	CREATE_NEW_CONSOLE			= 0x00000010;

		int	DETACHED_PROCESS			= 0x00000008;

		/*
		 * typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE
		 * hThread; DWORD dwProcessId; DWORD dwThreadId; }
		 */

		/**
		 * The Class PROCESS_INFORMATION.
		 */
		public static class PROCESS_INFORMATION extends Structure
		{

			/** The h process. */
			public HANDLE	hProcess	= null;

			/** The h thread. */
			public Pointer	hThread		= null;

			/** The dw process id. */
			public int		dwProcessId	= -1;

			/** The dw thread id. */
			public int		dwThreadId	= -1;

		}

		/*
		 * typedef struct _STARTUPINFO { DWORD cb; LPTSTR lpReserved; LPTSTR
		 * lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD
		 * dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD
		 * dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2;
		 * LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE
		 * hStdError; }
		 */

		/**
		 * The Class STARTUPINFO.
		 */
		public static class STARTUPINFO extends Structure
		{

			/** The cb. */
			public int		cb;

			/** The lp reserved. */
			public WString	lpReserved;

			/** The lp desktop. */
			public WString	lpDesktop;

			/** The lp title. */
			public WString	lpTitle;

			/** The dw x. */
			public int		dwX;

			/** The dw y. */
			public int		dwY;

			/** The dw x size. */
			public int		dwXSize;

			/** The dw y size. */
			public int		dwYSize;

			/** The dw x count chars. */
			public int		dwXCountChars;

			/** The dw y count chars. */
			public int		dwYCountChars;

			/** The dw fill attribute. */
			public int		dwFillAttribute;

			/** The dw flags. */
			public int		dwFlags;

			/** The w show window. */
			public short	wShowWindow;

			/** The cb reserved2. */
			public short	cbReserved2;

			/** The lp reserved2. */
			public Pointer	lpReserved2;

			/** The h std input. */
			public Pointer	hStdInput;

			/** The h std output. */
			public Pointer	hStdOutput;

			/** The h std error. */
			public Pointer	hStdError;
		}

		/** The START f_ usestdhandles. */
		int	STARTF_USESTDHANDLES		= 256;

		/** The IDL e_ priorit y_ class. */
		int	IDLE_PRIORITY_CLASS			= 0x00000040;

		/** The BELO w_ norma l_ priorit y_ class. */
		int	BELOW_NORMAL_PRIORITY_CLASS	= 0x00004000;

		/** The NORMA l_ priorit y_ class. */
		int	NORMAL_PRIORITY_CLASS		= 0x00000020;

		/** The ABOV e_ norma l_ priorit y_ class. */
		int	ABOVE_NORMAL_PRIORITY_CLASS	= 0x00008000;

		/** The HIG h_ priorit y_ class. */
		int	HIGH_PRIORITY_CLASS			= 0x00000080;

		/** The REALTIM e_ priorit y_ class. */
		int	REALTIME_PRIORITY_CLASS		= 0x00000100;

		/*
		 * DWORD WINAPI WaitForSingleObject( HANDLE hHandle, DWORD
		 * dwMilliseconds );
		 */
		/**
		 * Wait for single object.
		 *
		 * @param handle
		 *            the handle
		 * @param dwMilliseconds
		 *            the dw milliseconds
		 *
		 * @return the int
		 */
		int WaitForSingleObject(Pointer handle, int dwMilliseconds);

		/** The INFINITE. */
		int	INFINITE	= 0xFFFFFFFF;

		/*
		 * BOOL WINAPI GetExitCodeProcess( HANDLE hProcess, LPDWORD lpExitCode
		 * );
		 */
		/**
		 * Gets the exit code process.
		 *
		 * @param handle
		 *            the h process
		 * @param lpExitCode
		 *            the lp exit code
		 *
		 * @return true, if successful
		 */
		boolean GetExitCodeProcess(HANDLE handle, IntByReference lpExitCode);

		/** The STIL l_ active. */
		int	STILL_ACTIVE	= 0x103;

		/*
		 * BOOL WINAPI TerminateProcess( HANDLE hProcess, UINT uExitCode );
		 */

		/**
		 * Terminate process.
		 *
		 * @param handle
		 *            the h process
		 * @param uExitCode
		 *            the u exit code
		 *
		 * @return true, if successful
		 */
		boolean TerminateProcess(HANDLE handle, int uExitCode);

		/*
		 * BOOL WINAPI GetProcessAffinityMask( __in HANDLE hProcess, __out
		 * PDWORD_PTR lpProcessAffinityMask, __out PDWORD_PTR
		 * lpSystemAffinityMask );
		 */
		/**
		 * Gets the process affinity mask.
		 *
		 * @param handle
		 *            the h process
		 * @param lpProcessAffinityMask
		 *            the lp process affinity mask
		 * @param lpSystemAffinityMask
		 *            the lp system affinity mask
		 *
		 * @return true, if successful
		 */
		boolean GetProcessAffinityMask(HANDLE handle, IntByReference lpProcessAffinityMask, IntByReference lpSystemAffinityMask);

		/*
		 * BOOL WINAPI SetProcessAffinityMask( __in HANDLE hProcess, __in
		 * DWORD_PTR dwProcessAffinityMask );
		 */
		/**
		 * Sets the process affinity mask.
		 *
		 * @param handle
		 *            the h process
		 * @param dwProcessAffinityMask
		 *            the dw process affinity mask
		 *
		 * @return true, if successful
		 */
		boolean SetProcessAffinityMask(HANDLE handle, int dwProcessAffinityMask);

		/*
		 * BOOL WINAPI CloseHandle( HANDLE hObject );
		 */
		/**
		 * Close handle.
		 *
		 * @param hObject
		 *            the h object
		 *
		 * @return true, if successful
		 */
		public boolean CloseHandle(Pointer hObject);

		/*
		 * HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, DWORD
		 * th32ProcessID );
		 */
		/**
		 * Creates the toolhelp32 snapshot.
		 *
		 * @param dwFlags
		 *            the dw flags
		 * @param th32ProcessID
		 *            the th32 process id
		 *
		 * @return the pointer
		 */
		Pointer CreateToolhelp32Snapshot(int dwFlags, int th32ProcessID);

		/** The T h32 c s_ snapprocess. */
		int	TH32CS_SNAPPROCESS	= 0x2;

		int	WAIT_FAILED			= 0xFFFFFFFF;
		int	WAIT_TIMEOUT		= 0x00000102;
		int	WAIT_OBJECT_0		= 0x00000000;
		int	WAIT_ABANDONED		= 0x00000080;

		/*
		 * BOOL WINAPI Process32First( HANDLE hSnapshot, LPPROCESSENTRY32 lppe
		 * );
		 */
		/**
		 * Process32 first.
		 *
		 * @param hSnapshot
		 *            the h snapshot
		 * @param lppe
		 *            the lppe
		 *
		 * @return true, if successful
		 */
		boolean Process32First(Pointer hSnapshot, Structure lppe);

		/*
		 * typedef struct tagPROCESSENTRY32 { DWORD dwSize; DWORD cntUsage;
		 * DWORD th32ProcessID; ULONG_PTR th32DefaultHeapID; DWORD th32ModuleID;
		 * DWORD cntThreads; DWORD th32ParentProcessID; LONG pcPriClassBase;
		 * DWORD dwFlags; TCHAR szExeFile[MAX_PATH]; } PROCESSENTRY32,
		 * PPROCESSENTRY32;
		 */

		/**
		 * The Class PROCESSENTRY32.
		 */
		public static class PROCESSENTRY32 extends Structure
		{

			/** The dw size. */
			public int		dwSize;

			/** The cnt usage. */
			public int		cntUsage;

			/** The th32 process id. */
			public int		th32ProcessID;

			/** The th32 default heap id. */
			public int		th32DefaultHeapID;

			/** The th32 module id. */
			public int		th32ModuleID;

			/** The cnt threads. */
			public int		cntThreads;

			/** The th32 parent process id. */
			public int		th32ParentProcessID;

			/** The pc pri class base. */
			public int		pcPriClassBase;

			/** The dw flags. */
			public int		dwFlags;

			/** The sz exe file. */
			public char[]	szExeFile;
		}

		/** The MA x_ path. */
		int	MAX_PATH	= 260;

		/*
		 * BOOL WINAPI Process32Next( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );
		 */
		/**
		 * Process32 next.
		 *
		 * @param hSnapshot
		 *            the h snapshot
		 * @param lppe
		 *            the lppe
		 *
		 * @return true, if successful
		 */
		boolean Process32Next(Pointer hSnapshot, Structure lppe);

		/*
		 * HANDLE WINAPI OpenProcess( DWORD dwDesiredAccess, BOOL
		 * bInheritHandle, DWORD dwProcessId );
		 */
		/**
		 * Open process.
		 *
		 * @param dwDesiredAccess
		 *            the dw desired access
		 * @param bInheritHandle
		 *            the b inherit handle
		 * @param dwProcessId
		 *            the dw process id
		 *
		 * @return the pointer
		 */
		HANDLE OpenProcess(int dwDesiredAccess, boolean bInheritHandle, int dwProcessId);

		/** The PROCES s_ terminate. */
		int	PROCESS_TERMINATE			= 1;

		/** The PROCES s_ quer y_ information. */
		int	PROCESS_QUERY_INFORMATION	= 1024;

		/** The STANDAR d_ right s_ required. */
		int	STANDARD_RIGHTS_REQUIRED	= 0xF0000;

		/** The SYNCHRONIZE. */
		int	SYNCHRONIZE					= 0x100000;

		/** The PROCES s_ al l_ access. */
		int	PROCESS_ALL_ACCESS			= STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF;

		/*
		 * BOOL WINAPI GetProcessTimes( __in HANDLE hProcess, __out LPFILETIME
		 * lpCreationTime, __out LPFILETIME lpExitTime, __out LPFILETIME
		 * lpKernelTime, __out LPFILETIME lpUserTime );
		 */
		/**
		 * Gets the process times.
		 *
		 * @param handle
		 *            the h process
		 * @param lpCreationTime
		 *            the lp creation time
		 * @param lpExitTime
		 *            the lp exit time
		 * @param lpKernelTime
		 *            the lp kernel time
		 * @param lpUserTime
		 *            the lp user time
		 *
		 * @return true, if successful
		 */
		boolean GetProcessTimes(HANDLE handle, LongByReference lpCreationTime, LongByReference lpExitTime, LongByReference lpKernelTime,
				LongByReference lpUserTime);

		/*
		 * BOOL WINAPI CreatePipe( __out PHANDLE hReadPipe, __out PHANDLE
		 * hWritePipe, __in LPSECURITY_ATTRIBUTES lpPipeAttributes, __in DWORD
		 * nSize );
		 */
		/**
		 * Creates the pipe.
		 *
		 * @param hReadPipe
		 *            the h read pipe
		 * @param hWritePipe
		 *            the h write pipe
		 * @param lpPipeAttributes
		 *            the lp pipe attributes
		 * @param nSize
		 *            the n size
		 *
		 * @return the int
		 */
		int CreatePipe(PointerByReference hReadPipe, PointerByReference hWritePipe, Structure lpPipeAttributes, int nSize);

		/*
		 * typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID
		 * lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES,
		 * PSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES;
		 */

		/**
		 * The Class SECURITY_ATTRIBUTES.
		 */
		public static class SECURITY_ATTRIBUTES extends Structure
		{

			/** The n length. */
			public int		nLength;

			/** The lp security descriptor. */
			public Pointer	lpSecurityDescriptor;

			/** The b inherit handle. */
			public boolean	bInheritHandle;
		}

		/*
		 * BOOL WINAPI SetHandleInformation( __in HANDLE hObject, __in DWORD
		 * dwMask, __in DWORD dwFlags );
		 */
		/**
		 * Sets the handle information.
		 *
		 * @param hObject
		 *            the h object
		 * @param dwMask
		 *            the dw mask
		 * @param dwFlags
		 *            the dw flags
		 *
		 * @return true, if successful
		 */
		boolean SetHandleInformation(Pointer hObject, int dwMask, int dwFlags);

		/** The HANDL e_ fla g_ inherit. */
		int	HANDLE_FLAG_INHERIT				= 0x00000001;
		int	HANDLE_FLAG_PROTECT_FROM_CLOSE	= 0x00000002;

		/*
		 * HANDLE WINAPI CreateNamedPipe( __in LPCTSTR lpName, __in DWORD
		 * dwOpenMode, __in DWORD dwPipeMode, __in DWORD nMaxInstances, __in
		 * DWORD nOutBufferSize, __in DWORD nInBufferSize, __in DWORD
		 * nDefaultTimeOut, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes
		 * );
		 */
		/**
		 * Creates the named pipe a.
		 *
		 * @param lpName
		 *            the lp name
		 * @param dwOpenMode
		 *            the dw open mode
		 * @param dwPipeMode
		 *            the dw pipe mode
		 * @param nMaxInstances
		 *            the n max instances
		 * @param nOutBufferSize
		 *            the n out buffer size
		 * @param nInBufferSize
		 *            the n in buffer size
		 * @param nDefaultTimeOut
		 *            the n default time out
		 * @param lpSecurityAttributes
		 *            the lp security attributes
		 *
		 * @return the pointer
		 */
		Pointer CreateNamedPipeA(String lpName, int dwOpenMode, int dwPipeMode, int nMaxInstances, int nOutBufferSize, int nInBufferSize,
				int nDefaultTimeOut, SECURITY_ATTRIBUTES lpSecurityAttributes);

		/** The PIP e_ acces s_ outbound. */
		int	PIPE_ACCESS_OUTBOUND	= 0x00000002;

		/** The PIP e_ acces s_ inbound. */
		int	PIPE_ACCESS_INBOUND		= 0x00000001;

		/** The PIP e_ wait. */
		int	PIPE_WAIT				= 0x00000000;

		/** The PIP e_ nowait. */
		int	PIPE_NOWAIT				= 0x00000001;

		/** The GENERI c_ read. */
		int	GENERIC_READ			= 0x80000000;

		/**
		 * Creates the file a.
		 *
		 * @param lpFileName
		 *            the lp file name
		 * @param dwDesiredAccess
		 *            the dw desired access
		 * @param dwShareMode
		 *            the dw share mode
		 * @param lpSecurityAttributes
		 *            the lp security attributes
		 * @param dwCreationDisposition
		 *            the dw creation disposition
		 * @param dwFlagsAndAttributes
		 *            the dw flags and attributes
		 * @param hTemplateFile
		 *            the h template file
		 *
		 * @return the pointer
		 */
		Pointer CreateFileA(String lpFileName, int dwDesiredAccess, int dwShareMode, SECURITY_ATTRIBUTES lpSecurityAttributes,
				int dwCreationDisposition, int dwFlagsAndAttributes, Pointer hTemplateFile);

		/*
		 * BOOL WINAPI ConnectNamedPipe( __in HANDLE hNamedPipe, __inout_opt
		 * LPOVERLAPPED lpOverlapped );
		 */
		/**
		 * Connect named pipe.
		 *
		 * @param hNamedPipe
		 *            the h named pipe
		 * @param lpOverlapped
		 *            the lp overlapped
		 *
		 * @return true, if successful
		 */
		boolean ConnectNamedPipe(Pointer hNamedPipe, PointerByReference lpOverlapped);

		/** The INVALI d_ handl e_ value. */
		Pointer	INVALID_HANDLE_VALUE	= Pointer.createConstant(-1);

		/*
		 * BOOL WINAPI WaitNamedPipe( __in LPCTSTR lpNamedPipeName, __in DWORD
		 * nTimeOut );
		 */
		/**
		 * Wait named pipe a.
		 *
		 * @param lpNamedPipeName
		 *            the lp named pipe name
		 * @param nTimeOut
		 *            the n time out
		 *
		 * @return true, if successful
		 */
		boolean WaitNamedPipeA(String lpNamedPipeName, int nTimeOut);

		/** The NMPWAI t_ us e_ defaul t_ wait. */
		int	NMPWAIT_USE_DEFAULT_WAIT	= 0;

		/** The NMPWAI t_ wai t_ forever. */
		int	NMPWAIT_WAIT_FOREVER		= 0xffffffff;

		/*
		 * BOOL WINAPI SetCurrentDirectory( __in LPCTSTR lpPathName );
		 */
		boolean SetCurrentDirectoryA(String lpPathName);

		/*
		 * typedef struct _MEMORY_BASIC_INFORMATION { PVOID BaseAddress; PVOID
		 * AllocationBase; DWORD AllocationProtect; SIZE_T RegionSize; DWORD
		 * State; DWORD Protect; DWORD Type; } MEMORY_BASIC_INFORMATION,
		 * *PMEMORY_BASIC_INFORMATION;
		 */
		public static class MEMORY_BASIC_INFORMATION extends Structure
		{
			public Pointer	BaseAddress;
			public Pointer	AllocationBase;
			public int		AllocationProtect;
			public int		RegionSize;
			public int		State;
			public int		Protect;
			public int		Type;
		}

		public static int	PAGE_NOACCESS	= 0x01;
		public static int	PAGE_EXECUTE	= 0x10;

		/*
		 * SIZE_T WINAPI VirtualQueryEx( __in HANDLE hProcess, __in_opt LPCVOID
		 * lpAddress, __out PMEMORY_BASIC_INFORMATION lpBuffer, __in SIZE_T
		 * dwLength );
		 */
		int VirtualQueryEx(Pointer hProcess, Pointer lpAddress, Pointer lpBuffer, int dwLength);

	}

	/**
	 * The Interface Ntdll.
	 */
	public interface Ntdll extends com.sun.jna.win32.StdCallLibrary
	{

		/** The INSTANCE. */
		Ntdll	INSTANCE	= (Ntdll) Native.loadLibrary("Ntdll", Ntdll.class);

		/*
		 * NTOSAPI NTSTATUS NTAPI ZwReadVirtualMemory( /IN/ HANDLE
		 * ProcessHandle, /IN/ PVOID BaseAddress, /OUT/ PVOID Buffer, /IN/ ULONG
		 * BufferLength, /OUT/ PULONG ReturnLength /OPTIONAL/);
		 */

		/**
		 * Zw read virtual memory.
		 *
		 * @param ProcessHandle
		 *            the process handle
		 * @param BaseAddress
		 *            the base address
		 * @param Buffer
		 *            the buffer
		 * @param BufferLength
		 *            the buffer length
		 * @param ReturnLength
		 *            the return length
		 *
		 * @return the int
		 */
		int ZwReadVirtualMemory(Pointer ProcessHandle, Pointer BaseAddress, Pointer Buffer, int BufferLength, IntByReference ReturnLength);

		/*
		 * NTSTATUS WINAPI ZwQueryInformationProcess( __in HANDLE ProcessHandle,
		 * __in PROCESSINFOCLASS ProcessInformationClass, __out PVOID
		 * ProcessInformation, __in ULONG ProcessInformationLength, __out_opt
		 * PULONG ReturnLength );
		 */
		/**
		 * Zw query information process.
		 *
		 * @param process
		 *            the process handle
		 * @param ProcessInformationClass
		 *            the process information class
		 * @param ProcessInformation
		 *            the process information
		 * @param ProcessInformationLength
		 *            the process information length
		 * @param ReturnLength
		 *            the return length
		 *
		 * @return the int
		 */
		int ZwQueryInformationProcess(HANDLE process, int ProcessInformationClass, Pointer ProcessInformation, int ProcessInformationLength,
				IntByReference ReturnLength);

		/*
		 * typedef struct _PROCESS_BASIC_INFORMATION { PVOID Reserved1; PPEB
		 * PebBaseAddress; PVOID Reserved2[2]; ULONG_PTR UniqueProcessId; PVOID
		 * Reserved3; } PROCESS_BASIC_INFORMATION;
		 */
		/**
		 * The Class PROCESS_BASIC_INFORMATION.
		 */
		class PROCESS_BASIC_INFORMATION extends Structure
		{

			/** The Reserved1. */
			public Pointer	Reserved1;

			/** The Peb base address. */
			public Pointer	PebBaseAddress;

			/** The Reserved2. */
			public int[]	Reserved2	= new int[2];

			/** The Unique process id. */
			public Pointer	UniqueProcessId;

			/** The Reserved3. */
			public Pointer	Reserved3;
		}

		/*
		 * typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE
		 * Reserved2[1]; PVOID Reserved3[2]; PPEB_LDR_DATA Ldr;
		 * PRTL_USER_PROCESS_PARAMETERS ProcessParameters; BYTE Reserved4[104];
		 * PVOID Reserved5[52]; PPS_POST_PROCESS_INIT_ROUTINE
		 * PostProcessInitRoutine; BYTE Reserved6[128]; PVOID Reserved7[1];
		 * ULONG SessionId; } PEB, PPEB;
		 */
		/**
		 * The Class PEB.
		 */
		class PEB extends Structure
		{

			/** The Reserved1. */
			public byte[]	Reserved1	= new byte[2];

			/** The Being debugged. */
			public byte		BeingDebugged;

			/** The Reserved2. */
			public byte		Reserved2;

			/** The Reserved3. */
			public int[]	Reserved3	= new int[2];

			/** The Ldr. */
			public Pointer	Ldr;

			/** The Process parameters. */
			public Pointer	ProcessParameters;

			/** The Reserved4. */
			public byte[]	Reserved4	= new byte[104];

			/** The Reserved5. */
			public int[]	Reserved5	= new int[52];

			/** The Post process init routine. */
			public Pointer	PostProcessInitRoutine;

			/** The Reserved6. */
			public byte[]	Reserved6	= new byte[128];

			/** The Reserved7. */
			public int[]	Reserved7	= new int[1];

			/** The Session id. */
			public int		SessionId;

		}

		/*
		 * typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE
		 * Reserved2[21]; PPEB_LDR_DATA LoaderData; PRTL_USER_PROCESS_PARAMETERS
		 * ProcessParameters; BYTE Reserved3[520]; PPS_POST_PROCESS_INIT_ROUTINE
		 * PostProcessInitRoutine; BYTE Reserved4[136]; ULONG SessionId; } PEB;
		 */
		class PEB64 extends Structure
		{
			public byte[]	Reserved1	= new byte[2];
			public byte		BeingDebugged;
			public byte[]	Reserved2	= new byte[21];		;
			public Pointer	Ldr;
			public Pointer	ProcessParameters;
			public byte[]	Reserved3	= new byte[520];
			public Pointer	PostProcessInitRoutine;
			public byte[]	Reserved4	= new byte[136];
			public int		SessionId;
		}

		/*
		 * typedef struct _RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; 16
		 * PVOID Reserved2[10]; 40 UNICODE_STRING ImagePathName; UNICODE_STRING
		 * CommandLine; } RTL_USER_PROCESS_PARAMETERS,
		 * PRTL_USER_PROCESS_PARAMETERS;
		 *
		 * typedef struct _RTL_USER_PROCESS_PARAMETERS { ULONG MaximumLength; 4
		 * ULONG Length; 8 ULONG Flags; 12 ULONG DebugFlags; 16 PVOID
		 * ConsoleHandle; 4 ULONG ConsoleFlags; 8 HANDLE StdInputHandle; 12
		 * HANDLE StdOutputHandle; 16 HANDLE StdErrorHandle; 20 UNICODE_STRING
		 * CurrentDirectoryPath; 28 HANDLE CurrentDirectoryHandle; 32
		 * UNICODE_STRING DllPath; 40 UNICODE_STRING ImagePathName; 48
		 * UNICODE_STRING CommandLine; 56 PVOID Environment; 4 ULONG
		 * StartingPositionLeft; 8 ULONG StartingPositionTop; 12 ULONG Width; 16
		 * ULONG Height; 20 ULONG CharWidth; 24 ULONG CharHeight; 28 ULONG
		 * ConsoleTextAttributes; 32 ULONG WindowFlags; 36 ULONG
		 * ShowWindowFlags; 40 UNICODE_STRING WindowTitle; 48 UNICODE_STRING
		 * DesktopName; UNICODE_STRING ShellInfo; UNICODE_STRING RuntimeData;
		 * RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; }
		 * RTL_USER_PROCESS_PARAMETERS,PRTL_USER_PROCESS_PARAMETERS;
		 */

		/**
		 * The Class RTL_USER_PROCESS_PARAMETERS.
		 */
		class RTL_USER_PROCESS_PARAMETERS extends Structure
		{

			/** The Reserved1. */
			public byte[]			Reserved1	= new byte[16];

			/** The Reserved2. */
			public int[]			Reserved2	= new int[5];
			// public int[] Reserved2 = new int[16];

			public UNICODE_STRING	CurrentDirectoryPath;
			public Pointer			CurrentDirectoryHandle;
			public UNICODE_STRING	DllPath;

			/** The Image path name. */
			public UNICODE_STRING	ImagePathName;

			/** The Command line. */
			public UNICODE_STRING	CommandLine;
			public Pointer			Environment;				// new

			public int[]			reserved3	= new int[9];	// int[10];

			public UNICODE_STRING	WindowTitle;

		}

		/*
		 * typedef struct _LSA_UNICODE_STRING { USHORT Length; USHORT
		 * MaximumLength; PWSTR Buffer; } LSA_UNICODE_STRING,
		 * PLSA_UNICODE_STRING, UNICODE_STRING, PUNICODE_STRING;
		 */
		/**
		 * The Class UNICODE_STRING.
		 */
		class UNICODE_STRING extends Structure
		{

			/** The Length. */
			public short	Length	= 0;

			/** The Maximum length. */
			public short	MaximumLength;

			/** The Buffer. */
			public Pointer	Buffer;
		}
	}


	public interface MyAdvapi extends Advapi32
