Starting with Vista, Windows employs many components who expose their data via IPropertyStores. shacct.dll contains the implementation of the property stores dealing with users and their profiles. These interfaces can only be retrived via CoCreateInstance, and not via any exported functions.
CLSID's
Interfaces
Each of the clsid's above implement only the IComputerAccounts interface. The ancillary interfaces are documented further down the page
MIDL_INTERFACE("3C708557-C99D-4FA3-9231-56518418B4E4") IComputerAccounts : public IUnknown { // 0xc - retrives an interface to enumerate the members // of the user/group collection specified by the CLSID above STDMETHOD(GetEnumerator)(IEnumAccounts** pEnumerator) = 0; // 0x10 - number of property stores/entries in this collection STDMETHOD(GetCount)(ULONG* pCount) = 0; // 0x14 - Get the user/profile property store at index STDMETHOD(GetAt)(ULONG index, IPropertyStore** ppPropStore) = 0; // 0x18 - resets the object state to default STDMETHOD(Reset)(void) = 0; // 0x1c - Create a new permanent user and return its propstore // E_NOTIMPL for logged on users STDMETHOD(Create)(PCWSTR pName, IPropertyStore** ppPropStore) = 0; // 0x20 - Deletes a permanent user using the RID in the prop store STDMETHOD(Delete)(IPropertyStore* pPropStore) = 0; // 0x24 - Retrieve a property store using a user or groups display name // E_NOTIMPL for CLSID_ProfileAccounts, must use FindByRid STDMETHOD(FindByName)(PCWSTR pUserName, IPropertyStore** ppPropStore) = 0; // 0x28 - Retrieve a prop store using the user or account rid // The rid is the last subauthority in a SID as in // rid = *GetSidSubAuthority((*GetSidSubAuthorityCount(pAccountSid)) - 1) STDMETHOD(FindByRid)(ULONG rid, IPropertyStore** ppPropStore) = 0; // 0x2c - interface is IComputerAccountNotify // The passed interface is added to the IGlobalInterfaceTable // Requires Administrator or you'll get access denied (0xD0000022) STDMETHOD(Advise)(IComputerAccountNotify* pCompAcctNotify, DWORD* pToken) = 0; // 0x30 - stop receiving account events STDMETHOD(Unadvise)(DWORD token) = 0; };
Property Keys
All property keys except one contain the same GUID as the key, using only the pid of the PROPERTYKEY structure to differentiate. Not all property stores returned by the various CLSID implementations can query all the keys, some of these are noted in the table.
The prefix guid for all prop keys is GUID_AccPropPrefix = {0x705D8364, 0x7547, 0x468c, {0x8c, 0x88, 0x84, 0x86, 0x0b, 0xcb, 0xed, 0x4c}}
General PROPERTYKEYs Name & Definition Variant Type/Notes PKEY_SAM_Name = {GUID_AccPropPrefix, 2} LPWSTR PKEY_SAM_PasswordLastSet = {GUID_AccPropPrefix, 5} FILETIME PKEY_SAM_DateAccountExpires = {GUID_AccPropPrefix, 6} DATE PKEY_SAM_PasswordCanChange = {GUID_AccPropPrefix, 7} FILETIME PKEY_SAM_PasswordMustChange = {GUID_AccPropPrefix, 8} DATE PKEY_SAM_FullName = {GUID_AccPropPrefix, 9} LPWSTR PKEY_SAM_HomeDirectory = {GUID_AccPropPrefix, 10} LPWSTR PKEY_SAM_HomeDirectoryDrive = {GUID_AccPropPrefix, 11} LPWSTR PKEY_SAM_ScriptPath = {GUID_AccPropPrefix, 12} LPWSTR PKEY_SAM_ProfilePath = {GUID_AccPropPrefix, 13} LPWSTR PKEY_SAM_AdminComment = {GUID_AccPropPrefix, 14} LPWSTR PKEY_SAM_Workstations = {GUID_AccPropPrefix, 15} Empty for no restriction or VT_LPWSTR | VT_VECTOR otherwise
(note the last entry in the array isn't null terminated on Win7, fixed in 8)PKEY_SAM_UserComment = {GUID_AccPropPrefix, 16} LPWSTR PKEY_SAM_Password = {GUID_AccPropPrefix, 17} LPWSTR - always an empty string PKEY_SAM_SecurityID = {GUID_AccPropPrefix, 18} BLOB (PSID) PKEY_SAM_UserAccountControl = {GUID_AccPropPrefix, 19} UI4 - LocalUserAccounts only. See here for flag definitions PKEY_SAM_LogonHours = {GUID_AccPropPrefix, 20} VT_VECTOR | VT_UI1 - See here for a description of the format PKEY_SAM_CountryCode = {GUID_AccPropPrefix, 21} VT_UI2 PKEY_SAM_CodePage = {GUID_AccPropPrefix, 22} VT_UI2 PKEY_SAM_PasswordExpired = {GUID_AccPropPrefix, 23} BOOL PKEY_SAM_UserPicture = {GUID_AccPropPrefix, 24} BLOB - See the bottom of SHSetUserPicturePath for the data format PKEY_SAM_PasswordHint = {GUID_AccPropPrefix, 25} LPWSTR PKEY_SAM_Domain = {GUID_AccPropPrefix, 25} LPWSTR PKEY_SAM_Groups = {GUID_AccPropPrefix, 31} LPWSTR - Always returns the string "Need to implement groups" PKEY_SAM_Type = {GUID_AccPropPrefix, 32} UI4 PKEY_SAM_InteractiveLogin = {GUID_AccPropPrefix, 36} BOOL - This and the rest of the *Login values
test whether the user has the associated SE_*_LOGON_NAME rightPKEY_SAM_NetworkLogin = {GUID_AccPropPrefix, 37} BOOL PKEY_SAM_BatchLogin = {GUID_AccPropPrefix, 38} BOOL PKEY_SAM_ServiceLogin = {GUID_AccPropPrefix, 39} BOOL PKEY_SAM_RemoteInteractiveLogin = {GUID_AccPropPrefix, 40} BOOL PKEY_SAM_DenyInteractiveLogin = {GUID_AccPropPrefix, 41} BOOL PKEY_SAM_DenyNetworkLogin = {GUID_AccPropPrefix, 42} BOOL PKEY_SAM_DenyBatchLogin = {GUID_AccPropPrefix, 43} BOOL PKEY_SAM_DenyServiceLogin = {GUID_AccPropPrefix, 44} BOOL PKEY_SAM_DenyRemoteInteractiveLogin = {GUID_AccPropPrefix, 45} BOOL PKEY_SAM_DontShowInLogonUI = {GUID_AccPropPrefix, 46} BOOL PKEY_SAM_ShellAdminObjectProps = {GUID_AccPropPrefix, 47} ? PKEY_SAM_PasswordIsEmpty = {GUID_AccPropPrefix, 50} BOOL - only returns correct results when caller is an admin PKEY_SAM_GroupMembers = {GUID_AccPropPrefix, 102} for groups only - VT_VECTOR | VT_VARIANT
(variants are VT_UNKNOWN, IPropertyStore* for each user in the group)PKEY_SAM_ResidualID = {GUID_AccPropPrefix, 103} UI4 (RID part of SID) PKEY_SAM_AccountIsDisabledForLogonUI =
{{0x8BF6B9F6, 0xb4f5, 0x482f, {0xa2, 0xc2, 0x44, 0xbd, 0xad, 0x2f, 0xcf, 0xa9}}, 51}The nonstandard guid entry
GetValue returns E_INVALIDARG if not set, instead of returning a variant
LoggedOnAccounts Exclusive PROPERTYKEYs Name & Definition Variant Type/Notes PKEY_LOGON_LUID = {GUID_AccPropPrefix, 200} UI8 PKEY_LOGON_AuthenticationPackage = {GUID_AccPropPrefix, 201} LPWSTR - NTLM for example PKEY_LOGON_TSSession = {GUID_AccPropPrefix, 202} UI4 - session id PKEY_LOGON_LogonTime = {GUID_AccPropPrefix, 203} FILETIME PKEY_LOGON_LogonServer = {GUID_AccPropPrefix, 204} LPWSTR - computer name PKEY_LOGON_DnsDomainName = {GUID_AccPropPrefix, 205} LPWSTR PKEY_LOGON_UPN = {GUID_AccPropPrefix, 206} LPWSTR PKEY_LOGON_ClientName = {GUID_AccPropPrefix, 207} LPWSTR - value returned by WTSQuerySession(WTSClientName) PKEY_LOGON_WinStationName = {GUID_AccPropPrefix, 208} LPWSTR PKEY_LOGON_Status = {GUID_AccPropPrefix, 209} UI4 - a value from the WTS_CONNECTSTATE_CLASS enum
ProfileAccounts Exclusive PROPERTYKEYs Name & Definition Variant Type/Notes PKEY_PROFILE_Path = {GUID_AccPropPrefix, 500} LPWSTR - User profile directory PKEY_PROFILE_GUID = {GUID_AccPropPrefix, 501} GUID - profile guid
The property stores that contain these keys also support the documented IPersistSerializedPropStorage interface too.
IEnumAccounts is used to enumerate the contained groups/accounts using the familiar enumerator interface method
MIDL_INTERFACE("50C852B0-C95F-4FEE-be00-87DC18B2661B") IEnumAccounts : public IUnknown { public: // pFetched is required STDMETHOD(Next)(ULONG celt, IPropertyStore** ppStore, ULONG* pFetched) = 0; STDMETHOD(Skip)(ULONG celt) = 0; STDMETHOD(Reset)(void) = 0; STDMETHOD(Clone)(IEnumAccounts** ppEnum) = 0; };
The IComputerAccountNotify allows clients to be notified when a user or group is changed. No indications are given on what has changed as this information is unavailable from the internal notification system (SamRegisterObjectChangeNotification) which just sets an event.
MIDL_INTERFACE("F0009734-E8DE-48E5-B603-BFA5966A8F7C") IComputerAccountNotify : public IUnknown { // called when there is a change (user info changed, member added to a group, etc) STDMETHOD(OnEvent)(void) = 0; // called in IComputerAccounts::Advise() // queries for the delay in milliseconds // between OnEvent calls, can be set to 0 for no delay STDMETHOD(GetMinimumTimeout)(DWORD* pdwTimeout) = 0; };
Other Functionality
Alongside the profile property stores, shacct.dll also houses the default implementation of the IProfileNotify interface. This interface performs various tasks when users are created and deleted and when their profiles are loaded and unloaded.
MIDL_INTERFACE("E10F6C3A-F1ae-4adc-aa9d-2fe65525666e") IProfileNotify : public IUnknown { // Creates the <username>.dat file in the "%ProgramData%\Microsoft\User Account Pictures" directory STDMETHOD(OnCreate)(PCWSTR pStringSid, PCWSTR unused1, ULONG unused2) = 0; // Deletes the <username>.dat file in the "%ProgramData%\Microsoft\User Account Pictures" directory // and removes the folder aces created by OnLoad() STDMETHOD(OnDelete)(PCWSTR pStringSid, PCWSTR unused1, ULONG unused2) = 0; // E_NOTIMPL - aliased to OnUnload in the disassembly because they both return the same STDMETHOD(OnXXX)(PCWSTR, PCWSTR, ULONG, int) = 0; // E_NOTIMPL STDMETHOD(OnUpgrade)(PCWSTR, PCWSTR, ULONG, int) = 0; // adds an access allowed ace to the FOLDERID_PublicDesktop // folder and FOLDERID_CommonStartMenu // If pStringSid is an Administrator sid // an additional FILE_DELETE_CHILD | DELETE ace is added to both folders STDMETHOD(OnLoad)(PCWSTR pStringSid, PCWSTR unused1, ULONG unused2, PSID pSid) = 0; // E_NOTIMPL STDMETHOD(OnUnload)(PCWSTR, PCWSTR, ULONG, int) = 0; };
// frmo NTSam.h in the WDK #define USER_ACCOUNT_DISABLED (0x00000001) #define USER_HOME_DIRECTORY_REQUIRED (0x00000002) #define USER_PASSWORD_NOT_REQUIRED (0x00000004) #define USER_TEMP_DUPLICATE_ACCOUNT (0x00000008) #define USER_NORMAL_ACCOUNT (0x00000010) #define USER_MNS_LOGON_ACCOUNT (0x00000020) #define USER_INTERDOMAIN_TRUST_ACCOUNT (0x00000040) #define USER_WORKSTATION_TRUST_ACCOUNT (0x00000080) #define USER_SERVER_TRUST_ACCOUNT (0x00000100) #define USER_DONT_EXPIRE_PASSWORD (0x00000200) #define USER_ACCOUNT_AUTO_LOCKED (0x00000400) #define USER_ENCRYPTED_TEXT_PASSWORD_ALLOWED (0x00000800) #define USER_SMARTCARD_REQUIRED (0x00001000) #define USER_TRUSTED_FOR_DELEGATION (0x00002000) #define USER_NOT_DELEGATED (0x00004000) #define USER_USE_DES_KEY_ONLY (0x00008000) #define USER_DONT_REQUIRE_PREAUTH (0x00010000) #define USER_PASSWORD_EXPIRED (0x00020000) #define USER_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION (0x00040000) #define USER_NO_AUTH_DATA_REQUIRED (0x00080000) #define USER_PARTIAL_SECRETS_ACCOUNT (0x00100000) #define USER_USE_AES_KEYS (0x00200000)
From ntsam.h
LogonHours is a bit map of valid logon times. Each bit represents a unique division in a week. The largest bit map supported is 1260 bytes (10080 bits), which represents minutes per week.
In this case the first bit (bit 0, byte 0) is Sunday, 00:00:00 - 00-00:59; bit 1, byte 0 is Sunday, 00:01:00 - 00:01:59, etc.