All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.sun.jna.platform.win32.Advapi32Util Maven / Gradle / Ivy

/* Copyright (c) 2010 Daniel Doubrovkine, All Rights Reserved
 * 
 * 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.
 * 
 * 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.  
 */
package com.sun.jna.platform.win32;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinNT.EVENTLOGRECORD;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinNT.HANDLEByReference;
import com.sun.jna.platform.win32.WinNT.PSID;
import com.sun.jna.platform.win32.WinNT.PSIDByReference;
import com.sun.jna.platform.win32.WinNT.SID_AND_ATTRIBUTES;
import com.sun.jna.platform.win32.WinNT.SID_NAME_USE;
import com.sun.jna.platform.win32.WinReg.HKEY;
import com.sun.jna.platform.win32.WinReg.HKEYByReference;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;

/**
 * Advapi32 utility API.
 * @author dblock[at]dblock.org
 */
public abstract class Advapi32Util {
	
	/**
	 * An account.
	 */
	public static class Account {
		/**
		 * Account name.
		 */
		public String name;
		/**
		 * Account domain.
		 */
		public String domain;
		/**
		 * Account SID.
		 */
		public byte[] sid;
		/**
		 * String representation of the account SID.
		 */
		public String sidString;
		/**
		 * Account type, one of SID_NAME_USE.
		 */
		public int accountType;
		/**
		 * Fully qualified account name.
		 */
		public String fqn;
	}
	
	/**
	 * Retrieves the name of the user associated with the current thread.
	 * @return A user name.
	 */
	public static String getUserName() {
		char[] buffer = new char[128];
		IntByReference len = new IntByReference(buffer.length);
		boolean result = Advapi32.INSTANCE.GetUserNameW(buffer, len); 
		
		if (! result) {
			
			int rc = Kernel32.INSTANCE.GetLastError();

			switch(rc) {
			case W32Errors.ERROR_INSUFFICIENT_BUFFER:
				buffer = new char[len.getValue()];
				break;
			default:
				throw new Win32Exception(Native.getLastError());
			}
			
			result = Advapi32.INSTANCE.GetUserNameW(buffer, len);
		}
		
		if (! result) {
			throw new Win32Exception(Native.getLastError());
		}
		
		return Native.toString(buffer);
	}

	/**
	 * Retrieves a security identifier (SID) for the account on the current system.
	 * @param accountName Specifies the account name.
	 * @return A structure containing the account SID;
	 */
	public static Account getAccountByName(String accountName) {
		return getAccountByName(null, accountName);
	}
			
	/**
	 * Retrieves a security identifier (SID) for a given account.
	 * @param systemName Name of the system.
	 * @param accountName Account name.
	 * @return A structure containing the account SID.
	 */
	public static Account getAccountByName(String systemName, String accountName) {
		IntByReference pSid = new IntByReference(0);
		IntByReference cchDomainName = new IntByReference(0);
		PointerByReference peUse = new PointerByReference();
		
		if (Advapi32.INSTANCE.LookupAccountName(systemName, accountName, null, pSid, null, cchDomainName, peUse)) {
			throw new RuntimeException("LookupAccountNameW was expected to fail with ERROR_INSUFFICIENT_BUFFER");
		}
		
		int rc = Kernel32.INSTANCE.GetLastError();
		if (pSid.getValue() == 0 || rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
			throw new Win32Exception(rc);
		}

		Memory sidMemory = new Memory(pSid.getValue());
		PSID result = new PSID(sidMemory);
		char[] referencedDomainName = new char[cchDomainName.getValue() + 1]; 

		if (! Advapi32.INSTANCE.LookupAccountName(systemName, accountName, result, pSid, referencedDomainName, cchDomainName, peUse)) {
			throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
		}
		
		Account account = new Account();
		account.accountType = peUse.getPointer().getInt(0);
		account.name = accountName;

        String[] accountNamePartsBs = accountName.split("\\\\", 2);
        String[] accountNamePartsAt = accountName.split("@", 2);

        if (accountNamePartsBs.length == 2) {
        	account.name = accountNamePartsBs[1];
        } else if (accountNamePartsAt.length == 2) {
        	account.name = accountNamePartsAt[0];
        } else {
            account.name = accountName;
        }
		
		if (cchDomainName.getValue() > 0) {
			account.domain = Native.toString(referencedDomainName);
			account.fqn = account.domain + "\\" + account.name;
		} else {
			account.fqn = account.name;
		}
		
		account.sid = result.getBytes();
		account.sidString = convertSidToStringSid(new PSID(account.sid));
		return account;
	}

	/**
	 * Get the account by SID on the local system.
	 * 
	 * @param sid SID.
	 * @return Account.
	 */
	public static Account getAccountBySid(PSID sid) {
		return getAccountBySid(null, sid);
	}
	
