/* 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 static com.sun.jna.platform.win32.WinBase.CREATE_FOR_IMPORT;
import static com.sun.jna.platform.win32.WinBase.FILE_DIR_DISALOWED;
import static com.sun.jna.platform.win32.WinBase.FILE_ENCRYPTABLE;
import static com.sun.jna.platform.win32.WinBase.FILE_IS_ENCRYPTED;
import static com.sun.jna.platform.win32.WinBase.FILE_READ_ONLY;
import static com.sun.jna.platform.win32.WinNT.DACL_SECURITY_INFORMATION;
import static com.sun.jna.platform.win32.WinNT.FILE_ALL_ACCESS;
import static com.sun.jna.platform.win32.WinNT.FILE_GENERIC_EXECUTE;
import static com.sun.jna.platform.win32.WinNT.FILE_GENERIC_READ;
import static com.sun.jna.platform.win32.WinNT.FILE_GENERIC_WRITE;
import static com.sun.jna.platform.win32.WinNT.GENERIC_ALL;
import static com.sun.jna.platform.win32.WinNT.GENERIC_EXECUTE;
import static com.sun.jna.platform.win32.WinNT.GENERIC_READ;
import static com.sun.jna.platform.win32.WinNT.GENERIC_WRITE;
import static com.sun.jna.platform.win32.WinNT.GROUP_SECURITY_INFORMATION;
import static com.sun.jna.platform.win32.WinNT.OWNER_SECURITY_INFORMATION;
import static com.sun.jna.platform.win32.WinNT.SACL_SECURITY_INFORMATION;
import static com.sun.jna.platform.win32.WinNT.SE_RESTORE_NAME;
import static com.sun.jna.platform.win32.WinNT.SE_SECURITY_NAME;
import static com.sun.jna.platform.win32.WinNT.TOKEN_ADJUST_PRIVILEGES;
import static com.sun.jna.platform.win32.WinNT.TOKEN_DUPLICATE;
import static com.sun.jna.platform.win32.WinNT.TOKEN_IMPERSONATE;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.LMAccess.USER_INFO_1;
import com.sun.jna.platform.win32.WinBase.FE_EXPORT_FUNC;
import com.sun.jna.platform.win32.WinBase.FE_IMPORT_FUNC;
import com.sun.jna.platform.win32.WinBase.FILETIME;
import com.sun.jna.platform.win32.WinBase.PROCESS_INFORMATION;
import com.sun.jna.platform.win32.WinBase.STARTUPINFO;
import com.sun.jna.platform.win32.WinDef.BOOL;
import com.sun.jna.platform.win32.WinDef.BOOLByReference;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.DWORDByReference;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinDef.ULONGByReference;
import com.sun.jna.platform.win32.WinNT.ACCESS_ALLOWED_ACE;
import com.sun.jna.platform.win32.WinNT.ACL;
import com.sun.jna.platform.win32.WinNT.EVENTLOGRECORD;
import com.sun.jna.platform.win32.WinNT.GENERIC_MAPPING;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinNT.HANDLEByReference;
import com.sun.jna.platform.win32.WinNT.PACLByReference;
import com.sun.jna.platform.win32.WinNT.PRIVILEGE_SET;
import com.sun.jna.platform.win32.WinNT.PSID;
import com.sun.jna.platform.win32.WinNT.PSIDByReference;
import com.sun.jna.platform.win32.WinNT.SECURITY_DESCRIPTOR;
import com.sun.jna.platform.win32.WinNT.SECURITY_DESCRIPTOR_RELATIVE;
import com.sun.jna.platform.win32.WinNT.SECURITY_IMPERSONATION_LEVEL;
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.WinNT.TOKEN_PRIVILEGES;
import com.sun.jna.platform.win32.WinNT.TOKEN_TYPE;
import com.sun.jna.platform.win32.WinNT.WELL_KNOWN_SID_TYPE;
import com.sun.jna.platform.win32.WinReg.HKEYByReference;
import com.sun.jna.platform.win32.Winsvc.SC_HANDLE;
import com.sun.jna.platform.win32.Winsvc.SC_STATUS_TYPE;
import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_PROCESS;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import com.sun.jna.ptr.ShortByReference;

import junit.framework.TestCase;

/**
 * @author dblock[at]dblock[dot]org
 */
public class Advapi32Test extends TestCase {

    private static final String EVERYONE = "S-1-1-0";

    public static void main(String[] args) {
        junit.textui.TestRunner.run(Advapi32Test.class);
    }

    // see https://github.com/twall/jna/issues/482
    public void testNoDuplicateMethodsNames() {
        Collection<String> dupSet = AbstractWin32TestSupport.detectDuplicateMethods(Advapi32.class);
        if (dupSet.size() > 0) {
            for (String name : new String[] {
                    // has several overloads by design since the output value can be several types of data
                    "RegQueryValueEx",
                    // has several overloads by design since the input value can be several types of data
                    "RegSetValueEx"
                }) {
                dupSet.remove(name);
            }
        }

        assertTrue("Duplicate methods found: " + dupSet, dupSet.isEmpty());
    }

    public void testGetUserName() {
		IntByReference len = new IntByReference();
		assertFalse(Advapi32.INSTANCE.GetUserNameW(null, len));
		assertEquals(W32Errors.ERROR_INSUFFICIENT_BUFFER, Kernel32.INSTANCE.GetLastError());
		char[] buffer = new char[len.getValue()];
		assertTrue(Advapi32.INSTANCE.GetUserNameW(buffer, len));
		String username = Native.toString(buffer);
		assertTrue(username.length() > 0);
    }

    public void testLookupAccountName() {
		IntByReference pSid = new IntByReference(0);
		IntByReference pDomain = new IntByReference(0);
		PointerByReference peUse = new PointerByReference();
		String accountName = "Administrator";
		assertFalse(Advapi32.INSTANCE.LookupAccountName(
				null, accountName, null, pSid, null, pDomain, peUse));
		assertEquals(W32Errors.ERROR_INSUFFICIENT_BUFFER, Kernel32.INSTANCE.GetLastError());
		assertTrue(pSid.getValue() > 0);
		Memory sidMemory = new Memory(pSid.getValue());
		PSID pSidMemory = new PSID(sidMemory);
		char[] referencedDomainName = new char[pDomain.getValue() + 1];
		assertTrue(Advapi32.INSTANCE.LookupAccountName(
				null, accountName, pSidMemory, pSid, referencedDomainName, pDomain, peUse));
		assertEquals(SID_NAME_USE.SidTypeUser, peUse.getPointer().getInt(0));
		assertTrue(Native.toString(referencedDomainName).length() > 0);
    }

    public void testIsValidSid() {
    	String sidString = EVERYONE;
    	PSIDByReference sid = new PSIDByReference();
    	assertTrue("SID conversion failed", Advapi32.INSTANCE.ConvertStringSidToSid(sidString, sid));

    	PSID value = sid.getValue();
    	try {
        	assertTrue("Converted SID not valid: " + value, Advapi32.INSTANCE.IsValidSid(value));
        	int sidLength = Advapi32.INSTANCE.GetLengthSid(value);
        	assertTrue("Non positive sid length", sidLength > 0);
        	assertTrue("Invalid sid", Advapi32.INSTANCE.IsValidSid(value));
    	} finally {
    	    Kernel32Util.freeLocalMemory(value.getPointer());
    	}
    }

    public void testEqualSid() {
        String sidString = EVERYONE;
        PSIDByReference sid1 = new PSIDByReference();
        PSIDByReference sid2 = new PSIDByReference();
        assertTrue("SID1 conversion failed", Advapi32.INSTANCE.ConvertStringSidToSid(sidString, sid1));
        assertTrue("SID2 conversion failed", Advapi32.INSTANCE.ConvertStringSidToSid(sidString, sid2));

        try {
            assertTrue("Converted SID1 not valid", Advapi32.INSTANCE.IsValidSid(sid1.getValue()));
            assertTrue("Converted SID2 not valid", Advapi32.INSTANCE.IsValidSid(sid2.getValue()));
            assertTrue("Invalid sid", Advapi32.INSTANCE.EqualSid(sid1.getValue(), sid2.getValue()));
        } finally {
            Kernel32Util.freeLocalMemory(sid1.getValue().getPointer());
            Kernel32Util.freeLocalMemory(sid2.getValue().getPointer());
        }
    }

    public void testGetSidLength() {
    	String sidString = EVERYONE;
    	PSIDByReference sid = new PSIDByReference();
    	assertTrue("SID conversion failed", Advapi32.INSTANCE.ConvertStringSidToSid(sidString, sid));

    	PSID value = sid.getValue();
    	try {
    	    assertEquals("Wrong SID length", 12, Advapi32.INSTANCE.GetLengthSid(value));
    	} finally {
    	    Kernel32Util.freeLocalMemory(value.getPointer());
    	}
    }

    public void testLookupAccountSid() {
    	// get SID bytes
    	String sidString = EVERYONE;
    	PSIDByReference sid = new PSIDByReference();
    	assertTrue("Failed to create sid", Advapi32.INSTANCE.ConvertStringSidToSid(sidString, sid));

    	PSID value = sid.getValue();
    	try {
        	int sidLength = Advapi32.INSTANCE.GetLengthSid(value);
        	assertTrue("Non-positive sid length", sidLength > 0);
        	// lookup account
        	IntByReference cchName = new IntByReference();
        	IntByReference cchReferencedDomainName = new IntByReference();
        	PointerByReference peUse = new PointerByReference();
        	assertFalse(Advapi32.INSTANCE.LookupAccountSid(null, value,
        			null, cchName, null, cchReferencedDomainName, peUse));
    		assertEquals(W32Errors.ERROR_INSUFFICIENT_BUFFER, Kernel32.INSTANCE.GetLastError());
        	assertTrue(cchName.getValue() > 0);
        	assertTrue(cchReferencedDomainName.getValue() > 0);
    		char[] referencedDomainName = new char[cchReferencedDomainName.getValue()];
    		char[] name = new char[cchName.getValue()];
        	assertTrue(Advapi32.INSTANCE.LookupAccountSid(null, value,
        			name, cchName, referencedDomainName, cchReferencedDomainName, peUse));
    		assertEquals(5, peUse.getPointer().getInt(0)); // SidTypeWellKnownGroup
    		String nameString = Native.toString(name);
    		String referencedDomainNameString = Native.toString(referencedDomainName);
    		assertTrue(nameString.length() > 0);
                if(AbstractWin32TestSupport.isEnglishLocale) {
                    assertEquals("Everyone", nameString);
                }
    		assertTrue(referencedDomainNameString.length() == 0);
    	} finally {
    	    Kernel32Util.freeLocalMemory(value.getPointer());
    	}
    }

    public void testConvertSid() {
    	String sidString = EVERYONE;
    	PSIDByReference sid = new PSIDByReference();
    	assertTrue("Failed to convert SID string", Advapi32.INSTANCE.ConvertStringSidToSid(sidString, sid));

    	PSID value = sid.getValue();
    	try {
        	PointerByReference convertedSidStringPtr = new PointerByReference();
        	assertTrue("Failed to convert SID string", Advapi32.INSTANCE.ConvertSidToStringSid(value, convertedSidStringPtr));

        	Pointer conv = convertedSidStringPtr.getValue();
        	try {
            	String convertedSidString = conv.getWideString(0);
            	assertEquals("Mismatched SID string", convertedSidString, sidString);
        	} finally {
        	    Kernel32Util.freeLocalMemory(conv);
        	}
    	} finally {
    	    Kernel32Util.freeLocalMemory(value.getPointer());
    	}
    }

    public void testLogonUser() {
    	HANDLEByReference phToken = new HANDLEByReference();
    	assertFalse(Advapi32.INSTANCE.LogonUser("AccountDoesntExist", ".", "passwordIsInvalid",
    			WinBase.LOGON32_LOGON_NETWORK, WinBase.LOGON32_PROVIDER_DEFAULT, phToken));
    	assertTrue(W32Errors.ERROR_SUCCESS != Kernel32.INSTANCE.GetLastError());
    }

    public void testOpenThreadTokenNoToken() {
    	HANDLEByReference phToken = new HANDLEByReference();
    	HANDLE threadHandle = Kernel32.INSTANCE.GetCurrentThread();
    	assertNotNull(threadHandle);
    	assertFalse(Advapi32.INSTANCE.OpenThreadToken(threadHandle,
    			WinNT.TOKEN_READ, false, phToken));
    	assertEquals(W32Errors.ERROR_NO_TOKEN, Kernel32.INSTANCE.GetLastError());
    }

    public void testOpenProcessToken() {
    	HANDLEByReference phToken = new HANDLEByReference();
    	try {
        	HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
        	assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle,
        			WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, phToken));
    	} finally {
    	    Kernel32Util.closeHandleRef(phToken);
    	}
    }

    public void testOpenThreadOrProcessToken() {
    	HANDLEByReference phToken = new HANDLEByReference();
    	try {
        	HANDLE threadHandle = Kernel32.INSTANCE.GetCurrentThread();
        	if (! Advapi32.INSTANCE.OpenThreadToken(threadHandle,
        			WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, true, phToken)) {
            	assertEquals(W32Errors.ERROR_NO_TOKEN, Kernel32.INSTANCE.GetLastError());
            	HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
            	assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle,
            			WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, phToken));
        	}
        } finally {
            Kernel32Util.closeHandleRef(phToken);
        }
    }

    public void testSetThreadTokenCurrentThread() {
        HANDLEByReference phToken = new HANDLEByReference();
        HANDLEByReference phTokenDup = new HANDLEByReference();
        try {
            HANDLE threadHandle = Kernel32.INSTANCE.GetCurrentThread();
            // See if thread has a token. If not, must duplicate process token and set thread token using that.
            if (!Advapi32.INSTANCE.OpenThreadToken(threadHandle,
                 WinNT.TOKEN_IMPERSONATE | WinNT.TOKEN_QUERY, false, phToken)) {
                assertEquals(W32Errors.ERROR_NO_TOKEN, Kernel32.INSTANCE.GetLastError());
                HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
                assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle, WinNT.TOKEN_DUPLICATE, phToken));
                assertTrue(Advapi32.INSTANCE.DuplicateTokenEx(phToken.getValue(),
                            WinNT.TOKEN_IMPERSONATE,
                            null,
                            WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                            WinNT.TOKEN_TYPE.TokenImpersonation,
                            phTokenDup));
                // Null sets on current thread
                assertTrue(Advapi32.INSTANCE.SetThreadToken(null, phTokenDup.getValue()));
            }
            else {
                //Null sets on current thread
                assertTrue(Advapi32.INSTANCE.SetThreadToken(null, phToken.getValue()));
            }
            // Revert and cleanup
            assertTrue(Advapi32.INSTANCE.SetThreadToken(null, null));
        } finally {
            Kernel32Util.closeHandleRefs(phToken, phTokenDup);
        }
    }

    public void testSetThreadTokenThisThread() {
        HANDLEByReference phToken = new HANDLEByReference();
        HANDLEByReference phTokenDup = new HANDLEByReference();
        try {
            HANDLEByReference pthreadHandle = new HANDLEByReference();
            pthreadHandle.setValue(Kernel32.INSTANCE.GetCurrentThread());
            // See if thread has a token. If not, must duplicate process token and set thread token using that.
            if (!Advapi32.INSTANCE.OpenThreadToken(pthreadHandle.getValue(),
                 WinNT.TOKEN_IMPERSONATE | WinNT.TOKEN_QUERY, false, phToken)) {
                assertEquals(W32Errors.ERROR_NO_TOKEN, Kernel32.INSTANCE.GetLastError());
                HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
                assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle,
                            WinNT.TOKEN_DUPLICATE, phToken));
                assertTrue(Advapi32.INSTANCE.DuplicateTokenEx(phToken.getValue(),
                            WinNT.TOKEN_IMPERSONATE,
                            null,
                            WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                            WinNT.TOKEN_TYPE.TokenImpersonation,
                            phTokenDup));
                // Use HANDLEByReference on this thread to test, should be good enough for API compatibility.
                assertTrue(Advapi32.INSTANCE.SetThreadToken(pthreadHandle, phTokenDup.getValue()));
            }
            else {
                // Use HANDLEByReference on this thread to test, should be good enough for API compatibility.
                assertTrue(Advapi32.INSTANCE.SetThreadToken(pthreadHandle, phToken.getValue()));
            }
            // Revert and cleanup
            assertTrue(Advapi32.INSTANCE.SetThreadToken(null, null));
        } finally {
            Kernel32Util.closeHandleRefs(phToken, phTokenDup);
        }
    }

    public void testDuplicateToken() {
    	HANDLEByReference phToken = new HANDLEByReference();
    	HANDLEByReference phTokenDup = new HANDLEByReference();
    	HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
    	try {
            assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle,
            		WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, phToken));
            assertTrue(Advapi32.INSTANCE.DuplicateToken(phToken.getValue(),
            		WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, phTokenDup));
    	} finally {
            Kernel32Util.closeHandleRefs(phTokenDup, phToken);
    	}
    }

    public void testDuplicateTokenEx() {
    	HANDLEByReference hExistingToken = new HANDLEByReference();
    	HANDLEByReference phNewToken = new HANDLEByReference();
    	try {
        	HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
        	assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle,
        			WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, hExistingToken));
        	assertTrue(Advapi32.INSTANCE.DuplicateTokenEx(hExistingToken.getValue(),
        			WinNT.GENERIC_READ, null, SECURITY_IMPERSONATION_LEVEL.SecurityAnonymous,
        			TOKEN_TYPE.TokenPrimary, phNewToken));
    	} finally {
    	    Kernel32Util.closeHandleRefs(phNewToken, hExistingToken);
    	}
    }

    public void testGetTokenOwnerInformation() {
    	HANDLEByReference phToken = new HANDLEByReference();
    	try {
        	HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
            assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle,
            		WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, phToken));
            IntByReference tokenInformationLength = new IntByReference();
            assertFalse(Advapi32.INSTANCE.GetTokenInformation(phToken.getValue(),
            		WinNT.TOKEN_INFORMATION_CLASS.TokenOwner, null, 0, tokenInformationLength));
            assertEquals(W32Errors.ERROR_INSUFFICIENT_BUFFER, Kernel32.INSTANCE.GetLastError());
    		WinNT.TOKEN_OWNER owner = new WinNT.TOKEN_OWNER(tokenInformationLength.getValue());
            assertTrue(Advapi32.INSTANCE.GetTokenInformation(phToken.getValue(),
            		WinNT.TOKEN_INFORMATION_CLASS.TokenOwner, owner,
            		tokenInformationLength.getValue(), tokenInformationLength));
            assertTrue(tokenInformationLength.getValue() > 0);
            assertTrue(Advapi32.INSTANCE.IsValidSid(owner.Owner));
            int sidLength = Advapi32.INSTANCE.GetLengthSid(owner.Owner);
            assertTrue(sidLength < tokenInformationLength.getValue());
            assertTrue(sidLength > 0);
        	// System.out.println(Advapi32Util.convertSidToStringSid(owner.Owner));
    	} finally {
    	    Kernel32Util.closeHandleRef(phToken);
    	}
    }

    public void testGetTokenUserInformation() {
    	HANDLEByReference phToken = new HANDLEByReference();
    	try {
        	HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
            assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle,
            		WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, phToken));
            IntByReference tokenInformationLength = new IntByReference();
            assertFalse(Advapi32.INSTANCE.GetTokenInformation(phToken.getValue(),
            		WinNT.TOKEN_INFORMATION_CLASS.TokenUser, null, 0, tokenInformationLength));
            assertEquals(W32Errors.ERROR_INSUFFICIENT_BUFFER, Kernel32.INSTANCE.GetLastError());
    		WinNT.TOKEN_USER user = new WinNT.TOKEN_USER(tokenInformationLength.getValue());
            assertTrue(Advapi32.INSTANCE.GetTokenInformation(phToken.getValue(),
            		WinNT.TOKEN_INFORMATION_CLASS.TokenUser, user,
            		tokenInformationLength.getValue(), tokenInformationLength));
            assertTrue(tokenInformationLength.getValue() > 0);
            assertTrue(Advapi32.INSTANCE.IsValidSid(user.User.Sid));
            int sidLength = Advapi32.INSTANCE.GetLengthSid(user.User.Sid);
            assertTrue(sidLength > 0);
            assertTrue(sidLength < tokenInformationLength.getValue());
            // System.out.println(Advapi32Util.convertSidToStringSid(user.User.Sid));
    	} finally {
    	    Kernel32Util.closeHandleRef(phToken);
    	}
    }

    public void testGetTokenGroupsInformation() {
    	HANDLEByReference phToken = new HANDLEByReference();
    	try {
        	HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
            assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle,
            		WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, phToken));
            IntByReference tokenInformationLength = new IntByReference();
            assertFalse(Advapi32.INSTANCE.GetTokenInformation(phToken.getValue(),
            		WinNT.TOKEN_INFORMATION_CLASS.TokenGroups, null, 0, tokenInformationLength));
            assertEquals(W32Errors.ERROR_INSUFFICIENT_BUFFER, Kernel32.INSTANCE.GetLastError());
    		WinNT.TOKEN_GROUPS groups = new WinNT.TOKEN_GROUPS(tokenInformationLength.getValue());
            assertTrue(Advapi32.INSTANCE.GetTokenInformation(phToken.getValue(),
            		WinNT.TOKEN_INFORMATION_CLASS.TokenGroups, groups,
            		tokenInformationLength.getValue(), tokenInformationLength));
            assertTrue(tokenInformationLength.getValue() > 0);
            assertTrue(groups.GroupCount > 0);
        	for (SID_AND_ATTRIBUTES sidAndAttribute : groups.getGroups()) {
        		assertTrue(Advapi32.INSTANCE.IsValidSid(sidAndAttribute.Sid));
        		// System.out.println(Advapi32Util.convertSidToStringSid(sidAndAttribute.Sid));
        	}
    	} finally {
    	    Kernel32Util.closeHandleRef(phToken);
    	}
    }

    public void testImpersonateLoggedOnUser() {
    	USER_INFO_1 userInfo = new USER_INFO_1();
    	userInfo.usri1_name = "JNAAdvapi32TestImp";
    	userInfo.usri1_password = "!JNAP$$Wrd0";
    	userInfo.usri1_priv = LMAccess.USER_PRIV_USER;
        // ignore test if not able to add user (need to be administrator to do this).
    	if (LMErr.NERR_Success != Netapi32.INSTANCE.NetUserAdd(null, 1, userInfo, null)) {
            return;
        }
		try {
			HANDLEByReference phUser = new HANDLEByReference();
			try {
				assertTrue(Advapi32.INSTANCE.LogonUser(userInfo.usri1_name.toString(),
						null, userInfo.usri1_password.toString(), WinBase.LOGON32_LOGON_NETWORK,
						WinBase.LOGON32_PROVIDER_DEFAULT, phUser));
				assertTrue(Advapi32.INSTANCE.ImpersonateLoggedOnUser(phUser.getValue()));
				assertTrue(Advapi32.INSTANCE.RevertToSelf());
			} finally {
			    HANDLE hUser = phUser.getValue();
				if (!WinBase.INVALID_HANDLE_VALUE.equals(hUser)) {
					Kernel32Util.closeHandle(hUser);
				}
			}
		} finally {
	    	assertEquals(LMErr.NERR_Success, Netapi32.INSTANCE.NetUserDel(
	    			null, userInfo.usri1_name.toString()));
		}
    }

    public void testRegOpenKeyEx() {
    	HKEYByReference phKey = new HKEYByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegOpenKeyEx(
    			WinReg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft", 0, WinNT.KEY_READ, phKey));
    	assertTrue(WinBase.INVALID_HANDLE_VALUE != phKey.getValue());
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCloseKey(phKey.getValue()));
    }

    public void testRegQueryValueEx() {
    	HKEYByReference phKey = new HKEYByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegOpenKeyEx(
    			WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, WinNT.KEY_READ, phKey));
    	IntByReference lpcbData = new IntByReference();
    	IntByReference lpType = new IntByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegQueryValueEx(
    			phKey.getValue(), "User Agent", 0, lpType, (char[]) null, lpcbData));
    	assertEquals(WinNT.REG_SZ, lpType.getValue());
    	assertTrue(lpcbData.getValue() > 0);
    	char[] buffer = new char[lpcbData.getValue()];
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegQueryValueEx(
    			phKey.getValue(), "User Agent", 0, lpType, buffer, lpcbData));
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCloseKey(phKey.getValue()));
    }

    public void testRegDeleteValue() {
    	assertEquals(W32Errors.ERROR_FILE_NOT_FOUND, Advapi32.INSTANCE.RegDeleteValue(
    			WinReg.HKEY_CURRENT_USER, "JNAAdvapi32TestDoesntExist"));
    }

    public void testRegSetValueEx_REG_SZ() {
    	HKEYByReference phKey = new HKEYByReference();
    	// create parent key
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegOpenKeyEx(
    			WinReg.HKEY_CURRENT_USER, "Software", 0, WinNT.KEY_WRITE | WinNT.KEY_READ, phKey));
    	HKEYByReference phkTest = new HKEYByReference();
    	IntByReference lpdwDisposition = new IntByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCreateKeyEx(
    			phKey.getValue(), "JNAAdvapi32Test", 0, null, 0, WinNT.KEY_ALL_ACCESS,
    			null, phkTest, lpdwDisposition));
    	// write a REG_SZ value
    	char[] lpData = Native.toCharArray("Test");
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegSetValueEx(
    			phkTest.getValue(), "REG_SZ", 0, WinNT.REG_SZ, lpData, lpData.length * 2));
    	// re-read the REG_SZ value
    	IntByReference lpType = new IntByReference();
    	IntByReference lpcbData = new IntByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegQueryValueEx(
    			phkTest.getValue(), "REG_SZ", 0, lpType, (char[]) null, lpcbData));
    	assertEquals(WinNT.REG_SZ, lpType.getValue());
    	assertTrue(lpcbData.getValue() > 0);
    	char[] buffer = new char[lpcbData.getValue()];
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegQueryValueEx(
    			phkTest.getValue(), "REG_SZ", 0, lpType, buffer, lpcbData));
    	assertEquals("Test", Native.toString(buffer));
    	// delete the test key
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCloseKey(
    			phkTest.getValue()));
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegDeleteKey(
    			phKey.getValue(), "JNAAdvapi32Test"));
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCloseKey(phKey.getValue()));
    }

    public void testRegSetValueEx_DWORD() {
    	HKEYByReference phKey = new HKEYByReference();
    	// create parent key
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegOpenKeyEx(
    			WinReg.HKEY_CURRENT_USER, "Software", 0, WinNT.KEY_WRITE | WinNT.KEY_READ, phKey));
    	HKEYByReference phkTest = new HKEYByReference();
    	IntByReference lpdwDisposition = new IntByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCreateKeyEx(
    			phKey.getValue(), "JNAAdvapi32Test", 0, null, 0, WinNT.KEY_ALL_ACCESS,
    			null, phkTest, lpdwDisposition));
    	// write a REG_DWORD value
    	int value = 42145;
        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);
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegSetValueEx(
    			phkTest.getValue(), "DWORD", 0, WinNT.REG_DWORD, data, 4));
    	// re-read the REG_DWORD value
    	IntByReference lpType = new IntByReference();
    	IntByReference lpcbData = new IntByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegQueryValueEx(
    			phkTest.getValue(), "DWORD", 0, lpType, (char[]) null, lpcbData));
    	assertEquals(WinNT.REG_DWORD, lpType.getValue());
    	assertEquals(4, lpcbData.getValue());
    	IntByReference valueRead = new IntByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegQueryValueEx(
    			phkTest.getValue(), "DWORD", 0, lpType, valueRead, lpcbData));
    	assertEquals(value, valueRead.getValue());
    	// delete the test key
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCloseKey(
    			phkTest.getValue()));
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegDeleteKey(
    			phKey.getValue(), "JNAAdvapi32Test"));
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCloseKey(phKey.getValue()));
    }

    public void testRegCreateKeyEx() {
    	HKEYByReference phKey = new HKEYByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegOpenKeyEx(
    			WinReg.HKEY_CURRENT_USER, "Software", 0, WinNT.KEY_WRITE | WinNT.KEY_READ, phKey));
    	HKEYByReference phkResult = new HKEYByReference();
    	IntByReference lpdwDisposition = new IntByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCreateKeyEx(
    			phKey.getValue(), "JNAAdvapi32Test", 0, null, 0, WinNT.KEY_ALL_ACCESS,
    			null, phkResult, lpdwDisposition));
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCloseKey(phkResult.getValue()));
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegDeleteKey(
    			phKey.getValue(), "JNAAdvapi32Test"));
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCloseKey(phKey.getValue()));
    }

    public void testRegDeleteKey() {
    	assertEquals(W32Errors.ERROR_FILE_NOT_FOUND, Advapi32.INSTANCE.RegDeleteKey(
    			WinReg.HKEY_CURRENT_USER, "JNAAdvapi32TestDoesntExist"));
    }

    public void testRegEnumKeyEx() {
    	HKEYByReference phKey = new HKEYByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegOpenKeyEx(
    			WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
    			0, WinNT.KEY_READ, phKey));
    	IntByReference lpcSubKeys = new IntByReference();
    	IntByReference lpcMaxSubKeyLen = new IntByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegQueryInfoKey(
    			phKey.getValue(), null, null, null, lpcSubKeys, lpcMaxSubKeyLen, null, null,
    			null, null, null, null));
    	char[] name = new char[lpcMaxSubKeyLen.getValue() + 1];
    	for (int i = 0; i < lpcSubKeys.getValue(); i++) {
    		IntByReference lpcchValueName = new IntByReference(lpcMaxSubKeyLen.getValue() + 1);
        	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegEnumKeyEx(
        			phKey.getValue(), i, name, lpcchValueName, null, null, null, null));
        	assertEquals(Native.toString(name).length(), lpcchValueName.getValue());
    	}
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCloseKey(phKey.getValue()));
    }

    public void testRegEnumValue() {
    	HKEYByReference phKey = new HKEYByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegOpenKeyEx(
    			WinReg.HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
    			0, WinNT.KEY_READ, phKey));
    	IntByReference lpcValues = new IntByReference();
    	IntByReference lpcMaxValueNameLen = new IntByReference();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegQueryInfoKey(
    			phKey.getValue(), null, null, null, null, null, null, lpcValues,
    			lpcMaxValueNameLen, null, null, null));
    	char[] name = new char[lpcMaxValueNameLen.getValue() + 1];
    	for (int i = 0; i < lpcValues.getValue(); i++) {
    		IntByReference lpcchValueName = new IntByReference(lpcMaxValueNameLen.getValue() + 1);
    		IntByReference lpType = new IntByReference();
        	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegEnumValue(
        			phKey.getValue(), i, name, lpcchValueName, null,
        			lpType, null, null));
        	assertEquals(Native.toString(name).length(), lpcchValueName.getValue());
    	}
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegCloseKey(phKey.getValue()));
    }

    public void testRegQueryInfoKey() {
    	IntByReference lpcClass = new IntByReference();
    	IntByReference lpcSubKeys = new IntByReference();
    	IntByReference lpcMaxSubKeyLen = new IntByReference();
    	IntByReference lpcValues = new IntByReference();
    	IntByReference lpcMaxClassLen = new IntByReference();
    	IntByReference lpcMaxValueNameLen = new IntByReference();
    	IntByReference lpcMaxValueLen = new IntByReference();
    	IntByReference lpcbSecurityDescriptor = new IntByReference();
    	FILETIME lpftLastWriteTime = new FILETIME();
    	assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.RegQueryInfoKey(
    			WinReg.HKEY_LOCAL_MACHINE, null, lpcClass, null,
    			lpcSubKeys, lpcMaxSubKeyLen, lpcMaxClassLen, lpcValues,
    			lpcMaxValueNameLen, lpcMaxValueLen, lpcbSecurityDescriptor,
    			lpftLastWriteTime));
    	assertTrue(lpcSubKeys.getValue() > 0);
    }

    public void testIsWellKnownSid() {
    	String sidString = EVERYONE;
    	PSIDByReference sid = new PSIDByReference();
    	assertTrue("sid conversion failed", Advapi32.INSTANCE.ConvertStringSidToSid(sidString, sid));

    	PSID value = sid.getValue();
    	try {
    	    assertTrue("Not a world sid", Advapi32.INSTANCE.IsWellKnownSid(value, WELL_KNOWN_SID_TYPE.WinWorldSid));
    	    assertFalse("Unexpected admin sid", Advapi32.INSTANCE.IsWellKnownSid(value, WELL_KNOWN_SID_TYPE.WinAccountAdministratorSid));
    	} finally {
    	    Kernel32Util.freeLocalMemory(value.getPointer());
    	}
    }

    public void testCreateWellKnownSid() {
    	PSID pSid = new PSID(WinNT.SECURITY_MAX_SID_SIZE);
    	IntByReference cbSid = new IntByReference(WinNT.SECURITY_MAX_SID_SIZE);
    	assertTrue("Failed to create well-known SID",
    	        Advapi32.INSTANCE.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinWorldSid, null, pSid, cbSid));
    	assertTrue("Not recognized as well-known SID",
    	        Advapi32.INSTANCE.IsWellKnownSid(pSid, WELL_KNOWN_SID_TYPE.WinWorldSid));
    	assertTrue("Invalid SID size", cbSid.getValue() <= WinNT.SECURITY_MAX_SID_SIZE);
    	PointerByReference convertedSidStringPtr = new PointerByReference();
    	assertTrue("Failed to convert SID", Advapi32.INSTANCE.ConvertSidToStringSid(pSid, convertedSidStringPtr));

    	Pointer conv = convertedSidStringPtr.getValue();
    	try {
    	    String convertedSidString = conv.getWideString(0);
    	    assertEquals("Mismatched SID string", EVERYONE, convertedSidString);
    	} finally {
    	    Kernel32Util.freeLocalMemory(conv);
    	}
    }

    public void testInitializeSecurityDescriptor() {
        SECURITY_DESCRIPTOR sd = new SECURITY_DESCRIPTOR(64 * 1024);
        assertTrue(Advapi32.INSTANCE.InitializeSecurityDescriptor(sd, WinNT.SECURITY_DESCRIPTOR_REVISION));
    }

    public void testSetGetSecurityDescriptorControl() {
        SECURITY_DESCRIPTOR sd = new SECURITY_DESCRIPTOR(64 * 1024);
        assertTrue(Advapi32.INSTANCE.InitializeSecurityDescriptor(sd, WinNT.SECURITY_DESCRIPTOR_REVISION));
        assertTrue(Advapi32.INSTANCE.SetSecurityDescriptorControl(sd, (short)WinNT.SE_DACL_PROTECTED, (short)WinNT.SE_DACL_PROTECTED));
        ShortByReference pControl = new ShortByReference();
        IntByReference lpdwRevision = new IntByReference();
        assertTrue(Advapi32.INSTANCE.GetSecurityDescriptorControl(sd, pControl, lpdwRevision));
        assertTrue(pControl.getValue() == WinNT.SE_DACL_PROTECTED);
        assertTrue(lpdwRevision.getValue() == WinNT.SECURITY_DESCRIPTOR_REVISION);
    }

    public void testSetGetSecurityDescriptorOwner() {
        SECURITY_DESCRIPTOR sd = new SECURITY_DESCRIPTOR(64 * 1024);
        assertTrue(Advapi32.INSTANCE.InitializeSecurityDescriptor(sd, WinNT.SECURITY_DESCRIPTOR_REVISION));

        PSID pSidPut = new PSID(WinNT.SECURITY_MAX_SID_SIZE);
        IntByReference cbSid = new IntByReference(WinNT.SECURITY_MAX_SID_SIZE);
        assertTrue("Failed to create well-known SID",
                Advapi32.INSTANCE.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinBuiltinAdministratorsSid, null, pSidPut, cbSid));

        assertTrue(Advapi32.INSTANCE.SetSecurityDescriptorOwner(sd, pSidPut, true));

        BOOLByReference lpbOwnerDefaulted = new BOOLByReference();
        PSIDByReference prSd = new PSIDByReference();
        assertTrue(Advapi32.INSTANCE.GetSecurityDescriptorOwner(sd, prSd, lpbOwnerDefaulted));

        PSID pSidGet = prSd.getValue();
        assertTrue(Advapi32.INSTANCE.EqualSid(pSidPut, pSidGet));
    }

    public void testSetGetSecurityDescriptorGroup() {
        SECURITY_DESCRIPTOR sd = new SECURITY_DESCRIPTOR(64 * 1024);
        assertTrue(Advapi32.INSTANCE.InitializeSecurityDescriptor(sd, WinNT.SECURITY_DESCRIPTOR_REVISION));

        PSID pSidPut = new PSID(WinNT.SECURITY_MAX_SID_SIZE);
        IntByReference cbSid = new IntByReference(WinNT.SECURITY_MAX_SID_SIZE);
        assertTrue("Failed to create well-known SID",
                Advapi32.INSTANCE.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinBuiltinAdministratorsSid, null, pSidPut, cbSid));

        assertTrue(Advapi32.INSTANCE.SetSecurityDescriptorGroup(sd, pSidPut, true));

        BOOLByReference lpbOwnerDefaulted = new BOOLByReference();
        PSIDByReference prSd = new PSIDByReference();
        assertTrue(Advapi32.INSTANCE.GetSecurityDescriptorGroup(sd, prSd, lpbOwnerDefaulted));

        PSID pSidGet = prSd.getValue();
        assertTrue(Advapi32.INSTANCE.EqualSid(pSidPut, pSidGet));
    }

    public void testSetGetSecurityDescriptorDacl() throws IOException {
        SECURITY_DESCRIPTOR sd = new SECURITY_DESCRIPTOR(64 * 1024);
        assertTrue(Advapi32.INSTANCE.InitializeSecurityDescriptor(sd, WinNT.SECURITY_DESCRIPTOR_REVISION));

        ACL pAcl;
        int cbAcl = 0;
        PSID pSid = new PSID(WinNT.SECURITY_MAX_SID_SIZE);
        IntByReference cbSid = new IntByReference(WinNT.SECURITY_MAX_SID_SIZE);
        assertTrue("Failed to create well-known SID",
                Advapi32.INSTANCE.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinBuiltinAdministratorsSid, null, pSid, cbSid));

        int sidLength = Advapi32.INSTANCE.GetLengthSid(pSid);
        cbAcl = Native.getNativeSize(ACL.class, null);
        cbAcl += Native.getNativeSize(ACCESS_ALLOWED_ACE.class, null);
        cbAcl += (sidLength - DWORD.SIZE);
        cbAcl = Advapi32Util.alignOnDWORD(cbAcl);
        pAcl = new ACL(cbAcl);
        assertTrue(Advapi32.INSTANCE.InitializeAcl(pAcl, cbAcl, WinNT.ACL_REVISION));
        assertTrue(Advapi32.INSTANCE.AddAccessAllowedAce(pAcl, WinNT.ACL_REVISION, WinNT.STANDARD_RIGHTS_ALL, pSid));
        assertTrue(Advapi32.INSTANCE.SetSecurityDescriptorDacl(sd, true, pAcl, false));
        BOOLByReference lpbDaclPresent  = new BOOLByReference();
        BOOLByReference lpbDaclDefaulted  = new BOOLByReference();
        PACLByReference pDacl = new PACLByReference();
        assertTrue(Advapi32.INSTANCE.GetSecurityDescriptorDacl(sd, lpbDaclPresent, pDacl, lpbDaclDefaulted));
        ACL pAclGet = pDacl.getValue();
        assertEquals(new BOOL(true), lpbDaclPresent.getValue());
        assertEquals(new BOOL(false), lpbDaclDefaulted.getValue());
        assertEquals(1, pAclGet.AceCount);
        assertEquals(WinNT.ACL_REVISION, pAclGet.AclRevision);
    }

    public void testInitializeAcl() throws IOException {
        ACL pAcl;
        int cbAcl = 0;
        PSID pSid = new PSID(WinNT.SECURITY_MAX_SID_SIZE);
        IntByReference cbSid = new IntByReference(WinNT.SECURITY_MAX_SID_SIZE);
        assertTrue("Failed to create well-known SID",
                Advapi32.INSTANCE.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinBuiltinAdministratorsSid, null, pSid, cbSid));

        int sidLength = Advapi32.INSTANCE.GetLengthSid(pSid);
        cbAcl = Native.getNativeSize(ACL.class, null);
        cbAcl += Native.getNativeSize(ACCESS_ALLOWED_ACE.class, null);
        cbAcl += (sidLength - DWORD.SIZE);
        cbAcl = Advapi32Util.alignOnDWORD(cbAcl);
        pAcl = new ACL(cbAcl);
        assertTrue(Advapi32.INSTANCE.InitializeAcl(pAcl, cbAcl, WinNT.ACL_REVISION));
    }

    public void testGetAce() throws IOException {
        ACL pAcl;
        int cbAcl = 0;
        PSID pSid = new PSID(WinNT.SECURITY_MAX_SID_SIZE);
        IntByReference cbSid = new IntByReference(WinNT.SECURITY_MAX_SID_SIZE);
        assertTrue("Failed to create well-known SID",
                Advapi32.INSTANCE.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinBuiltinAdministratorsSid, null, pSid, cbSid));

        int sidLength = Advapi32.INSTANCE.GetLengthSid(pSid);
        cbAcl = Native.getNativeSize(ACL.class, null);
        cbAcl += Native.getNativeSize(ACCESS_ALLOWED_ACE.class, null);
        cbAcl += (sidLength - DWORD.SIZE);
        cbAcl = Advapi32Util.alignOnDWORD(cbAcl);
        pAcl = new ACL(cbAcl);
        assertTrue(Advapi32.INSTANCE.InitializeAcl(pAcl, cbAcl, WinNT.ACL_REVISION));
        assertTrue(Advapi32.INSTANCE.AddAccessAllowedAce(pAcl, WinNT.ACL_REVISION, WinNT.STANDARD_RIGHTS_ALL, pSid));

        PointerByReference pAce = new PointerByReference(new Memory(16));
        assertTrue(Advapi32.INSTANCE.GetAce(pAcl, 0, pAce));
        ACCESS_ALLOWED_ACE pAceGet = new ACCESS_ALLOWED_ACE(pAce.getValue());
        assertTrue(pAceGet.Mask == WinNT.STANDARD_RIGHTS_ALL);
        assertTrue(Advapi32.INSTANCE.EqualSid(pAceGet.psid, pSid));
    }

    public void testAddAce() throws IOException {
        ACL pAcl;
        int cbAcl = 0;
        PSID pSid = new PSID(WinNT.SECURITY_MAX_SID_SIZE);
        IntByReference cbSid = new IntByReference(WinNT.SECURITY_MAX_SID_SIZE);
        assertTrue("Failed to create well-known SID",
                Advapi32.INSTANCE.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinBuiltinAdministratorsSid, null, pSid, cbSid));

        int sidLength = Advapi32.INSTANCE.GetLengthSid(pSid);
        cbAcl = Native.getNativeSize(ACL.class, null);
        cbAcl += Advapi32Util.getAceSize(sidLength);
        cbAcl = Advapi32Util.alignOnDWORD(cbAcl);
        pAcl = new ACL(cbAcl);
        ACCESS_ALLOWED_ACE pace = new ACCESS_ALLOWED_ACE(WinNT.STANDARD_RIGHTS_ALL,
                WinNT.INHERITED_ACE,
                pSid);

        assertTrue(Advapi32.INSTANCE.InitializeAcl(pAcl, cbAcl, WinNT.ACL_REVISION));
        assertTrue(Advapi32.INSTANCE.AddAce(pAcl, WinNT.ACL_REVISION, WinNT.MAXDWORD, pace.getPointer(), pace.size()));

        PointerByReference pAce = new PointerByReference(new Memory(16));
        assertTrue(Advapi32.INSTANCE.GetAce(pAcl, 0, pAce));
        ACCESS_ALLOWED_ACE pAceGet = new ACCESS_ALLOWED_ACE(pAce.getValue());
        assertTrue(pAceGet.Mask == WinNT.STANDARD_RIGHTS_ALL);
        assertTrue(Advapi32.INSTANCE.EqualSid(pAceGet.psid, pSid));
    }

    public void testAddAccessAllowedAce() throws IOException {
        ACL pAcl;
        int cbAcl = 0;
        PSID pSid = new PSID(WinNT.SECURITY_MAX_SID_SIZE);
        IntByReference cbSid = new IntByReference(WinNT.SECURITY_MAX_SID_SIZE);
        assertTrue("Failed to create well-known SID",
                Advapi32.INSTANCE.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinBuiltinAdministratorsSid, null, pSid, cbSid));

        int sidLength = Advapi32.INSTANCE.GetLengthSid(pSid);
        cbAcl = Native.getNativeSize(ACL.class, null);
        cbAcl += Advapi32Util.getAceSize(sidLength);
        cbAcl = Advapi32Util.alignOnDWORD(cbAcl);
        pAcl = new ACL(cbAcl);
        assertTrue(Advapi32.INSTANCE.InitializeAcl(pAcl, cbAcl, WinNT.ACL_REVISION));
        assertTrue(Advapi32.INSTANCE.AddAccessAllowedAce(pAcl, WinNT.ACL_REVISION, WinNT.STANDARD_RIGHTS_ALL, pSid));

        PointerByReference pAce = new PointerByReference(new Memory(16));
        assertTrue(Advapi32.INSTANCE.GetAce(pAcl, 0, pAce));
        ACCESS_ALLOWED_ACE pAceGet = new ACCESS_ALLOWED_ACE(pAce.getValue());
        assertTrue(pAceGet.Mask == WinNT.STANDARD_RIGHTS_ALL);
        assertTrue(Advapi32.INSTANCE.EqualSid(pAceGet.psid, pSid));
    }

    public void testAddAccessAllowedAceEx() throws IOException {
        ACL pAcl;
        int cbAcl = 0;
        PSID pSid = new PSID(WinNT.SECURITY_MAX_SID_SIZE);
        IntByReference cbSid = new IntByReference(WinNT.SECURITY_MAX_SID_SIZE);
        assertTrue("Failed to create well-known SID",
                Advapi32.INSTANCE.CreateWellKnownSid(WELL_KNOWN_SID_TYPE.WinBuiltinAdministratorsSid, null, pSid, cbSid));

        int sidLength = Advapi32.INSTANCE.GetLengthSid(pSid);
        cbAcl = Native.getNativeSize(ACL.class, null);
        cbAcl += Advapi32Util.getAceSize(sidLength);
        cbAcl = Advapi32Util.alignOnDWORD(cbAcl);
        pAcl = new ACL(cbAcl);
        assertTrue(Advapi32.INSTANCE.InitializeAcl(pAcl, cbAcl, WinNT.ACL_REVISION));
        assertTrue(Advapi32.INSTANCE.AddAccessAllowedAceEx(pAcl, WinNT.ACL_REVISION, WinNT.INHERIT_ONLY_ACE, WinNT.STANDARD_RIGHTS_ALL, pSid));

        PointerByReference pAce = new PointerByReference(new Memory(16));
        assertTrue(Advapi32.INSTANCE.GetAce(pAcl, 0, pAce));
        ACCESS_ALLOWED_ACE pAceGet = new ACCESS_ALLOWED_ACE(pAce.getValue());
        assertTrue(pAceGet.Mask == WinNT.STANDARD_RIGHTS_ALL);
        assertTrue(Advapi32.INSTANCE.EqualSid(pAceGet.psid, pSid));
    }

    public void testOpenEventLog() {
    	HANDLE h = Advapi32.INSTANCE.OpenEventLog(null, "Application");
    	assertNotNull(h);
    	assertFalse(h.equals(WinBase.INVALID_HANDLE_VALUE));
    	assertTrue(Advapi32.INSTANCE.CloseEventLog(h));
    }

    public void testRegisterEventSource() {
    	// the Security event log is reserved
    	HANDLE h = Advapi32.INSTANCE.RegisterEventSource(null, "Security");
    	assertNull(h);
    	assertEquals(W32Errors.ERROR_ACCESS_DENIED, Kernel32.INSTANCE.GetLastError());
    }

    public void testReportEvent() {
    	String applicationEventLog = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application";
    	String jnaEventSource = "JNADevEventSource";
    	String jnaEventSourceRegistryPath = applicationEventLog + "\\" + jnaEventSource;
        // ignore test if not able to create key (need to be administrator to do this).
        try {
            final boolean keyCreated = Advapi32Util.registryCreateKey(WinReg.HKEY_LOCAL_MACHINE, jnaEventSourceRegistryPath);
            if (!keyCreated) {
                return;
            }
        } catch (Win32Exception e) {
            return;
        }

    	HANDLE h = Advapi32.INSTANCE.RegisterEventSource(null, jnaEventSource);
    	IntByReference before = new IntByReference();
    	assertTrue(Advapi32.INSTANCE.GetNumberOfEventLogRecords(h, before));
    	assertNotNull(h);
    	String s[] = { "JNA", "Event" };
    	Memory m = new Memory(4);
    	m.setByte(0, (byte) 1);
    	m.setByte(1, (byte) 2);
    	m.setByte(2, (byte) 3);
    	m.setByte(3, (byte) 4);
    	assertTrue(Advapi32.INSTANCE.ReportEvent(h, WinNT.EVENTLOG_ERROR_TYPE, 0, 0, null, 2, 4, s, m));
    	IntByReference after = new IntByReference();
    	assertTrue(Advapi32.INSTANCE.GetNumberOfEventLogRecords(h, after));
    	assertTrue(before.getValue() < after.getValue());
    	assertFalse(h.equals(WinBase.INVALID_HANDLE_VALUE));
    	assertTrue(Advapi32.INSTANCE.DeregisterEventSource(h));
    	Advapi32Util.registryDeleteKey(WinReg.HKEY_LOCAL_MACHINE, jnaEventSourceRegistryPath);
    }

    public void testGetNumberOfEventLogRecords() {
    	HANDLE h = Advapi32.INSTANCE.OpenEventLog(null, "Application");
    	assertFalse(h.equals(WinBase.INVALID_HANDLE_VALUE));
    	IntByReference n = new IntByReference();
    	assertTrue(Advapi32.INSTANCE.GetNumberOfEventLogRecords(h, n));
    	assertTrue(n.getValue() >= 0);
    	assertTrue(Advapi32.INSTANCE.CloseEventLog(h));
    }

    /*
    public void testClearEventLog() {
    	HANDLE h = Advapi32.INSTANCE.OpenEventLog(null, "Application");
    	assertFalse(h.equals(WinBase.INVALID_HANDLE_VALUE));
    	IntByReference before = new IntByReference();
    	assertTrue(Advapi32.INSTANCE.GetNumberOfEventLogRecords(h, before));
    	assertTrue(before.getValue() >= 0);
    	assertTrue(Advapi32.INSTANCE.ClearEventLog(h, null));
    	IntByReference after = new IntByReference();
    	assertTrue(Advapi32.INSTANCE.GetNumberOfEventLogRecords(h, after));
    	assertTrue(after.getValue() < before.getValue() || before.getValue() == 0);
    	assertTrue(Advapi32.INSTANCE.CloseEventLog(h));
    }
    */

    public void testBackupEventLog() {
    	HANDLE h = Advapi32.INSTANCE.OpenEventLog(null, "Application");
    	assertNotNull(h);
    	String backupFileName = Kernel32Util.getTempPath() + "\\JNADevEventLog.bak";
    	File f = new File(backupFileName);
    	if (f.exists()) {
    		f.delete();
    	}

    	assertTrue(Advapi32.INSTANCE.BackupEventLog(h, backupFileName));
    	HANDLE hBackup = Advapi32.INSTANCE.OpenBackupEventLog(null, backupFileName);
    	assertNotNull(hBackup);

    	IntByReference n = new IntByReference();
    	assertTrue(Advapi32.INSTANCE.GetNumberOfEventLogRecords(hBackup, n));
    	assertTrue(n.getValue() >= 0);

    	assertTrue(Advapi32.INSTANCE.CloseEventLog(h));
    	assertTrue(Advapi32.INSTANCE.CloseEventLog(hBackup));
    }

    public void testReadEventLog() {
    	HANDLE h = Advapi32.INSTANCE.OpenEventLog(null, "Application");
    	IntByReference pnBytesRead = new IntByReference();
    	IntByReference pnMinNumberOfBytesNeeded = new IntByReference();
    	Memory buffer = new Memory(1);
    	assertFalse(Advapi32.INSTANCE.ReadEventLog(h,
    			WinNT.EVENTLOG_SEQUENTIAL_READ | WinNT.EVENTLOG_BACKWARDS_READ,
    			0, buffer, (int) buffer.size(), pnBytesRead, pnMinNumberOfBytesNeeded));
    	assertEquals(W32Errors.ERROR_INSUFFICIENT_BUFFER, Kernel32.INSTANCE.GetLastError());
    	assertTrue(pnMinNumberOfBytesNeeded.getValue() > 0);
    	assertTrue(Advapi32.INSTANCE.CloseEventLog(h));
    }

    public void testReadEventLogEntries() {
    	HANDLE h = Advapi32.INSTANCE.OpenEventLog(null, "Application");
    	IntByReference pnBytesRead = new IntByReference();
    	IntByReference pnMinNumberOfBytesNeeded = new IntByReference();
    	Memory buffer = new Memory(1024 * 64);
    	// shorten test, avoid iterating through all events
    	int maxReads = 3;
    	int rc = 0;
    	while(true) {
            if (maxReads-- <= 0)
                break;
            if (! Advapi32.INSTANCE.ReadEventLog(h,
                                                 WinNT.EVENTLOG_SEQUENTIAL_READ | WinNT.EVENTLOG_FORWARDS_READ,
                                                 0, buffer, (int) buffer.size(), pnBytesRead, pnMinNumberOfBytesNeeded)) {
                rc = Kernel32.INSTANCE.GetLastError();
                if (rc == W32Errors.ERROR_INSUFFICIENT_BUFFER) {
                    buffer = new Memory(pnMinNumberOfBytesNeeded.getValue());
                    rc = 0;
                    continue;
                }
                break;
            }
            int dwRead = pnBytesRead.getValue();
            Pointer pevlr = buffer;
            int maxRecords = 3;
            while (dwRead > 0 && maxRecords-- > 0) {
                EVENTLOGRECORD record = new EVENTLOGRECORD(pevlr);
                /*
                  System.out.println(record.RecordNumber.intValue()
                  + " Event ID: " + record.EventID.intValue()
                  + " Event Type: " + record.EventType.intValue()
                  + " Event Source: " + pevlr.getString(record.size(), true));
                */
                dwRead -= record.Length.intValue();
                pevlr = pevlr.share(record.Length.intValue());
            }
    	}
        assertTrue("Unexpected error after reading event log: "
                   + new Win32Exception(rc),
                   rc == W32Errors.ERROR_HANDLE_EOF || rc == 0);
        assertTrue("Error closing event log",
                   Advapi32.INSTANCE.CloseEventLog(h));
    }

    public void testGetOldestEventLogRecord() {
    	HANDLE h = Advapi32.INSTANCE.OpenEventLog(null, "Application");
    	IntByReference oldestRecord = new IntByReference();
    	assertTrue(Advapi32.INSTANCE.GetOldestEventLogRecord(h, oldestRecord));
    	assertTrue(oldestRecord.getValue() >= 0);
    	assertTrue(Advapi32.INSTANCE.CloseEventLog(h));
    }

    public void testQueryServiceStatusEx() {

    	SC_HANDLE scmHandle = Advapi32.INSTANCE.OpenSCManager(null, null, Winsvc.SC_MANAGER_CONNECT);
    	assertNotNull(scmHandle);

    	SC_HANDLE serviceHandle = Advapi32.INSTANCE.OpenService(scmHandle, "eventlog", Winsvc.SERVICE_QUERY_STATUS);
    	assertNotNull(serviceHandle);

    	IntByReference pcbBytesNeeded = new IntByReference();

    	assertFalse(Advapi32.INSTANCE.QueryServiceStatusEx(serviceHandle, SC_STATUS_TYPE.SC_STATUS_PROCESS_INFO,
    			null, 0, pcbBytesNeeded));
    	assertEquals(W32Errors.ERROR_INSUFFICIENT_BUFFER, Kernel32.INSTANCE.GetLastError());

    	assertTrue(pcbBytesNeeded.getValue() > 0);

    	SERVICE_STATUS_PROCESS status = new SERVICE_STATUS_PROCESS(pcbBytesNeeded.getValue());

    	assertTrue(Advapi32.INSTANCE.QueryServiceStatusEx(serviceHandle, SC_STATUS_TYPE.SC_STATUS_PROCESS_INFO,
    			status, status.size(), pcbBytesNeeded));

    	assertTrue(status.dwCurrentState == Winsvc.SERVICE_STOPPED ||
    			status.dwCurrentState == Winsvc.SERVICE_RUNNING);

    	assertTrue(Advapi32.INSTANCE.CloseServiceHandle(serviceHandle));
    	assertTrue(Advapi32.INSTANCE.CloseServiceHandle(scmHandle));
    }


    public void testControlService() {
    	SC_HANDLE scmHandle = Advapi32.INSTANCE.OpenSCManager(null, null, Winsvc.SC_MANAGER_CONNECT);
    	assertNotNull(scmHandle);

    	SC_HANDLE serviceHandle = Advapi32.INSTANCE.OpenService(scmHandle, "eventlog", Winsvc.SERVICE_QUERY_CONFIG);
    	assertNotNull(serviceHandle);

    	Winsvc.SERVICE_STATUS serverStatus = new Winsvc.SERVICE_STATUS();

    	assertNotNull(serviceHandle);
    	assertFalse(Advapi32.INSTANCE.ControlService(serviceHandle, Winsvc.SERVICE_CONTROL_STOP, serverStatus));
    	assertEquals(W32Errors.ERROR_ACCESS_DENIED, Kernel32.INSTANCE.GetLastError());

    	assertTrue(Advapi32.INSTANCE.CloseServiceHandle(serviceHandle));
    	assertTrue(Advapi32.INSTANCE.CloseServiceHandle(scmHandle));
    }

    public void testStartService() {
    	SC_HANDLE scmHandle = Advapi32.INSTANCE.OpenSCManager(null, null, Winsvc.SC_MANAGER_CONNECT);
    	assertNotNull(scmHandle);

    	SC_HANDLE serviceHandle = Advapi32.INSTANCE.OpenService(scmHandle, "eventlog", Winsvc.SERVICE_QUERY_CONFIG);
    	assertNotNull(serviceHandle);

    	assertFalse(Advapi32.INSTANCE.StartService(serviceHandle, 0, null));
    	assertEquals(W32Errors.ERROR_ACCESS_DENIED, Kernel32.INSTANCE.GetLastError());

    	assertTrue(Advapi32.INSTANCE.CloseServiceHandle(serviceHandle));
    	assertTrue(Advapi32.INSTANCE.CloseServiceHandle(scmHandle));
    }

    public void testOpenService() {
    	assertNull(Advapi32.INSTANCE.OpenService(null, "eventlog", Winsvc.SERVICE_QUERY_CONFIG ));
    	assertEquals(W32Errors.ERROR_INVALID_HANDLE, Kernel32.INSTANCE.GetLastError());

    	SC_HANDLE scmHandle = Advapi32.INSTANCE.OpenSCManager(null, null, Winsvc.SC_MANAGER_CONNECT);
    	assertNotNull(scmHandle);

    	SC_HANDLE serviceHandle = Advapi32.INSTANCE.OpenService(scmHandle, "eventlog", Winsvc.SERVICE_QUERY_CONFIG );
    	assertNotNull(serviceHandle);
    	assertTrue(Advapi32.INSTANCE.CloseServiceHandle(serviceHandle));

    	assertNull(Advapi32.INSTANCE.OpenService(scmHandle, "slashesArentValidChars/", Winsvc.SERVICE_QUERY_CONFIG ));
    	assertEquals(W32Errors.ERROR_INVALID_NAME, Kernel32.INSTANCE.GetLastError());

    	assertNull(Advapi32.INSTANCE.OpenService(scmHandle, "serviceDoesNotExist", Winsvc.SERVICE_QUERY_CONFIG ));
    	assertEquals(W32Errors.ERROR_SERVICE_DOES_NOT_EXIST, Kernel32.INSTANCE.GetLastError());

    	assertTrue(Advapi32.INSTANCE.CloseServiceHandle(scmHandle));
    }

    public void testOpenSCManager() {
    	SC_HANDLE handle = Advapi32.INSTANCE.OpenSCManager(null, null, Winsvc.SC_MANAGER_CONNECT);
    	assertNotNull(handle);
    	assertTrue(Advapi32.INSTANCE.CloseServiceHandle(handle));

    	assertNull(Advapi32.INSTANCE.OpenSCManager("invalidMachineName", null, Winsvc.SC_MANAGER_CONNECT));
        int err = Kernel32.INSTANCE.GetLastError();
    	assertTrue("Unexpected error in OpenSCManager: " + err,
                   err == W32Errors.RPC_S_SERVER_UNAVAILABLE
                   || err == W32Errors.RPC_S_INVALID_NET_ADDR);

    	assertNull(Advapi32.INSTANCE.OpenSCManager(null, "invalidDatabase", Winsvc.SC_MANAGER_CONNECT));
    	assertEquals(W32Errors.ERROR_INVALID_NAME, Kernel32.INSTANCE.GetLastError());
    }

    public void testCloseServiceHandle() throws Exception {
    	SC_HANDLE handle = Advapi32.INSTANCE.OpenSCManager(null, null, Winsvc.SC_MANAGER_CONNECT);
    	assertNotNull(handle);
    	assertTrue(Advapi32.INSTANCE.CloseServiceHandle(handle));

    	assertFalse(Advapi32.INSTANCE.CloseServiceHandle(null));
    	assertEquals(W32Errors.ERROR_INVALID_HANDLE, Kernel32.INSTANCE.GetLastError());
    }

    public void testCreateProcessAsUser() {
    	HANDLEByReference hToken = new HANDLEByReference();
    	HANDLE processHandle = Kernel32.INSTANCE.GetCurrentProcess();
    	assertTrue(Advapi32.INSTANCE.OpenProcessToken(processHandle,
    			WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY, hToken));
    	try {
        	assertFalse(Advapi32.INSTANCE.CreateProcessAsUser(hToken.getValue(), null, "InvalidCmdLine.jna",
        			null, null, false, 0, null, null, new WinBase.STARTUPINFO(),
        			new WinBase.PROCESS_INFORMATION()));
        	assertEquals(W32Errors.ERROR_FILE_NOT_FOUND, Kernel32.INSTANCE.GetLastError());
    	} finally {
    	    Kernel32Util.closeHandleRef(hToken);
    	}
    }

    /**
     * Tests both {@link Advapi32#LookupPrivilegeValue} and {@link Advapi32#LookupPrivilegeName}
     */
    public void testLookupPrivilegeValueAndLookupPrivilegeName() {
    	WinNT.LUID luid = new WinNT.LUID();

    	assertFalse(Advapi32.INSTANCE.LookupPrivilegeValue(null, "InvalidName", luid));
    	assertEquals(Kernel32.INSTANCE.GetLastError(), W32Errors.ERROR_NO_SUCH_PRIVILEGE);

    	assertTrue(Advapi32.INSTANCE.LookupPrivilegeValue(null, WinNT.SE_BACKUP_NAME, luid));
    	assertTrue(luid.LowPart > 0 || luid.HighPart > 0);

    	char[] lpName = new char[256];
    	IntByReference cchName = new IntByReference(lpName.length);
    	assertTrue(Advapi32.INSTANCE.LookupPrivilegeName(null, luid, lpName, cchName));
    	assertEquals(WinNT.SE_BACKUP_NAME.length(), cchName.getValue());
    	assertEquals(WinNT.SE_BACKUP_NAME, Native.toString(lpName));
    }

    public void testAdjustTokenPrivileges() {
    	HANDLEByReference hToken = new HANDLEByReference();
    	assertTrue(Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(),
    			WinNT.TOKEN_ADJUST_PRIVILEGES | WinNT.TOKEN_QUERY, hToken));

    	try {
        	// Find an already enabled privilege
        	TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES(1024);
        	IntByReference returnLength = new IntByReference();
        	assertTrue(Advapi32.INSTANCE.GetTokenInformation(hToken.getValue(),	WinNT.TOKEN_INFORMATION_CLASS.TokenPrivileges,
        			tp, tp.size(), returnLength));
        	assertTrue(tp.PrivilegeCount.intValue() > 0);

        	WinNT.LUID luid = null;
        	for (int i=0; i<tp.PrivilegeCount.intValue(); i++) {
        		if ((tp.Privileges[i].Attributes.intValue() & WinNT.SE_PRIVILEGE_ENABLED) > 0) {
        			luid = tp.Privileges[i].Luid;
        		}
        	}
        	assertTrue(luid != null);

        	// Re-enable it. That should succeed.
        	tp = new WinNT.TOKEN_PRIVILEGES(1);
        	tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(luid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED));

        	assertTrue(Advapi32.INSTANCE.AdjustTokenPrivileges(hToken.getValue(), false, tp, 0, null, null));
    	} finally {
    	    Kernel32Util.closeHandleRef(hToken);
    	}
    }

    public void testImpersonateSelf() {
    	assertTrue(Advapi32.INSTANCE.ImpersonateSelf(WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityAnonymous));
    	assertTrue(Advapi32.INSTANCE.RevertToSelf());
    }

    public void testGetSetFileSecurityNoSACL() throws Exception {
        int infoType = OWNER_SECURITY_INFORMATION
                       | GROUP_SECURITY_INFORMATION
                       | DACL_SECURITY_INFORMATION;

        int memSize = 64 * 1024;
        Memory memorySecurity = new Memory(memSize);
        IntByReference sizeNeeded = new IntByReference(0);
        // create a temp file
        File file = createTempFile();
        String filePath = file.getAbsolutePath();

        try {

            assertEquals("GetFileSecurity(" + filePath + ")", true,
                         Advapi32.INSTANCE.GetFileSecurity(
                                 filePath,
                                 infoType,
                                 memorySecurity,
                                 memSize,
                                 sizeNeeded));
            assertEquals("SetFileSecurity(" + filePath + ")", true,
                         Advapi32.INSTANCE.SetFileSecurity(
                                 filePath,
                                 infoType,
                                 memorySecurity));
        } finally {
            file.delete();
        }
    }

    public void testGetSetSecurityInfoNoSACL() throws Exception {
        int infoType = OWNER_SECURITY_INFORMATION
                       | GROUP_SECURITY_INFORMATION
                       | DACL_SECURITY_INFORMATION;

        PointerByReference ppsidOwner = new PointerByReference();
        PointerByReference ppsidGroup = new PointerByReference();
        PointerByReference ppDacl = new PointerByReference();
        PointerByReference ppSecurityDescriptor = new PointerByReference();
        // create a temp file
        File file = createTempFile();
        String filePath = file.getAbsolutePath();

        HANDLE hFile = Kernel32.INSTANCE.CreateFile(
                filePath,
                WinNT.GENERIC_ALL | WinNT.WRITE_OWNER | WinNT.WRITE_DAC,
                WinNT.FILE_SHARE_READ,
                new WinBase.SECURITY_ATTRIBUTES(),
                WinNT.OPEN_EXISTING,
                WinNT.FILE_ATTRIBUTE_NORMAL,
                null);
        assertFalse("Failed to create file handle: " + filePath, WinBase.INVALID_HANDLE_VALUE.equals(hFile));

        try {
            try {

                assertEquals("GetSecurityInfo(" + filePath + ")", 0,
                        Advapi32.INSTANCE.GetSecurityInfo(
                                hFile,
                                AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                                infoType,
                                ppsidOwner,
                                ppsidGroup,
                                ppDacl,
                                null,
                                ppSecurityDescriptor));

                assertEquals("SetSecurityInfo(" + filePath + ")", 0,
                        Advapi32.INSTANCE.SetSecurityInfo(
                                hFile,
                                AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                                infoType,
                                ppsidOwner.getValue(),
                                ppsidGroup.getValue(),
                                ppDacl.getValue(),
                                null));
            } finally {
                Kernel32.INSTANCE.CloseHandle(hFile);
                file.delete();
            }
        } finally {
            Kernel32Util.freeLocalMemory(ppSecurityDescriptor.getValue());
        }
    }

    public void testGetSetSecurityInfoForFileWithSACL() throws Exception {
        boolean impersontating = false;

        HANDLEByReference phToken = new HANDLEByReference();
        HANDLEByReference phTokenDuplicate = new HANDLEByReference();
        try {
            // open thread or process token, elevate
            if (!Advapi32.INSTANCE.OpenThreadToken(
                 Kernel32.INSTANCE.GetCurrentThread(),
                 TOKEN_ADJUST_PRIVILEGES,
                 false,
                 phToken))
            {
                assertEquals(W32Errors.ERROR_NO_TOKEN, Kernel32.INSTANCE.GetLastError());
                // OpenThreadToken may fail with W32Errors.ERROR_NO_TOKEN if current thread is anonymous.  When this happens,
                // we need to open the process token to duplicate it, then set our thread token.
                assertTrue(Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(), TOKEN_DUPLICATE, phToken));
                // Process token opened, now duplicate
                assertTrue(Advapi32.INSTANCE.DuplicateTokenEx(
                            phToken.getValue(),
                            TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE,
                            null,
                            SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                            TOKEN_TYPE.TokenImpersonation,
                            phTokenDuplicate));
                // And set thread token.
                assertTrue(Advapi32.INSTANCE.SetThreadToken(null, phTokenDuplicate.getValue()));
                impersontating = true;
            }

            // Which token to adjust depends on whether we had to impersonate or not.
            HANDLE tokenAdjust = impersontating ? phTokenDuplicate.getValue() : phToken.getValue();

            WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(1);
            WinNT.LUID pLuid = new WinNT.LUID();

            assertTrue(Advapi32.INSTANCE.LookupPrivilegeValue(null, SE_SECURITY_NAME, pLuid));
            tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED));
            assertTrue(Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null));

            assertTrue(Advapi32.INSTANCE.LookupPrivilegeValue(null, SE_RESTORE_NAME, pLuid));
            tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED));
            assertTrue(Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null));

            // create a temp file
            File file = createTempFile();
            int infoType = OWNER_SECURITY_INFORMATION
                           | GROUP_SECURITY_INFORMATION
                           | DACL_SECURITY_INFORMATION
                           | SACL_SECURITY_INFORMATION;

            PointerByReference ppsidOwner = new PointerByReference();
            PointerByReference ppsidGroup = new PointerByReference();
            PointerByReference ppDacl = new PointerByReference();
            PointerByReference ppSacl = new PointerByReference();
            PointerByReference ppSecurityDescriptor = new PointerByReference();
            String filePath = file.getAbsolutePath();
            HANDLE hFile = WinBase.INVALID_HANDLE_VALUE;

            try {
                try {
                    hFile = Kernel32.INSTANCE.CreateFile(
                                filePath,
                                WinNT.ACCESS_SYSTEM_SECURITY | WinNT.GENERIC_WRITE | WinNT.WRITE_OWNER | WinNT.WRITE_DAC,
                                WinNT.FILE_SHARE_READ,
                                new WinBase.SECURITY_ATTRIBUTES(),
                                WinNT.OPEN_EXISTING,
                                WinNT.FILE_ATTRIBUTE_NORMAL,
                                null);
                    assertEquals("GetSecurityInfo(" + filePath + ")", 0,
                            Advapi32.INSTANCE.GetSecurityInfo(
                                    hFile,
                                    AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                                    infoType,
                                    ppsidOwner,
                                    ppsidGroup,
                                    ppDacl,
                                    ppSacl,
                                    ppSecurityDescriptor));
                    assertEquals("SetSecurityInfo(" + filePath + ")", 0,
                            Advapi32.INSTANCE.SetSecurityInfo(
                                    hFile,
                                    AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                                    infoType,
                                    ppsidOwner.getValue(),
                                    ppsidGroup.getValue(),
                                    ppDacl.getValue(),
                                    ppSacl.getValue()));
                } finally {
                    if (hFile != WinBase.INVALID_HANDLE_VALUE)
                        Kernel32.INSTANCE.CloseHandle(hFile);
                    file.delete();
                }
            } finally {
                Kernel32Util.freeLocalMemory(ppSecurityDescriptor.getValue());
            }

            if (impersontating) {
                assertTrue("SetThreadToken", Advapi32.INSTANCE.SetThreadToken(null, null));
            }
            else {
                tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(0));
                assertTrue("AdjustTokenPrivileges", Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null));
            }
        } finally {
            Kernel32Util.closeHandleRefs(phToken, phTokenDuplicate);
        }
    }

    public void testGetNamedSecurityInfoForFileNoSACL() throws Exception {
        int infoType = OWNER_SECURITY_INFORMATION
                       | GROUP_SECURITY_INFORMATION
                       | DACL_SECURITY_INFORMATION;

        PointerByReference ppsidOwner = new PointerByReference();
        PointerByReference ppsidGroup = new PointerByReference();
        PointerByReference ppDacl = new PointerByReference();
        PointerByReference ppSecurityDescriptor = new PointerByReference();

        File file = createTempFile();
        try {
            try {
                assertEquals("GetNamedSecurityInfo(" + file + ")", 0,
                        Advapi32.INSTANCE.GetNamedSecurityInfo(
                              file.getAbsolutePath(),
                              AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                              infoType,
                              ppsidOwner,
                              ppsidGroup,
                              ppDacl,
                              null,
                              ppSecurityDescriptor));
            } finally {
                file.delete();
            }
        } finally {
            Kernel32Util.freeLocalMemory(ppSecurityDescriptor.getValue());
        }
    }

    public void testGetNamedSecurityInfoForFileWithSACL() throws Exception {

        boolean impersontating = false;
        WinNT.LUID pLuid = new WinNT.LUID();

        assertTrue(Advapi32.INSTANCE.LookupPrivilegeValue(null, SE_SECURITY_NAME, pLuid));

        HANDLEByReference phToken = new HANDLEByReference();
        HANDLEByReference phTokenDuplicate = new HANDLEByReference();
        try {
            // open thread or process token, elevate
            if (!Advapi32.INSTANCE.OpenThreadToken(
                    Kernel32.INSTANCE.GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, false, phToken))
    		{
                assertEquals(W32Errors.ERROR_NO_TOKEN, Kernel32.INSTANCE.GetLastError());
                // OpenThreadToken may fail with W32Errors.ERROR_NO_TOKEN if current thread is anonymous.  When this happens,
                // we need to open the process token to duplicate it, then set our thread token.
                assertTrue(Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(), TOKEN_DUPLICATE, phToken));
                // Process token opened, now duplicate
    			assertTrue(Advapi32.INSTANCE.DuplicateTokenEx(phToken.getValue(),
                            TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE,
                            null,
                            SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                            TOKEN_TYPE.TokenImpersonation,
                            phTokenDuplicate));
                // And set thread token.
                assertTrue(Advapi32.INSTANCE.SetThreadToken(null, phTokenDuplicate.getValue()));
                impersontating = true;
    		}

            // Which token to adjust depends on whether we had to impersonate or not.
            HANDLE tokenAdjust = impersontating ? phTokenDuplicate.getValue() : phToken.getValue();

            WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(1);
            tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED));
            assertTrue(Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null));

            int infoType = OWNER_SECURITY_INFORMATION
                           | GROUP_SECURITY_INFORMATION
                           | DACL_SECURITY_INFORMATION
                           | SACL_SECURITY_INFORMATION;

            PointerByReference ppsidOwner = new PointerByReference();
            PointerByReference ppsidGroup = new PointerByReference();
            PointerByReference ppDacl = new PointerByReference();
            PointerByReference ppSacl = new PointerByReference();
            PointerByReference ppSecurityDescriptor = new PointerByReference();

            File file = createTempFile();
            String filePath = file.getAbsolutePath();
            try {
                try {
                    assertEquals("GetNamedSecurityInfo(" + filePath + ")", 0,
                                 Advapi32.INSTANCE.GetNamedSecurityInfo(
                                         filePath,
                                         AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                                         infoType,
                                         ppsidOwner,
                                         ppsidGroup,
                                         ppDacl,
                                         ppSacl,
                                         ppSecurityDescriptor));
                } finally {
                    file.delete();
                }
            } finally {
                Kernel32Util.freeLocalMemory(ppSecurityDescriptor.getValue());
            }
            if (impersontating) {
            	Advapi32.INSTANCE.SetThreadToken(null, null);
            }
            else {
            	tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(0));
            	Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null);
            }
        } finally {
            Kernel32Util.closeHandleRefs(phToken, phTokenDuplicate);
        }
    }

    public void testSetNamedSecurityInfoForFileNoSACL() throws Exception {
        int infoType = OWNER_SECURITY_INFORMATION
                       | GROUP_SECURITY_INFORMATION
                       | DACL_SECURITY_INFORMATION;

        PointerByReference ppsidOwner = new PointerByReference();
        PointerByReference ppsidGroup = new PointerByReference();
        PointerByReference ppDacl = new PointerByReference();
        PointerByReference ppSecurityDescriptor = new PointerByReference();
        // create a temp file
        File file = createTempFile();
        String filePath = file.getAbsolutePath();
        try {
            try {
                assertEquals("GetNamedSecurityInfo(" + filePath + ")", 0,
                             Advapi32.INSTANCE.GetNamedSecurityInfo(
                                     filePath,
                                     AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                                     infoType,
                                     ppsidOwner,
                                     ppsidGroup,
                                     ppDacl,
                                     null,
                                     ppSecurityDescriptor));
                assertEquals("SetNamedSecurityInfo(" + filePath + ")", 0,
                             Advapi32.INSTANCE.SetNamedSecurityInfo(
                                     filePath,
                                     AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                                     infoType,
                                     ppsidOwner.getValue(),
                                     ppsidGroup.getValue(),
                                     ppDacl.getValue(),
                                     null));
            } finally {
                file.delete();
            }
        } finally {
            Kernel32Util.freeLocalMemory(ppSecurityDescriptor.getValue());
        }
    }

    public void testSetNamedSecurityInfoForFileWithSACL() throws Exception {
        boolean impersontating = false;

        HANDLEByReference phToken = new HANDLEByReference();
        HANDLEByReference phTokenDuplicate = new HANDLEByReference();
        try {
            // open thread or process token, elevate
            if (!Advapi32.INSTANCE.OpenThreadToken(
                 Kernel32.INSTANCE.GetCurrentThread(),
                 TOKEN_ADJUST_PRIVILEGES,
                 false,
                 phToken))
            {
                assertEquals(W32Errors.ERROR_NO_TOKEN, Kernel32.INSTANCE.GetLastError());
                // OpenThreadToken may fail with W32Errors.ERROR_NO_TOKEN if current thread is anonymous.  When this happens,
                // we need to open the process token to duplicate it, then set our thread token.
                assertTrue(Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(), TOKEN_DUPLICATE, phToken));
                // Process token opened, now duplicate
                assertTrue(Advapi32.INSTANCE.DuplicateTokenEx(
                            phToken.getValue(),
                            TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE,
                            null,
                            SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                            TOKEN_TYPE.TokenImpersonation,
                            phTokenDuplicate));
                // And set thread token.
                assertTrue(Advapi32.INSTANCE.SetThreadToken(null, phTokenDuplicate.getValue()));
                impersontating = true;
            }

            // Which token to adjust depends on whether we had to impersonate or not.
            HANDLE tokenAdjust = impersontating ? phTokenDuplicate.getValue() : phToken.getValue();

            WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(1);
            WinNT.LUID pLuid = new WinNT.LUID();

            assertTrue(Advapi32.INSTANCE.LookupPrivilegeValue(null, SE_SECURITY_NAME, pLuid));
            tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED));
            assertTrue(Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null));

            assertTrue(Advapi32.INSTANCE.LookupPrivilegeValue(null, SE_RESTORE_NAME, pLuid));
            tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED));
            assertTrue(Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null));

        	// create a temp file
            File file = createTempFile();
            int infoType = OWNER_SECURITY_INFORMATION
                           | GROUP_SECURITY_INFORMATION
                           | DACL_SECURITY_INFORMATION
                           | SACL_SECURITY_INFORMATION;

            PointerByReference ppsidOwner = new PointerByReference();
            PointerByReference ppsidGroup = new PointerByReference();
            PointerByReference ppDacl = new PointerByReference();
            PointerByReference ppSacl = new PointerByReference();
            PointerByReference ppSecurityDescriptor = new PointerByReference();
            String filePath = file.getAbsolutePath();
            try {
                try {
                    assertEquals("GetNamedSecurityInfo(" + filePath + ")", 0,
                            Advapi32.INSTANCE.GetNamedSecurityInfo(
                                  filePath,
                                  AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                                  infoType,
                                  ppsidOwner,
                                  ppsidGroup,
                                  ppDacl,
                                  ppSacl,
                                  ppSecurityDescriptor));

                    // Send the DACL as a SACL
                    assertEquals("SetNamedSecurityInfo(" + filePath + ")", 0,
                            Advapi32.INSTANCE.SetNamedSecurityInfo(
                                  filePath,
                                  AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                                  infoType,
                                  ppsidOwner.getValue(),
                                  ppsidGroup.getValue(),
                                  ppDacl.getValue(),
                                  ppDacl.getValue()));
                } finally {
                    file.delete();
                }
            } finally {
                Kernel32Util.freeLocalMemory(ppSecurityDescriptor.getValue());
            }

            if (impersontating) {
                assertTrue("SetThreadToken", Advapi32.INSTANCE.SetThreadToken(null, null));
            }
            else {
                tp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(pLuid, new DWORD(0));
                assertTrue("AdjustTokenPrivileges", Advapi32.INSTANCE.AdjustTokenPrivileges(tokenAdjust, false, tp, 0, null, null));
            }
        } finally {
            Kernel32Util.closeHandleRefs(phToken, phTokenDuplicate);
        }
    }

    public void testGetSecurityDescriptorLength() throws Exception {
        int infoType = OWNER_SECURITY_INFORMATION
                       | GROUP_SECURITY_INFORMATION
                       | DACL_SECURITY_INFORMATION;

        PointerByReference ppSecurityDescriptor = new PointerByReference();

        File file = createTempFile();
        try {
            try {
                assertEquals("GetNamedSecurityInfo(" + file + ")", 0,
                        Advapi32.INSTANCE.GetNamedSecurityInfo(
                              file.getAbsolutePath(),
                              AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                              infoType,
                              null,
                              null,
                              null,
                              null,
                              ppSecurityDescriptor));

                assertTrue("GetSecurityDescriptorLength(" + file + ")",
                        Advapi32.INSTANCE.GetSecurityDescriptorLength(ppSecurityDescriptor.getValue()) > 0);
            } finally {
                file.delete();
            }
        } finally {
            Kernel32Util.freeLocalMemory(ppSecurityDescriptor.getValue());
        }
    }

    public void testIsValidSecurityDescriptor() throws Exception {
        int infoType = OWNER_SECURITY_INFORMATION
                       | GROUP_SECURITY_INFORMATION
                       | DACL_SECURITY_INFORMATION;

        PointerByReference ppSecurityDescriptor = new PointerByReference();

        File file = createTempFile();
        try {
            try {
                assertEquals("GetNamedSecurityInfo(" + file + ")",
                        Advapi32.INSTANCE.GetNamedSecurityInfo(
                              file.getAbsolutePath(),
                              AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                              infoType,
                              null,
                              null,
                              null,
                              null,
                              ppSecurityDescriptor), 0);

                assertTrue("IsValidSecurityDescriptor(" + file + ")",
                        Advapi32.INSTANCE.IsValidSecurityDescriptor(ppSecurityDescriptor.getValue()));
            } finally {
                file.delete();
            }
        } finally {
            Kernel32Util.freeLocalMemory(ppSecurityDescriptor.getValue());
        }
    }

    public void testMakeSelfRelativeSD() {
        SECURITY_DESCRIPTOR absolute = new SECURITY_DESCRIPTOR(64 * 1024);
        assertTrue(Advapi32.INSTANCE.InitializeSecurityDescriptor(absolute, WinNT.SECURITY_DESCRIPTOR_REVISION));
        SECURITY_DESCRIPTOR_RELATIVE relative = new SECURITY_DESCRIPTOR_RELATIVE(64 * 1024);
        IntByReference lpdwBufferLength = new IntByReference(64 * 1024);
        assertTrue(Advapi32.INSTANCE.MakeSelfRelativeSD(absolute, relative, lpdwBufferLength));
        assertEquals(WinNT.SECURITY_DESCRIPTOR_REVISION, relative.Revision);
    }

    public void testMakeAbsoluteSD() throws Exception {
        SECURITY_DESCRIPTOR absolute = new SECURITY_DESCRIPTOR(64 * 1024);

        // Get a SD in self relative form
        int infoType = OWNER_SECURITY_INFORMATION
                | GROUP_SECURITY_INFORMATION
                | DACL_SECURITY_INFORMATION;

         PointerByReference relativeByReference = new PointerByReference();
         File file = createTempFile();
         try {
             try {
                 assertEquals("GetNamedSecurityInfo(" + file + ")",
                         Advapi32.INSTANCE.GetNamedSecurityInfo(
                               file.getAbsolutePath(),
                               AccCtrl.SE_OBJECT_TYPE.SE_FILE_OBJECT,
                               infoType,
                               null,
                               null,
                               null,
                               null,
                               relativeByReference), 0);

                 SECURITY_DESCRIPTOR_RELATIVE relative = new SECURITY_DESCRIPTOR_RELATIVE(relativeByReference.getValue());

                 PSID pOwner = new PSID(WinNT.SECURITY_MAX_SID_SIZE);
                 PSID pGroup = new PSID(WinNT.SECURITY_MAX_SID_SIZE);
                 ACL pDacl = new ACL(ACL.MAX_ACL_SIZE);
                 ACL pSacl = new ACL(ACL.MAX_ACL_SIZE);

                 IntByReference lpdwBufferLength = new IntByReference(absolute.size());
                 IntByReference lpdwDaclSize = new IntByReference(ACL.MAX_ACL_SIZE);
                 IntByReference lpdwSaclSize= new IntByReference(ACL.MAX_ACL_SIZE);
                 IntByReference lpdwOwnerSize= new IntByReference(WinNT.SECURITY_MAX_SID_SIZE);
                 IntByReference lpdwPrimaryGroupSize = new IntByReference(WinNT.SECURITY_MAX_SID_SIZE);

                 assertTrue(Advapi32.INSTANCE.MakeAbsoluteSD(relative, absolute, lpdwBufferLength, pDacl, lpdwDaclSize, pSacl, lpdwSaclSize, pOwner, lpdwOwnerSize, pGroup, lpdwPrimaryGroupSize));
             } finally {
                 file.delete();
             }
         } finally {
             Kernel32Util.freeLocalMemory(relativeByReference.getValue());
         }
    }

    public void testMapGenericReadMask() {
        final GENERIC_MAPPING mapping = new GENERIC_MAPPING();
        mapping.genericRead = new DWORD(FILE_GENERIC_READ);
        mapping.genericWrite = new DWORD(FILE_GENERIC_WRITE);
        mapping.genericExecute = new DWORD(FILE_GENERIC_EXECUTE);
        mapping.genericAll = new DWORD(FILE_ALL_ACCESS);

        final DWORDByReference rights = new DWORDByReference(new DWORD(GENERIC_READ));
        Advapi32.INSTANCE.MapGenericMask(rights, mapping);

        assertEquals(FILE_GENERIC_READ, rights.getValue().intValue());
        assertTrue(GENERIC_READ != (rights.getValue().intValue() & GENERIC_READ));
    }

    public void testMapGenericWriteMask() {
        final GENERIC_MAPPING mapping = new GENERIC_MAPPING();
        mapping.genericRead = new DWORD(FILE_GENERIC_READ);
        mapping.genericWrite = new DWORD(FILE_GENERIC_WRITE);
        mapping.genericExecute = new DWORD(FILE_GENERIC_EXECUTE);
        mapping.genericAll = new DWORD(FILE_ALL_ACCESS);

        final DWORDByReference rights = new DWORDByReference(new DWORD(GENERIC_WRITE));
        Advapi32.INSTANCE.MapGenericMask(rights, mapping);

        assertEquals(FILE_GENERIC_WRITE, rights.getValue().intValue());
        assertTrue(GENERIC_WRITE != (rights.getValue().intValue() & GENERIC_WRITE));
    }

    public void testMapGenericExecuteMask() {
        final GENERIC_MAPPING mapping = new GENERIC_MAPPING();
        mapping.genericRead = new DWORD(FILE_GENERIC_READ);
        mapping.genericWrite = new DWORD(FILE_GENERIC_WRITE);
        mapping.genericExecute = new DWORD(FILE_GENERIC_EXECUTE);
        mapping.genericAll = new DWORD(FILE_ALL_ACCESS);

        final DWORDByReference rights = new DWORDByReference(new DWORD(GENERIC_EXECUTE));
        Advapi32.INSTANCE.MapGenericMask(rights, mapping);

        assertEquals(FILE_GENERIC_EXECUTE, rights.getValue().intValue());
        assertTrue(GENERIC_EXECUTE != (rights.getValue().intValue() & GENERIC_EXECUTE));
    }

    public void testMapGenericAllMask() {
        final GENERIC_MAPPING mapping = new GENERIC_MAPPING();
        mapping.genericRead = new DWORD(FILE_GENERIC_READ);
        mapping.genericWrite = new DWORD(FILE_GENERIC_WRITE);
        mapping.genericExecute = new DWORD(FILE_GENERIC_EXECUTE);
        mapping.genericAll = new DWORD(FILE_ALL_ACCESS);

        final DWORDByReference rights = new DWORDByReference(new DWORD(GENERIC_ALL));
        Advapi32.INSTANCE.MapGenericMask(rights, mapping);

        assertEquals(FILE_ALL_ACCESS, rights.getValue().intValue());
        assertTrue(GENERIC_ALL != (rights.getValue().intValue() & GENERIC_ALL));
    }

    public void testAccessCheck() {
        final GENERIC_MAPPING mapping = new GENERIC_MAPPING();
        mapping.genericRead = new DWORD(FILE_GENERIC_READ);
        mapping.genericWrite = new DWORD(FILE_GENERIC_WRITE);
        mapping.genericExecute = new DWORD(FILE_GENERIC_EXECUTE);
        mapping.genericAll = new DWORD(FILE_ALL_ACCESS);
        final Memory securityDescriptorMemoryPointer = new Memory(1);

        final PRIVILEGE_SET privileges = new PRIVILEGE_SET(1);
        privileges.PrivilegeCount = new DWORD(0);
        final DWORDByReference privilegeLength = new DWORDByReference(new DWORD(privileges.size()));
        final DWORDByReference grantedAccess = new DWORDByReference();
        final BOOLByReference result = new BOOLByReference();

        final boolean status = Advapi32.INSTANCE.AccessCheck(securityDescriptorMemoryPointer, null, new DWORD(FILE_GENERIC_READ), mapping, privileges, privilegeLength, grantedAccess, result);
        assertFalse(status);
        assertFalse(result.getValue().booleanValue());

        assertEquals(WinError.ERROR_INVALID_HANDLE, Kernel32.INSTANCE.GetLastError());
    }

    public void testEncryptFile() throws Exception {
        // create a temp file
        File file = createTempFile();
        String lpFileName = file.getAbsolutePath();

        // encrypt a read only file
        file.setWritable(false);
        assertFalse(Advapi32.INSTANCE.EncryptFile(lpFileName));
        assertEquals(WinError.ERROR_FILE_READ_ONLY, Kernel32.INSTANCE.GetLastError());

        // encrypt a writable file
        file.setWritable(true);
        assertTrue(Advapi32.INSTANCE.EncryptFile(lpFileName));

        file.delete();
    }

    public void testDecryptFile() throws Exception {
        // create an encrypted file
        File file = createTempFile();
        String lpFileName = file.getAbsolutePath();
        assertTrue(Advapi32.INSTANCE.EncryptFile(lpFileName));

        // decrypt a read only file
        file.setWritable(false);
        assertFalse(Advapi32.INSTANCE.DecryptFile(lpFileName, new DWORD(0)));
        assertEquals(WinError.ERROR_FILE_READ_ONLY, Kernel32.INSTANCE.GetLastError());

        // decrypt
        file.setWritable(true);
        assertTrue(Advapi32.INSTANCE.DecryptFile(lpFileName, new DWORD(0)));

        file.delete();
    }

    public void testFileEncryptionStatus() throws Exception {
        DWORDByReference lpStatus = new DWORDByReference();

        // create a temp file
        File file = createTempFile();
        String lpFileName = file.getAbsolutePath();

        // unencrypted file
        assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(lpFileName, lpStatus));
        assertEquals(FILE_ENCRYPTABLE, lpStatus.getValue().intValue());

        // read only file
        file.setWritable(false);
        assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(lpFileName, lpStatus));
        assertEquals(FILE_READ_ONLY, lpStatus.getValue().intValue());

        // encrypted file
        file.setWritable(true);
        assertTrue(Advapi32.INSTANCE.EncryptFile(lpFileName));
        assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(lpFileName, lpStatus));
        assertEquals(FILE_IS_ENCRYPTED, lpStatus.getValue().intValue());

        file.delete();
    }

    public void testEncryptionDisable() throws Exception {
        DWORDByReference lpStatus = new DWORDByReference();

        // create a temp dir
        String filePath = System.getProperty("java.io.tmpdir") + File.separator +
                System.nanoTime();
        String DirPath = filePath;
        File dir = new File(filePath);
        dir.mkdir();

        // check status
        assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(DirPath, lpStatus));
        assertEquals(FILE_ENCRYPTABLE, lpStatus.getValue().intValue());

        // disable encryption
        assertTrue(Advapi32.INSTANCE.EncryptionDisable(DirPath, true));
        assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(DirPath, lpStatus));
        assertEquals(FILE_DIR_DISALOWED, lpStatus.getValue().intValue());

        // enable encryption
        assertTrue(Advapi32.INSTANCE.EncryptionDisable(DirPath, false));
        assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(DirPath, lpStatus));
        assertEquals(FILE_ENCRYPTABLE, lpStatus.getValue().intValue());

        // clean up
        for (File file : dir.listFiles()) {
            file.delete();
        }
        dir.delete();
    }

    public void testOpenEncryptedFileRaw() throws Exception {
        // create an encrypted file
        File file = createTempFile();
        String lpFileName = file.getAbsolutePath();
        assertTrue(Advapi32.INSTANCE.EncryptFile(lpFileName));

        // open file for export
        ULONG ulFlags = new ULONG(0);
        PointerByReference pvContext = new PointerByReference();
        assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.OpenEncryptedFileRaw(
                lpFileName, ulFlags, pvContext));

        Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext.getValue());
        file.delete();
    }

    public void testReadEncryptedFileRaw() throws Exception {
        // create an encrypted file
        File file = createTempFile();
        String lpFileName = file.getAbsolutePath();
        assertTrue("EncryptFile(" + lpFileName + ")", Advapi32.INSTANCE.EncryptFile(lpFileName));

        // open file for export
        ULONG ulFlags = new ULONG(0);
        PointerByReference pvContext = new PointerByReference();
        assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.OpenEncryptedFileRaw(
                lpFileName, ulFlags, pvContext));

        // read encrypted file
        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        FE_EXPORT_FUNC pfExportCallback = new FE_EXPORT_FUNC() {
            @Override
            public DWORD callback(Pointer pbData, Pointer
                    pvCallbackContext, ULONG ulLength) {
                if (pbData == null) {
                    throw new NullPointerException("Callback data unexpectedly missing");
                }
                byte[] arr = pbData.getByteArray(0, ulLength.intValue());
                try {
                    outputStream.write(arr);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                return new DWORD(W32Errors.ERROR_SUCCESS);
            }
        };

        assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.ReadEncryptedFileRaw(
                pfExportCallback, null, pvContext.getValue()));
        outputStream.close();

        Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext.getValue());
        file.delete();
    }

    public void testWriteEncryptedFileRaw() throws Exception {
        // create an encrypted file
        File file = createTempFile();
        String lpFileName = file.getAbsolutePath();
        assertTrue(Advapi32.INSTANCE.EncryptFile(lpFileName));

        // open file for export
        ULONG ulFlags = new ULONG(0);
        PointerByReference pvContext = new PointerByReference();
        assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.OpenEncryptedFileRaw(
                lpFileName, ulFlags, pvContext));

        // read encrypted file
        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        FE_EXPORT_FUNC pfExportCallback = new FE_EXPORT_FUNC() {
            @Override
            public DWORD callback(Pointer pbData, Pointer
                    pvCallbackContext, ULONG ulLength) {
                if (pbData == null) {
                    throw new NullPointerException("Callback data unexpectedly null");
                }
                byte[] arr = pbData.getByteArray(0, ulLength.intValue());
                try {
                    outputStream.write(arr);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                return new DWORD(W32Errors.ERROR_SUCCESS);
            }
        };

        assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.ReadEncryptedFileRaw(
                pfExportCallback, null, pvContext.getValue()));
        outputStream.close();
        Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext.getValue());

        // open file for import
        String lbFileName2 = System.getProperty("java.io.tmpdir") +
                File.separator + "backup-" + file.getName();
        ULONG ulFlags2 = new ULONG(CREATE_FOR_IMPORT);
        PointerByReference pvContext2 = new PointerByReference();
        assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.OpenEncryptedFileRaw(
                lbFileName2, ulFlags2, pvContext2));

        // write encrypted file
        final IntByReference elementsReadWrapper = new IntByReference(0);
        FE_IMPORT_FUNC pfImportCallback = new FE_IMPORT_FUNC() {
            @Override
            public DWORD callback(Pointer pbData, Pointer pvCallbackContext,
                                  ULONGByReference ulLength) {
                int elementsRead = elementsReadWrapper.getValue();
                int remainingElements = outputStream.size() - elementsRead;
                int length = Math.min(remainingElements, ulLength.getValue().intValue());
                pbData.write(0, outputStream.toByteArray(), elementsRead, length);
                elementsReadWrapper.setValue(elementsRead + length);
                ulLength.setValue(new ULONG(length));
                return new DWORD(W32Errors.ERROR_SUCCESS);
            }
        };

        assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.WriteEncryptedFileRaw(
                pfImportCallback, null, pvContext2.getValue()));
        Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext2.getValue());

        file.delete();
        new File(lbFileName2.toString()).delete();
    }

    private File createTempFile() throws Exception {
        String filePath = System.getProperty("java.io.tmpdir") + System.nanoTime()
                + ".text";
        File file = new File(filePath);
        file.createNewFile();
        FileWriter fileWriter = new FileWriter(file);
        for (int i = 0; i < 1000; i++) {
            fileWriter.write("Sample text " + i + System.getProperty("line.separator"));
        }
        fileWriter.close();
        return file;
    }

    public void testCreateProcessWithLogonW() {
    	String winDir = Kernel32Util.getEnvironmentVariable("WINDIR");
    	assertNotNull("No WINDIR value returned", winDir);
    	assertTrue("Specified WINDIR does not exist: " + winDir, new File(winDir).exists());

    	STARTUPINFO si = new STARTUPINFO();
    	si.lpDesktop = null;
    	PROCESS_INFORMATION results = new PROCESS_INFORMATION();

    	// i have the same combination on my luggage
    	boolean result = Advapi32.INSTANCE.CreateProcessWithLogonW("A" + System.currentTimeMillis(), "localhost", "12345", Advapi32.LOGON_WITH_PROFILE, new File(winDir, "notepad.exe").getAbsolutePath(), "", 0, null, "", si, results);

    	// we tried to run notepad as a bogus user, so it should fail.
    	assertFalse("CreateProcessWithLogonW should have returned false because the username was bogus.", result);

    	// should fail with "the user name or password is incorrect" (error 1326)
    	assertEquals("GetLastError() should have returned ERROR_LOGON_FAILURE because the username was bogus.", W32Errors.ERROR_LOGON_FAILURE, Native.getLastError());
    }
}
