How can I set a none data type.[Microsoft.Win32.RegistryValueKind]::None in Powershell 2.0?
Powershell 2.0 has types "Unknown, String, ExpandString, Binary, DWord, MultiString, QWord".
learn.microsoft.com
Add-Type -TypeDefinition #"
using System.Runtime.InteropServices;
using System.Security.Principal;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
namespace AdvapiSolution
{
public class Advapi
{
public enum HKEY : uint
{
HKEY_CLASSES_ROOT = 0x80000000,
HKEY_CURRENT_USER = 0x80000001,
HKEY_LOCAL_MACHINE = 0x80000002,
HKEY_USERS = 0x80000003,
HKEY_PERFORMANCE_DATA = 0x80000004,
HKEY_PERFORMANCE_TEXT = 0x80000050,
HKEY_PERFORMANCE_NLSTEXT = 0x80000060,
HKEY_CURRENT_CONFIG = 0x80000005
}
private enum VALUE_TYPE : uint
{
REG_NONE= 0,
REG_SZ = 1,
REG_EXPAND_SZ = 2,
REG_BINARY = 3,
REG_DWORD = 4,
REG_DWORD_LITTLE_ENDIAN = 4,
REG_DWORD_BIG_ENDIAN = 5,
REG_LINK = 6,
REG_MULTI_SZ = 7,
REG_RESOURCE_LIST = 8,
REG_FULL_RESOURCE_DESCRIPTOR = 9,
REG_RESOURCE_REQUIREMENTS_LIST = 10,
REG_QWORD_LITTLE_ENDIAN = 11
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto, BestFitMapping = false)]
private static extern int RegSetKeyValueW (
HKEY hkey,
string lpSubKey,
string lpValueName,
VALUE_TYPE type,
byte[] data,
uint dataLength);
public int set_key(HKEY hkey, string subkey, string valuename){
return RegSetKeyValueW(hkey, subkey, valuename, VALUE_TYPE.REG_NONE, null, 0);
}
}
}
"#
In my case data equals null and type is REG_NONE. You can change set_key as you like with object:
$Advapiobject = New-Object 'AdvapiSolution.Advapi'
$Advapiobject.set_key('HKEY_CURRENT_USER',$yoursubkey, $yourvaluename, 'REG_NONE')
Another way is using a static method set_key.
Change set_key to public static int set_key:
[AdvapiSolution.Advapi]::set_key('HKEY_CURRENT_USER',$yoursubkey, $yourvaluename, 'REG_NONE').
If it returns 0, it's okay. If the function fails, the return value is a nonzero error code defined in Winerror.h.
System Error Codes
(As implied in your question), [Microsoft.Win32.RegistryValueKind]::None (which translates to a REG_NONE registry value) requires at least .NET Framework 4, whereas Windows PowerShell v2 is built on .NET Framework 2. (Only Windows PowerShell v3 and above are built on .NET Framework 4 and above).
Unlike C#, PowerShell does not allow you to pass the numerical value of an enum-typed parameter if that value doesn't represent an officially defined member of that enumeration (at that time, in the underlying framework), so attempting to pass (-1), the value of [Microsoft.Win32.RegistryValueKind]::None, does not work in Windows PowerShell v2 - neither with New-ItemProperty / Set-ItemProperty's -Type parameter, nor with .NET API calls ([Microsoft.Win32.Registry]::SetValue(...)).
Therefore, in Windows PowerShell v2, calling the Windows API via P/Invoke declarations implemented via Add-Type is probably your only option - see the RegSetKeyValue() WinAPI function (among others).
Building on your own helpful answer, here's a streamlined solution, which:
uses Add-Type's -MemberDefinition parameter to simplify creation of types that wrap P/Invoke calls.
Exposes a static [WinApiHelper.Registry]::SetNoneValue() method, which:
focuses only on creating REG_NONE values
also supports creating such values with byte data ([byte[]] arrays)
has no return value, but throws a Win32Exception should an error occur.
Add-Type -Namespace WinApiHelper -Name Registry -MemberDefinition #'
public enum HKEY : uint
{
HKEY_CLASSES_ROOT = 0x80000000,
HKEY_CURRENT_CONFIG = 0x80000005,
HKEY_CURRENT_USER = 0x80000001,
HKEY_LOCAL_MACHINE = 0x80000002,
HKEY_USERS = 0x80000003
}
[DllImport("advapi32.dll")]
private static extern int RegSetKeyValue(
HKEY hkey,
string lpSubKey,
string lpValueName,
UInt32 type,
byte[] data,
UInt32 dataLength
);
public static void SetNoneValue(HKEY hkey, string subkey, string valuename, byte[] bytes)
{
int rc = RegSetKeyValue(hkey, subkey, valuename, 0 /* REG_NONE */, bytes, (UInt32) (bytes == null ? 0 : bytes.Length));
if (rc != 0) {
// Access the error code in PowerShell with $Error[0].Exception.InnerException.NativeErrorCode
throw new System.ComponentModel.Win32Exception(rc);
}
}
// Overload that creates an empty value.
// Note: .NET 2 doesn't support optional parameters, so an explicit overload is required.
public static void SetNoneValue(HKEY hkey, string subkey, string valuename) {
SetNoneValue(hkey, subkey, valuename, null);
}
'#
Sample calls:
# Create an empty REG_NONE value named 'bar1' in key HKEY_CURRENT_USER\foo.
[WinApiHelper.Registry]::SetNoneValue('HKEY_CURRENT_USER', 'foo', 'bar1')
# Create a REG_NONE value named 'bar2' with a byte array.
[WinApiHelper.Registry]::SetNoneValue('HKEY_CURRENT_USER', 'foo', 'bar2', [byte[]] (42, 43))
Related
How to call a native function with output parameter with Dart ffi, since dart doesn't support output parameters. i am looking for an alternative for something like this C# Code
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
String PrinterName="Printer1";
IntPtr hPrinter = new IntPtr(0);
OpenPrinter(PrinterName.Normalize(), out hPrinter, IntPtr.Zero)
// Allocate some memory for the output to be written to.
final Pointer<Pointer<duckdb_database>> outDb = calloc<Pointer<duckdb_database>>();
// That memory contains a null pointer (we used calloc to zero the memory).
assert(outDb.value == nullptr);
// The native function should populate that memory.
db.open_ext(outDb);
assert(outDb.value != nullptr);
// Our native API probably wants a `duckdb_database*` in the rest of the API, so lets keep that variable.
final Pointer<duckdb_database> db = outDb.value;
// Don't forget to free the memory that we had for the out parameter.
calloc.free(outDb);
// Use `db`...
The SQLite sample in the Dart SDK repo has the same pattern with out parameters.
I was able to do it by using malloc with zero length from package:ffi
final dbPointer = malloc.allocate<duckdb_database>(0);
print(dbPointer.value); // some initial value
db.open_ext(dbPointer);
print(dbPointer.value); // new value!!
Zero clue if this is "right", but it's working for me!
Here's the PowerShell code at the bottom that I've tried to write but failed to retrieve the cursor shadow state.
I've looked at the help here: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
And it states that:
SPI_GETCURSORSHADOW 0x101A
Determines whether the cursor has a shadow around it. The pvParam parameter must point to a BOOL variable that receives TRUE if the shadow is enabled, FALSE if it is disabled. This effect appears only if the system has a color depth of more than 256 colors.
My code attempt which can be saved as a .ps1 file and tested in PS ISE
$CSharpSig = #'
[DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
public static extern bool SystemParametersInfo(
int uiAction, uint uiParam, uint pvParam, uint fWinIni);
'#
$SPI_GETCURSORSHADOW = 0x101A
$CursorRefresh = Add-Type -MemberDefinition $CSharpSig -Name WinAPICall -Namespace SystemParamInfo -PassThru
# SPI_GETCURSORSHADOW - pvParam 0 or 1 (3rd argument)
$CursorRefresh::SystemParametersInfo($SPI_GETCURSORSHADOW, 0, $BOOLTOGGLE, 0)
write-output $BOOLTOGGLE
This returns False as the state all the time even though that is not the case.
I'm at a loss how to get this even after reading a similar thread which is not directly associated with PowerShell:
Messed with SystemParametersInfo and Booleans pvParam parameter
Edit, new issue:
My full intention for the code was to try toggle the setting for the shadow of the cursor so here's my latest attempt to do so. I've put the comments relating to the problem inside the code.
This code works for my initial problem but not when I add back in the commented code at the bottom.
# More info here: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
# Get the original setting
$CSharpSigGet = #'
[DllImport("user32.dll")]
public static extern bool SystemParametersInfo(
int uiAction, uint uiParam, out bool pvParam, uint fWinIni);
'#
$SPI_GETCURSORSHADOW = 0x101A
$CursorGet = Add-Type -MemberDefinition $CSharpSigGet -Name WinAPICall -Namespace SystemParamInfo -PassThru
[bool] $getBool = $false
$CursorGet::SystemParametersInfo($SPI_GETCURSORSHADOW, 0, [ref] $getBool, 0).value # Stores the boolean.
$cursorShadowBool = ([ref] $getBool).value
write-output $cursorShadowBool
# Toggle the original setting
# This code is now not working as it throws an error when this code below is uncommented..
# Error below:
# TYPE_ALREADY_EXISTS,Microsoft.PowerShell.Commands.AddTypeCommand
# $SPI_SETCURSORSHADOW = 0x101B
# $CSharpSigSet = #'
# [DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
# public static extern bool SystemParametersInfo(
# int uiAction, uint uiParam, uint pvParam, uint fWinIni);
# '#
# $CursorSet = Add-Type -MemberDefinition $CSharpSigSet -Name WinAPICall -Namespace SystemParamInfo -PassThru
# $CursorSet::SystemParametersInfo($SPI_SETCURSORSHADOW, 0, -not ([ref] $getBool).value, 0)
Per the docs:
SPI_GETCURSORSHADOW 0x101A
Determines whether the cursor has a shadow around it. The pvParam
parameter must point to a BOOL variable that receives TRUE if the
shadow is enabled, FALSE if it is disabled. This effect appears only
if the system has a color depth of more than 256 colors.
So your signature is incorrect; the third parameter must point to a BOOL. Assuming you only need to call it for this specific case and we don't need to bother with a generic IntPtr:
$CSharpSig = #'
[DllImport("user32.dll")]
public static extern bool SystemParametersInfo(
int uiAction, uint uiParam, out bool pvParam, uint fWinIni);
'#
[bool] $BOOLTOGGLE = $false
if ($CursorRefresh::SystemParametersInfo($SPI_GETCURSORSHADOW, 0, [ref] $BOOLTOGGLE, 0)) {
write-output $BOOLTOGGLE
}
I'm trying to set the screen resolution via script (Powershell).
I found the cmdlet Set-DisplayResolution, which should work on Server 2012/R2 and 8/8.1 (where it won't work), and it also didn't work on Windows 10 PS 5.0.
I also had a look at
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Screen]::AllScreens
and
[System.Windows.Forms.Screen]::PrimaryScreen
but it showed weird output:
BitsPerPixel : 32
Bounds : {X=0,Y=0,Width=2000,Height=1333}
DeviceName : \\.\DISPLAY1
Primary : True
WorkingArea : {X=0,Y=0,Width=2000,Height=1293}
I am running a 3000x2000 resolution. Even after I changed my display to 1920x1080 the output stays the same.
After a longer search I found this script, which does the job using win32 API. Is there a more handy way for doing this, as this Set-DisplayResolution just running on Windows 10 Installation?
I also would like to set the UI scaling of Windows 10 (like 150%).
And for sure, i would like to know, why the AllScreens and PrimaryScreen methods of [System.Windows.Forms.Screen] aren't functioning properly.
Refer below script to change display resolution on Windows 10. PowerShell server core does not supported on windows 10. You can use below script
Important : you can provide display resolution at end of the script
currently it is set as Set-ScreenResolution -Width 1920 -Height 1080. You can change these values. Copy this complete script and save as Set-ScreenResolution.ps1 and execute using PowerShell. Make sure you Run powershell as admin
Function Set-ScreenResolution {
<#
.Synopsis
Sets the Screen Resolution of the primary monitor
.Description
Uses Pinvoke and ChangeDisplaySettings Win32API to make the change
.Example
Set-ScreenResolution -Width 1024 -Height 768
#>
param (
[Parameter(Mandatory=$true,
Position = 0)]
[int]
$Width,
[Parameter(Mandatory=$true,
Position = 1)]
[int]
$Height
)
$pinvokeCode = #"
using System;
using System.Runtime.InteropServices;
namespace Resolution
{
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE1
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public short dmOrientation;
public short dmPaperSize;
public short dmPaperLength;
public short dmPaperWidth;
public short dmScale;
public short dmCopies;
public short dmDefaultSource;
public short dmPrintQuality;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmFormName;
public short dmLogPixels;
public short dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
};
class User_32
{
[DllImport("user32.dll")]
public static extern int EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE1 devMode);
[DllImport("user32.dll")]
public static extern int ChangeDisplaySettings(ref DEVMODE1 devMode, int flags);
public const int ENUM_CURRENT_SETTINGS = -1;
public const int CDS_UPDATEREGISTRY = 0x01;
public const int CDS_TEST = 0x02;
public const int DISP_CHANGE_SUCCESSFUL = 0;
public const int DISP_CHANGE_RESTART = 1;
public const int DISP_CHANGE_FAILED = -1;
}
public class PrmaryScreenResolution
{
static public string ChangeResolution(int width, int height)
{
DEVMODE1 dm = GetDevMode1();
if (0 != User_32.EnumDisplaySettings(null, User_32.ENUM_CURRENT_SETTINGS, ref dm))
{
dm.dmPelsWidth = width;
dm.dmPelsHeight = height;
int iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_TEST);
if (iRet == User_32.DISP_CHANGE_FAILED)
{
return "Unable To Process Your Request. Sorry For This Inconvenience.";
}
else
{
iRet = User_32.ChangeDisplaySettings(ref dm, User_32.CDS_UPDATEREGISTRY);
switch (iRet)
{
case User_32.DISP_CHANGE_SUCCESSFUL:
{
return "Success";
}
case User_32.DISP_CHANGE_RESTART:
{
return "You Need To Reboot For The Change To Happen.\n If You Feel Any Problem After Rebooting Your Machine\nThen Try To Change Resolution In Safe Mode.";
}
default:
{
return "Failed To Change The Resolution";
}
}
}
}
else
{
return "Failed To Change The Resolution.";
}
}
private static DEVMODE1 GetDevMode1()
{
DEVMODE1 dm = new DEVMODE1();
dm.dmDeviceName = new String(new char[32]);
dm.dmFormName = new String(new char[32]);
dm.dmSize = (short)Marshal.SizeOf(dm);
return dm;
}
}
}
"#
Add-Type $pinvokeCode -ErrorAction SilentlyContinue
[Resolution.PrmaryScreenResolution]::ChangeResolution($width,$height)
}
Set-ScreenResolution -Width 1920 -Height 1080
Sorry to tell, but it's not possible with powershell:
[System.Windows.Forms.Screen] - the namespace says all you need to understand: This namespace refers to the display area for the application you've coded as an windows form (Usefull, if you have to change the windows-size within your application)
The Cmdlet Set-Displayresolution only works on Windows Server 2016 Core (or any other Core-Server in the future). It is documented on Technet : https://technet.microsoft.com/de-de/library/jj603036(v=wps.630).aspx
The workaround to use Win32-API, that you found earlier, looks like the best solution you could get.
A bit late, but depending on how you need the resolution set you could use AHK (scripting language). This would require a script opening the display settings dialog and sending clicks or keystrokes to it. If you're interested I can give more information
Update:
First, you have to have AutoHotKey installed on your computer (this is straightforward enough to just Google). Next, use a script to send keyboard input to the control panel display settings. It might look something like this:
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn ; Enable warnings to assist with detecting common errors.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#SingleInstance, force
detecthiddenwindows, on
; Use command prompt to open display settings
Run, %comspec% \k,,hide, cmd_pid
WinWait, ahk_pid %cmd_pid%
ControlSend, , control desk.cpl`n, ahk_pid %cmd_pid%
; Wait for display settings to open
WinWaitActive, Settings
sleep, 2000
; Send the appropriate key strokes to the settings menu to change the resolution
Send,{Tab 3}
sleep 100
Send,{Enter}
sleep 100
; Change this as needed to move to the desired display settings
Send,{Down 2}
sleep 100
Send,{Enter}
return
i am trying to use a powershell script that allows me to change the user account and password a specific service runs under.
$account="domain\account"
$password="password"
$svc=gwmi win32_service -filter "name='MyService'"
$svc.change($null,$null,$null,$null,$null,$false,$account,$password,$null,$null,$null)
I could check that the account was changed but when i tried to run the service from the Service.msc GUI it fails with a logon failure.
If I use to start the service from the script itself i get the following error.
$svc.StartService()
__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ReturnValue : 0
PSComputerName :
__GENUS : 2
__CLASS : __PARAMETERS
__SUPERCLASS :
__DYNASTY : __PARAMETERS
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
ReturnValue : 15
PSComputerName :
According to microsoft it is a logon error : Return Value 15.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa384901%28v=vs.85%29.aspx
I have verified the password a millions time and when I copy and paste the password using the GUI it just works.
May be i am missing something in my script. (SecurityPolicy is unrestricted on this machine)
You need to set the SeServiceLogonRight privilege on the user before setting the service account.
Set-Privileges $account "SeServiceLogonRight"
$svc=gwmi win32_service -filter "name='MyService'"
$svc.change($null,$null,$null,$null,$null,$false,$account,$password,$null,$null,$null)
An example of the Set-Privileges function which uses LSA:
function Set-Privileges
{
param(
$username,
$Privilege
)
# C# code from http://www.codeproject.com/Articles/4863/LSA-Functions-Privileges-and-Impersonation
$Source = #"
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace Privileges {
public class LsaUtility {
// Import the LSA functions
[DllImport("advapi32.dll", PreserveSig=true)]
private static extern UInt32 LsaOpenPolicy(
ref LSA_UNICODE_STRING SystemName,
ref LSA_OBJECT_ATTRIBUTES ObjectAttributes,
Int32 DesiredAccess,
out IntPtr PolicyHandle
);
[DllImport("advapi32.dll", SetLastError=true, PreserveSig=true)]
private static extern int LsaAddAccountRights(
IntPtr PolicyHandle,
IntPtr AccountSid,
LSA_UNICODE_STRING[] UserRights,
int CountOfRights);
[DllImport("advapi32")]
public static extern void FreeSid(IntPtr pSid);
[DllImport( "advapi32.dll", CharSet=CharSet.Auto, SetLastError=true, PreserveSig=true)]
private static extern bool LookupAccountName(
string lpSystemName, string lpAccountName,
IntPtr psid,
ref int cbsid,
StringBuilder domainName, ref int cbdomainLength, ref int use );
[DllImport( "advapi32.dll")]
private static extern bool IsValidSid(IntPtr pSid);
[DllImport("advapi32.dll")]
private static extern int LsaClose(IntPtr ObjectHandle);
[DllImport("kernel32.dll")]
private static extern int GetLastError();
[DllImport("advapi32.dll")]
private static extern int LsaNtStatusToWinError(int status);
// define the structures
[StructLayout(LayoutKind.Sequential)]
private struct LSA_UNICODE_STRING {
public UInt16 Length;
public UInt16 MaximumLength;
public IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential)]
private struct LSA_OBJECT_ATTRIBUTES{
public int Length;
public IntPtr RootDirectory;
public LSA_UNICODE_STRING ObjectName;
public UInt32 Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;}
// enum all policies
private enum LSA_AccessPolicy : long{
POLICY_VIEW_LOCAL_INFORMATION = 0x00000001L,
POLICY_VIEW_AUDIT_INFORMATION = 0x00000002L,
POLICY_GET_PRIVATE_INFORMATION = 0x00000004L,
POLICY_TRUST_ADMIN = 0x00000008L,
POLICY_CREATE_ACCOUNT = 0x00000010L,
POLICY_CREATE_SECRET = 0x00000020L,
POLICY_CREATE_PRIVILEGE = 0x00000040L,
POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080L,
POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100L,
POLICY_AUDIT_LOG_ADMIN = 0x00000200L,
POLICY_SERVER_ADMIN = 0x00000400L,
POLICY_LOOKUP_NAMES = 0x00000800L,
POLICY_NOTIFICATION = 0x00001000L
}
/// <summary>Adds a privilege to an account</summary>
/// <param name="accountName">Name of an account - "domain\account" or only "account"</param>
/// <param name="privilegeName">Name ofthe privilege</param>
/// <returns>The windows error code returned by LsaAddAccountRights</returns>
public static int SetRight(String accountName, String privilegeName){
int winErrorCode = 0; //contains the last error
//pointer an size for the SID
IntPtr sid = IntPtr.Zero;
int sidSize = 0;
//StringBuilder and size for the domain name
StringBuilder domainName = new StringBuilder();
int nameSize = 0;
//account-type variable for lookup
int accountType = 0;
//get required buffer size
LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType);
//allocate buffers
domainName = new StringBuilder(nameSize);
sid = Marshal.AllocHGlobal(sidSize);
//lookup the SID for the account
bool result = LookupAccountName(String.Empty, accountName, sid, ref sidSize, domainName, ref nameSize, ref accountType);
//say what you're doing for debug
//Console.WriteLine("LookupAccountName result = "+result);
//Console.WriteLine("IsValidSid: "+IsValidSid(sid));
//Console.WriteLine("LookupAccountName domainName: "+domainName.ToString());
if( ! result ){
winErrorCode = GetLastError();
Console.WriteLine("LookupAccountName failed: "+ winErrorCode);
}else{
//initialize an empty unicode-string
LSA_UNICODE_STRING systemName = new LSA_UNICODE_STRING();
//combine all policies
int access = (int)(
LSA_AccessPolicy.POLICY_AUDIT_LOG_ADMIN |
LSA_AccessPolicy.POLICY_CREATE_ACCOUNT |
LSA_AccessPolicy.POLICY_CREATE_PRIVILEGE |
LSA_AccessPolicy.POLICY_CREATE_SECRET |
LSA_AccessPolicy.POLICY_GET_PRIVATE_INFORMATION |
LSA_AccessPolicy.POLICY_LOOKUP_NAMES |
LSA_AccessPolicy.POLICY_NOTIFICATION |
LSA_AccessPolicy.POLICY_SERVER_ADMIN |
LSA_AccessPolicy.POLICY_SET_AUDIT_REQUIREMENTS |
LSA_AccessPolicy.POLICY_SET_DEFAULT_QUOTA_LIMITS |
LSA_AccessPolicy.POLICY_TRUST_ADMIN |
LSA_AccessPolicy.POLICY_VIEW_AUDIT_INFORMATION |
LSA_AccessPolicy.POLICY_VIEW_LOCAL_INFORMATION
);
//initialize a pointer for the policy handle
IntPtr policyHandle = IntPtr.Zero;
//these attributes are not used, but LsaOpenPolicy wants them to exists
LSA_OBJECT_ATTRIBUTES ObjectAttributes = new LSA_OBJECT_ATTRIBUTES();
ObjectAttributes.Length = 0;
ObjectAttributes.RootDirectory = IntPtr.Zero;
ObjectAttributes.Attributes = 0;
ObjectAttributes.SecurityDescriptor = IntPtr.Zero;
ObjectAttributes.SecurityQualityOfService = IntPtr.Zero;
//get a policy handle
int resultPolicy = (int)LsaOpenPolicy(ref systemName, ref ObjectAttributes, access, out policyHandle);
winErrorCode = LsaNtStatusToWinError(resultPolicy);
if(winErrorCode != 0){
Console.WriteLine("OpenPolicy failed: "+ winErrorCode);
}else{
//Now that we have the SID an the policy,
//we can add rights to the account.
//initialize an unicode-string for the privilege name
LSA_UNICODE_STRING[] userRights = new LSA_UNICODE_STRING[1];
userRights[0] = new LSA_UNICODE_STRING();
userRights[0].Buffer = Marshal.StringToHGlobalUni(privilegeName);
userRights[0].Length = (UInt16)( privilegeName.Length * UnicodeEncoding.CharSize );
userRights[0].MaximumLength = (UInt16)( (privilegeName.Length+1) * UnicodeEncoding.CharSize );
//add the right to the account
int res = LsaAddAccountRights(policyHandle, sid, userRights, 1);
winErrorCode = LsaNtStatusToWinError(res);
if(winErrorCode != 0){
Console.WriteLine("LsaAddAccountRights failed: "+ winErrorCode);
}else{
Console.WriteLine("LsaAddAccountRights successful");
}
LsaClose(policyHandle);
}
FreeSid(sid);
}
return winErrorCode;
}
}
}
"#
Add-Type -TypeDefinition $Source -Language CSharp
[Privileges.LsaUtility]::SetRight($username, $Privilege) | Out-Null
}
Not entirely relevent to this thread but could be useful to someone searching for this type of script.
PowerShell script to scan a computer/server find a service running under a specific account and stop the service, change the password, and then restrart the service.
I used the ListServices.psm1 module, http://gallery.technet.microsoft.com/scriptcenter/How-to-Clear-Printing-21d59516, to make the task easier for me. One quick note which took me a good 15 minutes to troubleshoot, it appears that if one is trying to set a password with a dollar symbol ($) in it, contain it within 'single quotes' not "double quotes"
Import-Module "C:\Tools\ListServices.psm1"
$pass = 'H:5Th7$!pc'
$Username = "abcservice"
$compName = gc env:computername
$newpass = [ADSI]"WinNT://$compName/$Username,user"
$newpass.SetPassword($pass)
$newpass.SetInfo()
Get-OSCServiceList -ComputerName "$compName" -UserName "$Username" | select Name | foreach { $_.Name} | Out-file -FilePath "C:\servicelist\service.txt"
foreach ($name in (Get-Content -Path "C:\servicelist\service.txt")) {
Write-Host "$name"
Stop-Service "$name"
Set-OSCServicePSW -ComputerName "$compName" -ServiceName $name -UserName ".\$Username" - NewPassWord $pass
Start-Service "$name"
}
HTH someone out there.
If you are just updating the password for the service account, and not changing which account runs the service, you might have more luck just changing the password itself. I've had success with this call to Win32_Service.Change():
$service.Change($Null,$Null,$Null,$Null,$Null,$Null,$Null,$Password)
This has consistently worked for me to update passwords.
Have you tried stopping the service before changing the password?
The following is working for me
$account="domain\account"
$password="password"
$svc=gwmi win32_service -filter "name='MyService'"
$svc.StopService();
$result = $svc.change($null,$null,$null,$null,$null,$false,$account,$password,$null,$null,$null)
if ($result.ReturnValue -eq '0') {write-host "Password changed"} else {write-host "Error: $result.ReturnValue"};
$svc.StartService();
I suspect that your password probably has at least one character that has special meaning in an interpolated string. Try single-quoting:
$password='password'
In general, it's a better habit to use single quotes by default, and only use double quotes when you specifically want to interpolate something.
BTW, also verify that you're using the correct service name. You need to use the Name property not the DisplayName. Try gwmi win32_service -filter "name='MyService'" at the prompt and make sure that doesn't return an error. It's probably the single vs. double quoting issue, though.
I know this issue was posted several years ago, however, I just encountered the same exact issue, with nearly identical code, resulting in the exact same returns.
The installer that I am using utilizes subinacl.exe to set permissions. The following article explains how to set those permissions:
http://www.waynezim.com/2010/02/how-to-set-permission-on-a-service-using-subinacl/
I simply added the 'i' switch in installation, and added the user to the appropriate group (if applicable). Considering the age of the article, I hope someone finds this useful.
SCENARIO
I'm writing a cmdlet for Powershell 2.0 using Visual Studio 2008 and .NET 3.5
the cmdlet requires 3 arguments.
my intended grammar of the cmdlet is something like this:
cmdletname [foo|bar] p1, p2
That reads as the user must give a value for "-foo" or "-bar" but can't give both together.
EXAMPLE OF VALID INPUT
cmdletname -foo xxx -p1 hello -p2 world
cmdletname -bar yyy -p1 hello -p2 world
EXAMPLE OF INVALID INPUT
cmdletname -foo xxx -bar yyy -p1 hello -p2 world
MY QUESTION
My question is on how to do this in powershell so that it does all the checking for me - or if that is possible at all.
I know I can use just have two optional parameters for foo and bar and simply do the error checking manually. That's how I have it implemented currently.
Alternatively, I am interested in suggestions for different approaches.
You can use the parameter attribute to declare multiple parameter sets. You then simply assign parameters that are mutually exclusive to different parameter sets.
EDIT:
This is also documented in 'about_Functions_Advanced_Parameters', under the section "ParameterSetName Named Argument". This is how different sets of parameters is handled with cmdlets like Get-Random (which has mutually exclusive parameters):
> get-random -input 4 -max 77
Get-Random : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:11
+ get-random <<<< -input 4 -max 77
+ CategoryInfo : InvalidArgument: (:) [Get-Random], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.PowerShell.Commands.GetRandomCommand
Here's an example of doing it in a function:
function exclusive_params() {
param(
[parameter(ParameterSetName="seta")]$one,
[parameter(ParameterSetName="setb")]$two,
$three
)
"one: $one"; "two: $two"; "three: $three"
}
The parameters one and two are in different parameter sets, so they cannot be specified together:
> exclusive_params -one foo -two bar -three third
exclusive_params : Parameter set cannot be resolved using the specified named parameters.
At line:1 char:17
+ exclusive_params <<<< -one foo -two bar -three third
+ CategoryInfo : InvalidArgument: (:) [exclusive_params], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,exclusive_params
Which is the same error I got with Get-Random. But I can use the parameters independently:
> exclusive_params -one foo -three third
one: foo
two:
three: third
...or:
> exclusive_params -two bar -three third
one:
two: bar
three: third
Here's an example of using ParameterSetName taken from a cmdlet in the PowerShell Community Extensions. BTW, for ideas you can browse the PSCX source code.
[Cmdlet(VerbsCommon.Set, PscxNouns.Clipboard,
DefaultParameterSetName = ParamSetText)]
[Description("Copies the item in the system clipboard.")]
[RelatedLink(typeof(GetClipboardCommand))]
[RelatedLink(typeof(OutClipboardCommand))]
[RelatedLink(typeof(WriteClipboardCommand))]
public class SetClipboardCommand : ClipboardCommandBase
{
... fields elided
const string ParamSetRtf = "Rtf";
const string ParamSetHtml = "Html";
const string ParamSetText = "Text";
const string ParamSetFiles = "Files";
const string ParamSetImage = "Image";
.
[AllowNull]
[Parameter(ValueFromPipeline = true, ParameterSetName = ParamSetImage)]
public Image Image { get; set; }
.
[AllowNull]
[AllowEmptyCollection]
[Parameter(ValueFromPipeline = true, ValueFromRemainingArguments = true,
ParameterSetName = ParamSetFiles)]
public FileSystemInfo[] Files { get; set; }
.
[AllowNull]
[AllowEmptyString]
[Parameter(ValueFromPipeline = true, ValueFromRemainingArguments = true,
ParameterSetName = ParamSetText)]
public string Text { get; set; }
.
[Parameter(ValueFromPipeline = true, ValueFromRemainingArguments = true,
ParameterSetName = ParamSetHtml)]
public string Html { get; set; }
.
[Parameter(ValueFromPipeline = true, ValueFromRemainingArguments = true,
ParameterSetName = ParamSetRtf)]
public string Rtf { get; set; }
.
protected override void ProcessRecord()
{
...
}
.
protected override void EndProcessing()
{
ExecuteWrite(delegate
{
switch (ParameterSetName)
{
case ParamSetFiles:
if (Paths.Count == 0)
WinFormsClipboard.Clear();
else
WinFormsClipboard.SetFileDropList(_paths);
break;
case ParamSetImage:
if (Image == null)
WinFormsClipboard.Clear();
else
WinFormsClipboard.SetImage(_image);
break;
case ParamSetRtf:
SetTextContents(Rtf, TextDataFormat.Rtf);
break;
case ParamSetHtml:
SetTextContents(Html, TextDataFormat.Html);
break;
default:
SetTextContents(Text, TextDataFormat.UnicodeText);
break;
}
});
}
...
}
Note that the cmdlet typically declares a default ParameterSetName that helps PowerShell determine the "default" parameter set to use when there is ambiguity. Later on, if needed, you can determine which parameter set is in force by querying this.ParameterSetName as the switch statement does above in the EndProcessing() override.
I came here but with an additional requirement: Optional mutual exclusive parameters.
This post here helped me to find half of the answer. So I thought to post here the full answer in case someone has the same requirements.
The code below can be used at the top of a Powershell script to have 4 optional parameters of which LaunchAsAdmin and LaunchAsCouponBrowser are mutually exclusive while token and WorkstationName are also optional but can be combined with any other parameter.
[CmdletBinding(DefaultParametersetName="default")]
Param(
[string]$token,
[string]$WorkstationName,
[parameter(ParameterSetName="seta")][switch]$LaunchAsAdmin,
[parameter(ParameterSetName="setb")][switch]$LaunchAsCouponBrowser
)