	/**
	 * Get the account by SID.
	 * 
	 * @param systemName Name of the system.
	 * @param sid SID.
	 * @return Account.
	 */
	public static Account getAccountBySid(String systemName, PSID sid) {
    	IntByReference cchName = new IntByReference();
    	IntByReference cchDomainName = new IntByReference();
    	PointerByReference peUse = new PointerByReference();

    	if (Advapi32.INSTANCE.LookupAccountSid(null, sid, 
    			null, cchName, null, cchDomainName, peUse)) {
			throw new RuntimeException("LookupAccountSidW was expected to fail with ERROR_INSUFFICIENT_BUFFER");
    	}
    	
    	int rc = Kernel32.INSTANCE.GetLastError();
		if (cchName.getValue() == 0 || rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
			throw new Win32Exception(rc);
		}    	
		
		char[] domainName = new char[cchDomainName.getValue()];
		char[] name = new char[cchName.getValue()];
    	
		if (! Advapi32.INSTANCE.LookupAccountSid(null, sid, 
    			name, cchName, domainName, cchDomainName, peUse)) {
    		throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    	}
		
		Account account = new Account();
		account.accountType = peUse.getPointer().getInt(0);
		account.name = Native.toString(name);
		
		if (cchDomainName.getValue() > 0) {
			account.domain = Native.toString(domainName);
			account.fqn = account.domain + "\\" + account.name;
		} else {
			account.fqn = account.name;
		}
		
		account.sid = sid.getBytes();
		account.sidString = convertSidToStringSid(sid);
		return account;
	}
	
	/**
	 * Convert a security identifier (SID) to a string format suitable for display, 
	 * storage, or transmission.
	 * @param sid SID bytes.
	 * @return String SID.
	 */
	public static String convertSidToStringSid(PSID sid) {
		PointerByReference stringSid = new PointerByReference();
		if (! Advapi32.INSTANCE.ConvertSidToStringSid(sid, stringSid)) {
			throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
		}
		String result = stringSid.getValue().getString(0, true);
		Kernel32.INSTANCE.LocalFree(stringSid.getValue()); 
		return result;
	}
	
	/**
	 * Convert a string representation of a security identifier (SID) to 
	 * a binary format.
	 * @param sidString 
	 * 	String SID.
	 * @return SID bytes.
	 */
	public static byte[] convertStringSidToSid(String sidString) {
		PSIDByReference pSID = new PSIDByReference();
		if (! Advapi32.INSTANCE.ConvertStringSidToSid(sidString, pSID)) {
			throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
		}
		return pSID.getValue().getBytes();
	}
	
	/**
	 * Compares a SID to a well known SID and returns TRUE if they match.
	 * @param sidString
	 *  String representation of a SID.
	 * @param wellKnownSidType
	 *  Member of the WELL_KNOWN_SID_TYPE enumeration to compare with the SID at pSid.
	 * @return
	 *  True if the SID is of the well-known type, false otherwise.
	 */
	public static boolean isWellKnownSid(String sidString, int wellKnownSidType) {
		PSIDByReference pSID = new PSIDByReference();
		if (! Advapi32.INSTANCE.ConvertStringSidToSid(sidString, pSID)) {
			throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
		}
		return Advapi32.INSTANCE.IsWellKnownSid(pSID.getValue(), wellKnownSidType);
	}

	/**
	 * Compares a SID to a well known SID and returns TRUE if they match.
	 * @param sidBytes
	 *  Byte representation of a SID.
	 * @param wellKnownSidType
	 *  Member of the WELL_KNOWN_SID_TYPE enumeration to compare with the SID at pSid.
	 * @return
	 *  True if the SID is of the well-known type, false otherwise.
	 */
	public static boolean isWellKnownSid(byte[] sidBytes, int wellKnownSidType) {
		PSID pSID = new PSID(sidBytes);
		return Advapi32.INSTANCE.IsWellKnownSid(pSID, wellKnownSidType);		
	}
	
	/**
	 * Get an account name from a string SID on the local machine.
	 * 
	 * @param sidString SID.
	 * @return Account.
	 */
	public static Account getAccountBySid(String sidString) {
		return getAccountBySid(null, sidString); 
	}
	
	/**
	 * Get an account name from a string SID.
	 * 
	 * @param systemName System name.
	 * @param sidString SID.
	 * @return Account.
	 */
	public static Account getAccountBySid(String systemName, String sidString) {
		return getAccountBySid(systemName, new PSID(convertStringSidToSid(sidString))); 
	}
	
	/**
	 * This function returns the groups associated with a security token,
	 * such as a user token.
	 * 
	 * @param hToken Token.
	 * @return Token groups.
	 */
	public static Account[] getTokenGroups(HANDLE hToken) {
    	// get token group information size
        IntByReference tokenInformationLength = new IntByReference();
        if (Advapi32.INSTANCE.GetTokenInformation(hToken, 
        		WinNT.TOKEN_INFORMATION_CLASS.TokenGroups, null, 0, tokenInformationLength)) {
        	throw new RuntimeException("Expected GetTokenInformation to fail with ERROR_INSUFFICIENT_BUFFER");
        }
        int rc = Kernel32.INSTANCE.GetLastError();
        if (rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
        	throw new Win32Exception(rc);
        }
        // get token group information
		WinNT.TOKEN_GROUPS groups = new WinNT.TOKEN_GROUPS(tokenInformationLength.getValue());
        if (! Advapi32.INSTANCE.GetTokenInformation(hToken,
        		WinNT.TOKEN_INFORMATION_CLASS.TokenGroups, groups, 
        		tokenInformationLength.getValue(), tokenInformationLength)) {
        	throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
        }
        ArrayList userGroups = new ArrayList(); 
        // make array of names
    	for (SID_AND_ATTRIBUTES sidAndAttribute : groups.getGroups()) {
    		Account group = null;
    		try {
    			group = Advapi32Util.getAccountBySid(sidAndAttribute.Sid);
    		} catch(Exception e) {
    			group = new Account();
        		group.sid = sidAndAttribute.Sid.getBytes();
        		group.sidString = Advapi32Util.convertSidToStringSid(sidAndAttribute.Sid);
    			group.name = group.sidString;
    			group.fqn = group.sidString;
        		group.accountType = SID_NAME_USE.SidTypeGroup;
    		}
    		userGroups.add(group);
    	}
        return userGroups.toArray(new Account[0]);
	}

	/**
	 * This function returns the information about the user who owns a security token,
	 * 
	 * @param hToken Token.
	 * @return Token user.
	 */
	public static Account getTokenAccount(HANDLE hToken) {
    	// get token group information size
        IntByReference tokenInformationLength = new IntByReference();
        if (Advapi32.INSTANCE.GetTokenInformation(hToken, 
        		WinNT.TOKEN_INFORMATION_CLASS.TokenUser, null, 0, tokenInformationLength)) {
        	throw new RuntimeException("Expected GetTokenInformation to fail with ERROR_INSUFFICIENT_BUFFER");
        }
        int rc = Kernel32.INSTANCE.GetLastError();
        if (rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
        	throw new Win32Exception(rc);
        }
        // get token user information
		WinNT.TOKEN_USER user = new WinNT.TOKEN_USER(tokenInformationLength.getValue());
        if (! Advapi32.INSTANCE.GetTokenInformation(hToken,
        		WinNT.TOKEN_INFORMATION_CLASS.TokenUser, user, 
        		tokenInformationLength.getValue(), tokenInformationLength)) {
        	throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
        }        
        return getAccountBySid(user.User.Sid);
	}
	
	/**
	 * Return the group memberships of the currently logged on user.
	 * @return An array of groups.
	 */
	public static Account[] getCurrentUserGroups() {		
    	HANDLEByReference phToken = new HANDLEByReference();    	
    	try {
    		// open thread or process token
        	HANDLE threadHandle = Kernel32.INSTANCE.GetCurrentThread();
        	if (! Advapi32.INSTANCE.OpenThreadToken(threadHandle, 
        			WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, true, phToken)) {
            	if (W32Errors.ERROR_NO_TOKEN != Kernel32.INSTANCE.GetLastError()) {
            		throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
            	}        	
            	HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
            	if (! Advapi32.INSTANCE.OpenProcessToken(processHandle, 
            			WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, phToken)) {
            		throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
            	}
        	}
        	return getTokenGroups(phToken.getValue());
    	} finally {
    		if (phToken.getValue() != WinBase.INVALID_HANDLE_VALUE) {
    			if (! Kernel32.INSTANCE.CloseHandle(phToken.getValue())) {
    				throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    			}
    		}
    	}		
	}

	/**
	 * Checks whether a registry key exists.
	 * @param root 
	 *  HKEY_LOCAL_MACHINE, etc.
	 * @param key 
	 *  Path to the registry key.
	 * @return 
	 *  True if the key exists.
	 */
	public static boolean registryKeyExists(HKEY root, String key) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, key, 0, WinNT.KEY_READ, phkKey);
		switch(rc) {
		case W32Errors.ERROR_SUCCESS:
			Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			return true;
		case W32Errors.ERROR_FILE_NOT_FOUND:
			return false;
		default:
			throw new Win32Exception(rc);
		}
	}
	
	/**
	 * Checks whether a registry value exists.
	 * @param root
	 *  HKEY_LOCAL_MACHINE, etc.
	 * @param key
	 *  Registry key path.
	 * @param value
	 *  Value name.
	 * @return
	 *  True if the value exists.
	 */
	public static boolean registryValueExists(HKEY root, String key, String value) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, key, 0, WinNT.KEY_READ, phkKey);
		try {
			switch(rc) {
			case W32Errors.ERROR_SUCCESS:
				break;
			case W32Errors.ERROR_FILE_NOT_FOUND:
				return false;
			default:
				throw new Win32Exception(rc);
			}
			IntByReference lpcbData = new IntByReference();
			IntByReference lpType = new IntByReference();
			rc = Advapi32.INSTANCE.RegQueryValueEx(
					phkKey.getValue(), value, 0, lpType, (char[]) null, lpcbData);
			switch(rc) {
			case W32Errors.ERROR_SUCCESS:
			case W32Errors.ERROR_INSUFFICIENT_BUFFER:
				return true;
			case W32Errors.ERROR_FILE_NOT_FOUND:
				return false;
			default:
				throw new Win32Exception(rc);
			}
		} finally {
			if (phkKey.getValue() != WinBase.INVALID_HANDLE_VALUE) {
				rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
				if (rc != W32Errors.ERROR_SUCCESS) {
					throw new Win32Exception(rc);
				}
			}
		}
	}	

	/**
	 * Get a registry REG_SZ value.
	 * @param root
	 *  Root key.
	 * @param key
	 *  Registry path.
	 * @param value
	 *  Name of the value to retrieve.
	 * @return
	 *  String value.
	 */
	public static String registryGetStringValue(HKEY root, String key, String value) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, key, 0, WinNT.KEY_READ, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			IntByReference lpcbData = new IntByReference();
			IntByReference lpType = new IntByReference();
			rc = Advapi32.INSTANCE.RegQueryValueEx(
					phkKey.getValue(), value, 0, lpType, (char[]) null, lpcbData);
			if (rc != W32Errors.ERROR_SUCCESS && rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
				throw new Win32Exception(rc);
			}
			if (lpType.getValue() != WinNT.REG_SZ) {
				throw new RuntimeException("Unexpected registry type " + lpType.getValue() + ", expected REG_SZ");
			}
			char[] data = new char[lpcbData.getValue()];
			rc = Advapi32.INSTANCE.RegQueryValueEx(
					phkKey.getValue(), value, 0, lpType, data, lpcbData);
			if (rc != W32Errors.ERROR_SUCCESS && rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
				throw new Win32Exception(rc);
			}
			return Native.toString(data);
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}

	/**
	 * Get a registry REG_EXPAND_SZ value.
	 * @param root
	 *  Root key.
	 * @param key
	 *  Registry path.
	 * @param value
	 *  Name of the value to retrieve.
	 * @return
	 *  String value.
	 */
	public static String registryGetExpandableStringValue(HKEY root, String key, String value) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, key, 0, WinNT.KEY_READ, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			IntByReference lpcbData = new IntByReference();
			IntByReference lpType = new IntByReference();
			rc = Advapi32.INSTANCE.RegQueryValueEx(
					phkKey.getValue(), value, 0, lpType, (char[]) null, lpcbData);
			if (rc != W32Errors.ERROR_SUCCESS && rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
				throw new Win32Exception(rc);
			}
			if (lpType.getValue() != WinNT.REG_EXPAND_SZ) {
				throw new RuntimeException("Unexpected registry type " + lpType.getValue() + ", expected REG_SZ");
			}
			char[] data = new char[lpcbData.getValue()];
			rc = Advapi32.INSTANCE.RegQueryValueEx(
					phkKey.getValue(), value, 0, lpType, data, lpcbData);
			if (rc != W32Errors.ERROR_SUCCESS && rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
				throw new Win32Exception(rc);
			}
			return Native.toString(data);
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}
	
	/**
	 * Get a registry REG_MULTI_SZ value.
	 * @param root
	 *  Root key.
	 * @param key
	 *  Registry path.
	 * @param value
	 *  Name of the value to retrieve.
	 * @return
	 *  String value.
	 */
	public static String[] registryGetStringArray(HKEY root, String key, String value) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, key, 0, WinNT.KEY_READ, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			IntByReference lpcbData = new IntByReference();
			IntByReference lpType = new IntByReference();
			rc = Advapi32.INSTANCE.RegQueryValueEx(
					phkKey.getValue(), value, 0, lpType, (char[]) null, lpcbData);
			if (rc != W32Errors.ERROR_SUCCESS && rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
				throw new Win32Exception(rc);
			}
			if (lpType.getValue() != WinNT.REG_MULTI_SZ) {
				throw new RuntimeException("Unexpected registry type " + lpType.getValue() + ", expected REG_SZ");
			}
			Memory data = new Memory(lpcbData.getValue());
			rc = Advapi32.INSTANCE.RegQueryValueEx(
					phkKey.getValue(), value, 0, lpType, data, lpcbData);
			if (rc != W32Errors.ERROR_SUCCESS && rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
				throw new Win32Exception(rc);
			}
			ArrayList result = new ArrayList();
			int offset = 0;
			while(offset < data.size()) {
				String s = data.getString(offset, true);
				offset += s.length() * Native.WCHAR_SIZE;
				offset += Native.WCHAR_SIZE;
				result.add(s);
			}
			return result.toArray(new String[0]);
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}
	
	/**
	 * Get a registry REG_BINARY value.
	 * @param root
	 *  Root key.
	 * @param key
	 *  Registry path.
	 * @param value
	 *  Name of the value to retrieve.
	 * @return
	 *  String value.
	 */
	public static byte[] registryGetBinaryValue(HKEY root, String key, String value) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, key, 0, WinNT.KEY_READ, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			IntByReference lpcbData = new IntByReference();
			IntByReference lpType = new IntByReference();
			rc = Advapi32.INSTANCE.RegQueryValueEx(
					phkKey.getValue(), value, 0, lpType, (char[]) null, lpcbData);
			if (rc != W32Errors.ERROR_SUCCESS && rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
				throw new Win32Exception(rc);
			}
			if (lpType.getValue() != WinNT.REG_BINARY) {
				throw new RuntimeException("Unexpected registry type " + lpType.getValue() + ", expected REG_BINARY");
			}
			byte[] data = new byte[lpcbData.getValue()];
			rc = Advapi32.INSTANCE.RegQueryValueEx(
					phkKey.getValue(), value, 0, lpType, data, lpcbData);
			if (rc != W32Errors.ERROR_SUCCESS && rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
				throw new Win32Exception(rc);
			}
			return data;
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}
	
	/**
	 * Get a registry DWORD value.
	 * @param root
	 *  Root key.
	 * @param key
	 *  Registry key path.
	 * @param value
	 *  Name of the value to retrieve.
	 * @return
	 *  Integer value.
	 */
	public static int registryGetIntValue(HKEY root, String key, String value) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, key, 0, WinNT.KEY_READ, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			IntByReference lpcbData = new IntByReference();
			IntByReference lpType = new IntByReference();
			rc = Advapi32.INSTANCE.RegQueryValueEx(
					phkKey.getValue(), value, 0, lpType, (char[]) null, lpcbData);
			if (rc != W32Errors.ERROR_SUCCESS && rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
				throw new Win32Exception(rc);
			}
			if (lpType.getValue() != WinNT.REG_DWORD) {
				throw new RuntimeException("Unexpected registry type " + lpType.getValue() + ", expected REG_SZ");
			}
			IntByReference data = new IntByReference();
			rc = Advapi32.INSTANCE.RegQueryValueEx(
					phkKey.getValue(), value, 0, lpType, data, lpcbData);
			if (rc != W32Errors.ERROR_SUCCESS && rc != W32Errors.ERROR_INSUFFICIENT_BUFFER) {
				throw new Win32Exception(rc);
			}
			return data.getValue();
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}
	
	/**
	 * Create a registry key.
	 * @param hKey 
	 *  Parent key.
	 * @param keyName 
	 *  Key name.
	 * @return
	 *  True if the key was created, false otherwise.
	 */
	public static boolean registryCreateKey(HKEY hKey, String keyName) {
		HKEYByReference phkResult = new HKEYByReference();
		IntByReference lpdwDisposition = new IntByReference();
    	int rc = Advapi32.INSTANCE.RegCreateKeyEx(hKey, keyName, 0, null, WinNT.REG_OPTION_NON_VOLATILE, 
    			WinNT.KEY_READ, null, phkResult, lpdwDisposition);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		rc = Advapi32.INSTANCE.RegCloseKey(phkResult.getValue());
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		return WinNT.REG_CREATED_NEW_KEY == lpdwDisposition.getValue();
	}

	/**
	 * Create a registry key.
	 * @param root
	 *  Root key.
	 * @param parentPath
	 *  Path to an existing registry key.
	 * @param keyName
	 *  Key name.
	 * @return
	 *  True if the key was created, false otherwise.
	 */
	public static boolean registryCreateKey(HKEY root, String parentPath, String keyName) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, parentPath, 0, WinNT.KEY_CREATE_SUB_KEY, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			return registryCreateKey(phkKey.getValue(), keyName);
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}		
	}
	
	/**
	 * Set an integer value in registry.
	 * @param hKey
	 *  Parent key.
	 * @param name
	 *  Value name.
	 * @param value
	 *  Value to write to registry.
	 */
	public static void registrySetIntValue(HKEY hKey, String name, int value) {
        byte[] data = new byte[4];
        data[0] = (byte)(value & 0xff);
        data[1] = (byte)((value >> 8) & 0xff);
        data[2] = (byte)((value >> 16) & 0xff);
        data[3] = (byte)((value >> 24) & 0xff);
		int rc = Advapi32.INSTANCE.RegSetValueEx(hKey, name, 0, WinNT.REG_DWORD, data, 4);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
	}
	
	/**
	 * Set an integer value in registry.
	 * @param root
	 *  Root key.
	 * @param keyPath
	 *  Path to an existing registry key.
	 * @param name
	 *  Value name.
	 * @param value
	 *  Value to write to registry.
	 */
	public static void registrySetIntValue(HKEY root, String keyPath, String name, int value) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, keyPath, 0, WinNT.KEY_READ | WinNT.KEY_WRITE, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			registrySetIntValue(phkKey.getValue(), name, value);
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}		
	}

	/**
	 * Set a string value in registry.
	 * @param hKey
	 *  Parent key.
	 * @param name
	 *  Value name.
	 * @param value
	 *  Value to write to registry.
	 */
	public static void registrySetStringValue(HKEY hKey, String name, String value) {
    	char[] data = Native.toCharArray(value);
		int rc = Advapi32.INSTANCE.RegSetValueEx(hKey, name, 0, WinNT.REG_SZ, 
				data, data.length * Native.WCHAR_SIZE);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}	
	}

	/**
	 * Set a string value in registry.
	 * @param root
	 *  Root key.
	 * @param keyPath
	 *  Path to an existing registry key.
	 * @param name
	 *  Value name.
	 * @param value
	 *  Value to write to registry.
	 */
	public static void registrySetStringValue(HKEY root, String keyPath, String name, String value) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, keyPath, 0, WinNT.KEY_READ | WinNT.KEY_WRITE, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			registrySetStringValue(phkKey.getValue(), name, value);
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}
	
	/**
	 * Set an expandable string value in registry.
	 * @param hKey
	 *  Parent key.
	 * @param name
	 *  Value name.
	 * @param value
	 *  Value to write to registry.
	 */
	public static void registrySetExpandableStringValue(HKEY hKey, String name, String value) {
    	char[] data = Native.toCharArray(value);
		int rc = Advapi32.INSTANCE.RegSetValueEx(hKey, name, 0, WinNT.REG_EXPAND_SZ, 
				data, data.length * Native.WCHAR_SIZE);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}	
	}

	/**
	 * Set a string value in registry.
	 * @param root
	 *  Root key.
	 * @param keyPath
	 *  Path to an existing registry key.
	 * @param name
	 *  Value name.
	 * @param value
	 *  Value to write to registry.
	 */
	public static void registrySetExpandableStringValue(HKEY root, String keyPath, String name, String value) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, keyPath, 0, WinNT.KEY_READ | WinNT.KEY_WRITE, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			registrySetExpandableStringValue(phkKey.getValue(), name, value);
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}

	/**
	 * Set a string array value in registry.
	 * @param hKey
	 *  Parent key.
	 * @param name
	 *  Name.
	 * @param arr
	 *  Array of strings to write to registry.
	 */
	public static void registrySetStringArray(HKEY hKey, String name, String[] arr) {
		int size = 0;
		for(String s : arr) {
			size += s.length() * Native.WCHAR_SIZE;
			size += Native.WCHAR_SIZE;
		}
		
		int offset = 0;
		Memory data = new Memory(size);
		for(String s : arr) {
			data.setString(offset, s, true);
			offset += s.length() * Native.WCHAR_SIZE;
			offset += Native.WCHAR_SIZE;
		}
		
		int rc = Advapi32.INSTANCE.RegSetValueEx(hKey, name, 0, WinNT.REG_MULTI_SZ, 
				data.getByteArray(0, size), size);
		
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}	
	}

	/**
	 * Set a string array value in registry.
	 * @param root
	 *  Root key.
	 * @param keyPath
	 *  Path to an existing registry key.
	 * @param name
	 *  Value name.
	 * @param arr
	 *  Array of strings to write to registry.
	 */
	public static void registrySetStringArray(HKEY root, String keyPath, String name, String[] arr) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, keyPath, 0, WinNT.KEY_READ | WinNT.KEY_WRITE, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			registrySetStringArray(phkKey.getValue(), name, arr);
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}
	
	/**
	 * Set a binary value in registry.
	 * @param hKey
	 *  Parent key.
	 * @param name
	 *  Value name.
	 * @param data
	 *  Data to write to registry.
	 */
	public static void registrySetBinaryValue(HKEY hKey, String name, byte[] data) {
		int rc = Advapi32.INSTANCE.RegSetValueEx(hKey, name, 0, WinNT.REG_BINARY, data, data.length);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}	
	}

	/**
	 * Set a binary value in registry.
	 * @param root
	 *  Root key.
	 * @param keyPath
	 *  Path to an existing registry key.
	 * @param name
	 *  Value name.
	 * @param data
	 *  Data to write to registry.
	 */
	public static void registrySetBinaryValue(HKEY root, String keyPath, String name, byte[] data) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, keyPath, 0, WinNT.KEY_READ | WinNT.KEY_WRITE, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			registrySetBinaryValue(phkKey.getValue(), name, data);
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}
	
	/**
	 * Delete a registry key.
	 * @param hKey
	 *  Parent key.
	 * @param keyName
	 *  Name of the key to delete.
	 */
	public static void registryDeleteKey(HKEY hKey, String keyName) {
		int rc = Advapi32.INSTANCE.RegDeleteKey(hKey, keyName);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}		
	}
	
	/**
	 * Delete a registry key.
	 * @param root
	 *  Root key.
	 * @param keyPath
	 *  Path to an existing registry key.
	 * @param keyName
	 *  Name of the key to delete.
	 */
	public static void registryDeleteKey(HKEY root, String keyPath, String keyName) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, keyPath, 0, WinNT.KEY_READ | WinNT.KEY_WRITE, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			registryDeleteKey(phkKey.getValue(), keyName);
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}
	
	/**
	 * Delete a registry value.
	 * @param hKey
	 *  Parent key.
	 * @param valueName
	 *  Name of the value to delete.
	 */
	public static void registryDeleteValue(HKEY hKey, String valueName) {
		int rc = Advapi32.INSTANCE.RegDeleteValue(hKey, valueName);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}		
	}
	
	/**
	 * Delete a registry value.
	 * @param root
	 *  Root key.
	 * @param keyPath
	 *  Path to an existing registry key.
	 * @param valueName
	 *  Name of the value to delete.
	 */
	public static void registryDeleteValue(HKEY root, String keyPath, String valueName) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, keyPath, 0, WinNT.KEY_READ | WinNT.KEY_WRITE, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			registryDeleteValue(phkKey.getValue(), valueName);
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}
	
	/**
	 * Get names of the registry key's sub-keys.
	 * @param hKey
	 *  Registry key.
	 * @return
	 *  Array of registry key names.
	 */
	public static String[] registryGetKeys(HKEY hKey) {
    	IntByReference lpcSubKeys = new IntByReference();
    	IntByReference lpcMaxSubKeyLen = new IntByReference();
    	int rc = Advapi32.INSTANCE.RegQueryInfoKey(hKey, null, null, null, 
    			lpcSubKeys, lpcMaxSubKeyLen, null, null, null, null, null, null);
    	if (rc != W32Errors.ERROR_SUCCESS) {
    		throw new Win32Exception(rc);
    	}
    	ArrayList keys = new ArrayList(lpcSubKeys.getValue());
    	char[] name = new char[lpcMaxSubKeyLen.getValue() + 1];
    	for (int i = 0; i < lpcSubKeys.getValue(); i++) {
    		IntByReference lpcchValueName = new IntByReference(lpcMaxSubKeyLen.getValue() + 1);
        	rc = Advapi32.INSTANCE.RegEnumKeyEx(hKey, i, name, lpcchValueName, 
        			null, null, null, null);
        	if (rc != W32Errors.ERROR_SUCCESS) {
        		throw new Win32Exception(rc);
        	}
        	keys.add(Native.toString(name));
    	}
    	return keys.toArray(new String[0]);
	}

	/**
	 * Get names of the registry key's sub-keys.
	 * @param root
	 *  Root key.
	 * @param keyPath
	 *  Path to a registry key.
	 * @return
	 *  Array of registry key names.
	 */
	public static String[] registryGetKeys(HKEY root, String keyPath) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, keyPath, 0, WinNT.KEY_READ, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {
			throw new Win32Exception(rc);
		}
		try {
			return registryGetKeys(phkKey.getValue());
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}
	}

	/**
	 * Get a table of registry values.
	 * @param hKey
	 *  Registry key.
	 * @return
	 *  Table of values.
	 */
	public static TreeMap registryGetValues(HKEY hKey) {
    	IntByReference lpcValues = new IntByReference();
    	IntByReference lpcMaxValueNameLen = new IntByReference();
    	IntByReference lpcMaxValueLen = new IntByReference();
    	int rc = Advapi32.INSTANCE.RegQueryInfoKey(hKey, null, null, null, null, 
    			null, null, lpcValues, lpcMaxValueNameLen, lpcMaxValueLen, null, null);
    	if (rc != W32Errors.ERROR_SUCCESS) {
    		throw new Win32Exception(rc);
    	}
		TreeMap keyValues = new TreeMap();		
    	char[] name = new char[lpcMaxValueNameLen.getValue() + 1];
    	byte[] data = new byte[lpcMaxValueLen.getValue()];
    	for (int i = 0; i < lpcValues.getValue(); i++) {
    		IntByReference lpcchValueName = new IntByReference(lpcMaxValueNameLen.getValue() + 1);
    		IntByReference lpcbData = new IntByReference(lpcMaxValueLen.getValue());
    		IntByReference lpType = new IntByReference();
        	rc = Advapi32.INSTANCE.RegEnumValue(hKey, i, name, lpcchValueName, null, 
        			lpType, data, lpcbData);
        	if (rc != W32Errors.ERROR_SUCCESS) {
        		throw new Win32Exception(rc);
        	}
        	
        	String nameString = Native.toString(name);
        	
			Memory byteData = new Memory(lpcbData.getValue());
			byteData.write(0, data, 0, lpcbData.getValue());
        	
        	switch(lpType.getValue()) {
        	case WinNT.REG_DWORD:
        	{
            	keyValues.put(nameString, byteData.getInt(0));
        		break;
        	}
        	case WinNT.REG_SZ:
        	case WinNT.REG_EXPAND_SZ:
        	{
				keyValues.put(nameString, byteData.getString(0, true));
        		break;
        	}
        	case WinNT.REG_BINARY:
        	{
        		keyValues.put(nameString, byteData.getByteArray(0, lpcbData.getValue()));
        		break;
        	}
        	case WinNT.REG_MULTI_SZ:
        	{
    			Memory stringData = new Memory(lpcbData.getValue());
    			stringData.write(0, data, 0, lpcbData.getValue());
    			ArrayList result = new ArrayList();
    			int offset = 0;
    			while(offset < stringData.size()) {
    				String s = stringData.getString(offset, true);
    				offset += s.length() * Native.WCHAR_SIZE;
    				offset += Native.WCHAR_SIZE;
    				result.add(s);
    			}
    			keyValues.put(nameString, result.toArray(new String[0]));
    			break;
        	}
        	default:
        		throw new RuntimeException("Unsupported type: " + lpType.getValue());
        	}
    	}
    	return keyValues;
	}
	
	/**
	 * Get a table of registry values.
	 * @param root
	 *  Registry root.
	 * @param keyPath
	 *  Regitry key path.
	 * @return
	 *  Table of values.
	 */
	public static TreeMap registryGetValues(HKEY root, String keyPath) {
		HKEYByReference phkKey = new HKEYByReference();
		int rc = Advapi32.INSTANCE.RegOpenKeyEx(root, keyPath, 0, WinNT.KEY_READ, phkKey);
		if (rc != W32Errors.ERROR_SUCCESS) {			throw new Win32Exception(rc);
		}
		try {
			return registryGetValues(phkKey.getValue());
		} finally {
			rc = Advapi32.INSTANCE.RegCloseKey(phkKey.getValue());
			if (rc != W32Errors.ERROR_SUCCESS) {
				throw new Win32Exception(rc);
			}
		}		
	}
	
	/**
	 * Converts a map of environment variables to an environment block suitable
	 * for {@link Advapi32#CreateProcessAsUser}. This environment block consists
	 * of null-terminated blocks of null-terminated strings. Each string is in the
	 * following form: name=value\0
	 * @param environment Environment variables
	 * @return A environment block
	 */
	public static String getEnvironmentBlock(Map environment) {
		StringBuffer out = new StringBuffer();
		for (Entry entry: environment.entrySet()) {
			if (entry.getValue() != null) {
				out.append(entry.getKey() + "=" + entry.getValue() + "\0");
			}
		}
		return out.toString() + "\0";
	}
	
	/**
	 * Event log types.
	 */
	public static enum EventLogType {
		Error,
		Warning,
		Informational,
		AuditSuccess,
		AuditFailure
	}
	
	/**
	 * An event log record.
	 */
	public static class EventLogRecord {
		private EVENTLOGRECORD _record = null;
		private String _source;
		private byte[] _data;
		private String[] _strings;
		
		/**
		 * Raw record data.
		 * @return
		 *  EVENTLOGRECORD.
		 */
		public EVENTLOGRECORD getRecord() {
			return _record;
		}
		
		/**
		 * Event Id.
		 * @return
		 *  Integer.
		 */
		public int getEventId() {
			return _record.EventID.intValue();
		}
		
		/**
		 * Event source.
		 * @return
		 *  String.
		 */
		public String getSource() {
			return _source;
		}
		
		/**
		 * Status code for the facility, part of the Event ID.
		 * @return
		 *  Status code.
		 */
		public int getStatusCode() {
			return _record.EventID.intValue() & 0xFFFF;
		}
		
		/**
		 * Record number of the record. This value can be used with the EVENTLOG_SEEK_READ flag in 
		 * the ReadEventLog function to begin reading at a specified record.
		 * @return
		 *  Integer.
		 */
		public int getRecordNumber() {
			return _record.RecordNumber.intValue();
		}
		
		/**
		 * Record length, with data.
		 * @return
		 *  Number of bytes in the record including data.
		 */
		public int getLength() {
			return _record.Length.intValue();
		}
		
		/**
		 * Strings associated with this event.
		 * @return
		 *  Array of strings or null.
		 */
		public String[] getStrings() {
			return _strings;
		}
		
		/**
		 * Event log type.
		 * @return
		 *  Event log type.
		 */
		public EventLogType getType() {
			switch(_record.EventType.intValue()) {
			case WinNT.EVENTLOG_SUCCESS:
			case WinNT.EVENTLOG_INFORMATION_TYPE:
				return EventLogType.Informational;
			case WinNT.EVENTLOG_AUDIT_FAILURE:
				return EventLogType.AuditFailure;
			case WinNT.EVENTLOG_AUDIT_SUCCESS:
				return EventLogType.AuditSuccess;
			case WinNT.EVENTLOG_ERROR_TYPE:
				return EventLogType.Error;
			case WinNT.EVENTLOG_WARNING_TYPE:
				return EventLogType.Warning;
			default:
				throw new RuntimeException("Invalid type: " + _record.EventType.intValue());
			}
		}
		
		/**
		 * Raw data associated with the record.
		 * @return
		 *  Array of bytes or null.
		 */
		public byte[] getData() {
			return _data;
		}
		
		public EventLogRecord(Pointer pevlr) {
			_record = new EVENTLOGRECORD(pevlr);
			_source = pevlr.getString(_record.size(), true);
			// data
			if (_record.DataLength.intValue() > 0) {
				_data = pevlr.getByteArray(_record.DataOffset.intValue(),
						_record.DataLength.intValue());
			}
			// strings
			if (_record.NumStrings.intValue() > 0) {
				ArrayList strings = new ArrayList();
				int count = _record.NumStrings.intValue();
				long offset = _record.StringOffset.intValue();
				while(count > 0) {
					String s = pevlr.getString(offset, true);
					strings.add(s);
					offset += s.length() * Native.WCHAR_SIZE;
					offset += Native.WCHAR_SIZE;
					count--;
				}
				_strings = strings.toArray(new String[0]);
			}
		}
	}
	
	/**
	 * An iterator for Event Log entries.
	 */
	public static class EventLogIterator
		implements Iterable, Iterator {
		
    	private HANDLE _h = null;
    	private Memory _buffer = new Memory(1024 * 64); // memory buffer to store events
    	private boolean _done = false; // no more events
    	private int _dwRead = 0; // number of bytes remaining in the current buffer
    	private Pointer _pevlr = null; // pointer to the current record
    	private int _flags = WinNT.EVENTLOG_FORWARDS_READ;

		public EventLogIterator(String sourceName) {
			this(null, sourceName, WinNT.EVENTLOG_FORWARDS_READ);
		}
		
		public EventLogIterator(String serverName, String sourceName, int flags) {
			_flags = flags;
	    	_h = Advapi32.INSTANCE.OpenEventLog(serverName, sourceName);
	    	if (_h == null) {
	    		throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
	    	}
		}
		
		private boolean read() {
			// finished or bytes remain, don't read any new data
			if (_done || _dwRead > 0) {
				return false;
			}
			
	    	IntByReference pnBytesRead = new IntByReference();
	    	IntByReference pnMinNumberOfBytesNeeded = new IntByReference();
			
			if (! Advapi32.INSTANCE.ReadEventLog(_h, 
	    			WinNT.EVENTLOG_SEQUENTIAL_READ | _flags, 
	    			0, _buffer, (int) _buffer.size(), pnBytesRead, pnMinNumberOfBytesNeeded)) {

				int rc = Kernel32.INSTANCE.GetLastError();
				
				// not enough bytes in the buffer, resize
				if (rc == W32Errors.ERROR_INSUFFICIENT_BUFFER) {
					_buffer = new Memory(pnMinNumberOfBytesNeeded.getValue());
					
					if (! Advapi32.INSTANCE.ReadEventLog(_h, 
			    			WinNT.EVENTLOG_SEQUENTIAL_READ | _flags, 
			    			0, _buffer, (int) _buffer.size(), pnBytesRead, pnMinNumberOfBytesNeeded)) {
						throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
					}					
				} else {
					// read failed, no more entries or error
					close();
					if (rc != W32Errors.ERROR_HANDLE_EOF) {
						throw new Win32Exception(rc);
					}
					return false;
				}
			}
			
    		_dwRead = pnBytesRead.getValue();
    		_pevlr = _buffer;
    		return true;
		}
		
		/**
		 * Call close() in the case when the caller needs to abandon the iterator before 
		 * the iteration completes.
		 */
		public void close() {
			_done = true;
			if (_h != null) {
				if (! Advapi32.INSTANCE.CloseEventLog(_h)) {
					throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
				}
				_h = null;
			}
		}
		
		@Override
		public Iterator iterator() {
			return this;
		}

		@Override
		public boolean hasNext() {
			read();
			return ! _done;
		}

		@Override
		public EventLogRecord next() {
			read();
			EventLogRecord record = new EventLogRecord(_pevlr);
    		_dwRead -= record.getLength();
    		_pevlr = _pevlr.share(record.getLength());
			return record;
		}

		@Override
		public void remove() {
			
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy