Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

Is it possible to read/write the registry of a remote machine with different credentials?

Ask Question

I am using Delphi to remotely read and write the registry of a remote machine. This works when my account on my machine has admin access to the remote machine.

However, I'd like to be able to specify a username / pwd when connecting to read the registry so I can connect with alternate credentials.

With the file-system I called the following (with the username and password) and was able to establish a connection to the remote system and perform filesystem related functions. However, this does not appear to work with the registry.

netResource : TNetResource; begin FillChar(netResource, SizeOf(netResource), 0); netResource.dwScope := RESOURCE_GLOBALNET; netResource.dwType := RESOURCETYPE_DISK; netResource.dwDisplayType := RESOURCEDISPLAYTYPE_SHARE; netResource.dwUsage := RESOURCEUSAGE_CONNECTABLE; netResource.lpRemoteName := PChar('\\192.168.1.105\IPC$'); WNetAddConnection2(netResource, PChar(password), PChar(username), 0);

And here is the example of the function I'd like to be able to call, but specify the credentials with access to the remote machine:

procedure TForm1.SetWallpaperKey() ;
   reg:TRegistry;
begin
   reg:=TRegistry.Create;
   with reg do begin
     if RegistryConnect('192.168.1.105') then
       if OpenKey('\Control Panel\desktop', False) then begin
       //change wallpaper and tile it
        reg.WriteString ('Wallpaper','c:\windows\CIRCLES.bmp') ;
        reg.WriteString ('TileWallpaper','1') ;
        //disable screen saver//('0'=disable, '1'=enable)
        reg.WriteString('ScreenSaveActive','0') ;
    finally
      reg.Free;
                Well, WNetAddConnection2 does not enable remote registry access. I was just using that as an example of what I was looking for in regard to registry access.
– Mick
                May 12, 2011 at 1:26

Mick , i can't resist give you a WMI solution for you problem ;) , the wmi have a class called StdRegProv which allow you to access the registry in local and remote machines. A key point is the namespace where the class is located, that depends of the version of windows installed in the remote machine. so for Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95 the StdRegProv class is available in the root\default namespace and for others versions like windows Vista/7 the namespace is root\CIMV2.

Now to configure the credentials to access the registry, you must set these values in the SWbemLocator.ConnectServer method in this way :

FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass);

Another impor point is which this class just exposes methods to access the registry not properties, so you cannot use a wmi query, instead you must execute wmi methods.

check the next samples to see how it works.

checking if you have permissions over a key

Windows, SysUtils, ActiveX, ComObj; // The CheckAccess method verifies that the user possesses the specified // permissions. The method returns a uint32 which is 0 if successful or some other // value if any other error occurred. procedure Invoke_StdRegProv_CheckAccess; const Server = '192.168.52.128'; User = 'Administrator'; Pass = 'password'; FSWbemLocator : OLEVariant; FWMIService : OLEVariant; FWbemObjectSet : OLEVariant; FInParams : OLEVariant; FOutParams : OLEVariant; begin FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator'); //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2. //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95: StdRegProv is available only in root\default namespace. FWMIService := FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass); //For Windows Vista or Windows 7 you must use the root\CIMV2 namespace //FWMIService := FSWbemLocator.ConnectServer(Server, 'root\CIMV2', User, Pass); FWbemObjectSet:= FWMIService.Get('StdRegProv'); FInParams := FWbemObjectSet.Methods_.Item('CheckAccess').InParameters.SpawnInstance_(); FInParams.hDefKey:=HKEY_LOCAL_MACHINE; FInParams.sSubKeyName:='SYSTEM\CurrentControlSet'; FInParams.uRequired:=KEY_QUERY_VALUE; FOutParams := FWMIService.ExecMethod('StdRegProv', 'CheckAccess', FInParams); Writeln(Format('bGranted %s',[FOutParams.bGranted])); Writeln(Format('ReturnValue %s',[FOutParams.ReturnValue]));

reading a string value

// The GetStringValue method returns the data value for a named value whose data 
// type is REG_SZ. 
procedure  Invoke_StdRegProv_GetStringValue;
const
  Server = '192.168.52.128';
  User   = 'Administrator';
  Pass   = 'password';
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  //http://msdn.microsoft.com/en-us/library/aa393664%28v=vs.85%29.aspx
  //StdRegProv is preinstalled in the WMI namespaces root\default and root\cimv2.
  //Windows Server 2003, Windows XP, Windows 2000, Windows NT 4.0, and Windows Me/98/95:  StdRegProv is available only in root\default namespace.
  FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass);
  //For Windows Vista or Windows 7 you must use the  root\CIMV2 namespace
  //FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\CIMV2', User, Pass);
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('GetStringValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\Borland\Delphi\5.0';
  FInParams.sValueName:='App';
  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'GetStringValue', FInParams);
  Writeln(Format('sValue                %s',[FOutParams.sValue]));
  Writeln(Format('ReturnValue           %s',[FOutParams.ReturnValue]));

writing a string value

// The SetStringValue method sets the data value for a named value whose data type 
// is REG_SZ.
procedure  Invoke_StdRegProv_SetStringValue;
const
  Server = '192.168.52.128';
  User   = 'Administrator';
  Pass   = 'password';
  FSWbemLocator   : OLEVariant;
  FWMIService     : OLEVariant;
  FWbemObjectSet  : OLEVariant;
  FInParams       : OLEVariant;
  FOutParams      : OLEVariant;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer(Server, 'root\default', User, Pass);
  FWbemObjectSet:= FWMIService.Get('StdRegProv');
  FInParams     := FWbemObjectSet.Methods_.Item('SetStringValue').InParameters.SpawnInstance_();
  FInParams.hDefKey:=HKEY_LOCAL_MACHINE;
  FInParams.sSubKeyName:='SOFTWARE\Borland\Delphi\5.0';
  FInParams.sValueName:='Dummy';
  FInParams.sValue:='ADummyValue';
  FOutParams    := FWMIService.ExecMethod('StdRegProv', 'SetStringValue', FInParams);
  Writeln(Format('ReturnValue           %s',[FOutParams.ReturnValue]));

For more options you must check the documentation about this class.

I hope this help you.

One follow-up question, how could I write a DWORD with this? I changed "SetStringValue" to SetDWORDValue...and I changed 'ADummyValue' to a Cardinal, but it still throws an error on that line. Any ideas? – Mick May 12, 2011 at 17:43 @Mick, you must change the line FInParams.sValue:='ADummyValue' to FInParams.uValue:=1000;//your dword value goes here – RRUZ May 12, 2011 at 17:47

Now using the windows API , the TRegistry.RegistryConnect function internally call the RegConnectRegistry windows function , the documentation says :

If the current user does not have proper access to the remote computer, the call to RegConnectRegistry fails. To connect to a remote registry, call LogonUser with LOGON32_LOGON_NEW_CREDENTIALS and ImpersonateLoggedOnUser before calling RegConnectRegistry.

Windows 2000:  One possible workaround is to establish a session
  

to an administrative share such as IPC$ using a different set of credentials. To specify credentials other than those of the current user, use the WNetAddConnection2 function to connect to the share. When you have finished accessing the registry, cancel the connection.

Windows XP Home Edition:  You cannot use this function to connect to
  

a remote computer running Windows XP Home Edition. This function does work with the name of the local computer even if it is running Windows XP Home Edition because this bypasses the authentication layer.

check the next samples

using the windows api registry functions

Windows, Registry, SysUtils; procedure AccessRemoteRegistry(const lpMachineName, lpszUsername , lpszPassword: PChar); const LOGON32_LOGON_NEW_CREDENTIALS = 9; REG_OPTION_OPEN_LINK = $00000008; netResource : TNetResource; dwFlags : DWORD; dwRetVal : DWORD; phToken : THandle; phkResult : HKEY; phkResult2 : HKEY; lpType : DWORD; lpData : PByte; lpcbData : DWORD; begin ZeroMemory(@netResource, SizeOf(netResource)); netResource.dwType := RESOURCETYPE_ANY; netResource.lpLocalName := nil; netResource.lpRemoteName:= lpMachineName; netResource.lpProvider := nil; dwFlags := CONNECT_UPDATE_PROFILE; dwRetVal := WNetAddConnection2(netResource, lpszPassword, lpszUsername, dwFlags); if dwRetVal=NO_ERROR then begin Writeln('Connected'); if LogonUser(lpszUsername, nil, lpszPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50,phToken) then begin if ImpersonateLoggedOnUser(phToken) then begin dwRetVal:=RegConnectRegistry(lpMachineName,HKEY_LOCAL_MACHINE,phkResult); if dwRetVal = ERROR_SUCCESS then begin dwRetVal:=RegOpenKeyEx(phkResult,PChar('SOFTWARE\Borland\Delphi\5.0'), REG_OPTION_OPEN_LINK, KEY_QUERY_VALUE, phkResult2); if dwRetVal = ERROR_SUCCESS then begin lpType:=REG_SZ; //get the size of the buffer dwRetVal:=RegQueryValueEx(phkResult2, PChar('App'), nil, @lpType, nil, @lpcbData); if dwRetVal = ERROR_SUCCESS then begin GetMem(lpData,lpcbData); dwRetVal:=RegQueryValueEx(phkResult2, 'App', nil, @lpType, lpData, @lpcbData); if dwRetVal = ERROR_SUCCESS then WriteLn(PChar(lpData)) Writeln(Format('RegQueryValueEx error %d',[dwRetVal])); finally FreeMem(lpData); Writeln(Format('RegQueryValueEx error %d',[dwRetVal])); finally RegCloseKey(phkResult2); Writeln(Format('RegOpenKeyEx error %d',[dwRetVal])); Writeln(Format('RegConnectRegistry error %d',[dwRetVal])); finally RevertToSelf; RaiseLastOSError; finally CloseHandle(phToken); RaiseLastOSError; finally dwRetVal:=WNetCancelConnection2(netResource.lpRemoteName, CONNECT_UPDATE_PROFILE, false); if dwRetVal<>NO_ERROR then Writeln(Format('WNetCancelConnection2 error %d',[dwRetVal])); Writeln(Format('WNetAddConnection2 Connection error %d',[dwRetVal]));

use in this way

AccessRemoteRegistry('\\192.168.52.128','NormalUser','password');

using the TRegistry delphi class

procedure AccessRemoteRegistry2(const lpMachineName, lpszUsername , lpszPassword: PChar);
const
 LOGON32_LOGON_NEW_CREDENTIALS = 9;
 REG_OPTION_OPEN_LINK          = $00000008;
  netResource : TNetResource;
  dwFlags     : DWORD;
  dwRetVal    : DWORD;
  phToken     : THandle;
  Reg         : TRegistry;
begin
  ZeroMemory(@netResource, SizeOf(netResource));
  netResource.dwType      := RESOURCETYPE_ANY;
  netResource.lpLocalName := nil;
  netResource.lpRemoteName:= lpMachineName;
  netResource.lpProvider  := nil;
  dwFlags  := CONNECT_UPDATE_PROFILE;
  dwRetVal := WNetAddConnection2(netResource, lpszPassword, lpszUsername, dwFlags);
  if dwRetVal=NO_ERROR then
  begin
     Writeln('Connected');
     if LogonUser(lpszUsername, nil, lpszPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50,phToken) then
     begin
        if ImpersonateLoggedOnUser(phToken) then
        begin
           reg:=TRegistry.Create;
             reg.RootKey:=HKEY_LOCAL_MACHINE;
             if reg.RegistryConnect(lpMachineName) then
              if reg.OpenKey('SOFTWARE\Borland\Delphi\5.0',False) then
                WriteLn(reg.ReadString('App'));
           finally
             reg.CloseKey;
             reg.Free;
         finally
            RevertToSelf;
        RaiseLastOSError;
       finally
         CloseHandle(phToken);
     RaiseLastOSError;
    finally
       dwRetVal:=WNetCancelConnection2(netResource.lpRemoteName, CONNECT_UPDATE_PROFILE, false);
       if dwRetVal<>NO_ERROR then
         Writeln(Format('WNetCancelConnection2 error %d',[dwRetVal]));
    Writeln(Format('WNetAddConnection2 Connection error %d',[dwRetVal]));

use in this way

AccessRemoteRegistry2('\\192.168.52.128','NormalUser','password');

Maybe you need WNetAddConnection3? I don't think it enables remote registry though.

Personally though I would look into WMI, and network-named-pipes, which are methods of remote registry access that Windows supports out of the box.

Also, you know about TRegistry.RegistryConnect. It doesn't seem to have the userid/password, thing you want, but I am still not clear why that isn't enough.

WMI may be my only option. The issue with TRegistry.RegistryConnect is exactly that it doesn't have the userid/pwd option. The remote machine is not on a domain, and I only have the credentials of a local account on the remote system, which is why I need to be able to specify them when reading/writing the remote system's registry. – Mick May 12, 2011 at 1:33 You can probably create an extra account on each remote machine, with your current name, and current password, and give it access to the administrators group, and this will also work. – Warren P May 12, 2011 at 4:12

In case anyone wants to use WNetAddConnection2 correctly in C#, here's some code. The problem is WNetAddConnection2 expects an IntPtr which holds the buffer of the NETRESOURCE, otherwise you get return code of 487 (0x1E7) which is ERROR_INVALID_ADDRESS. Attempt to access invalid address.

I personally was trying to get registry key that honored elevated privileges and thought by connecting to IPC$ it would work, but of course not, seems only way is using RegCreateKeyEx with the base key from RegConnectRegistry... which is acceptable as long as it actually works once I get around to it, haha!

public class IPCShareSession : IDisposable
    #region PInvoke Functions,Structs,Enums
    [DllImport("mpr.dll", CharSet = CharSet.Unicode)]
    private static extern int WNetAddConnection2(IntPtr NetResource, string Password, string Username, WNetConnect Flags);
    [DllImport("mpr.dll", CharSet = CharSet.Unicode)]
    private static extern int WNetCancelConnection2(string ServerName, WNetDisconnect Flags, bool Force);
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private class NETRESOURCE
        public WNetResourceScope dwScope = 0;
        public WNetResourceType dwType = 0;
        public WNetResourceDisplayType dwDisplayType = 0;
        public WNetResourceUsage dwUsage = 0;
        public string lpLocalName = null;
        public string lpRemoteName = null;
        public string lpComment = null;
        public string lpProvider = null;
    private enum WNetResourceScope
        Connected = 0x1,
        GlobalNetwork = 0x2,
        Remembered = 0x3,
        Recent = 0x4,
        Context = 0x5
    public enum WNetResourceType
        Any = 0,
        Disk = 1,
        Print = 2,
        Reserved = 8
    private enum WNetResourceUsage
        Connectable = 0x1,
        Container = 0x2,
        NoLocalDevice = 0x4,
        Sibling = 0x8,
        Attached = 0x10,
        All = Connectable | Container | Attached
    private enum WNetResourceDisplayType
        Generic = 0x0,
        Domain = 0x1,
        Server = 0x2,
        Share = 0x3,
        File = 0x4,
        Group = 0x5,
        Network = 0x6,
        Root = 0x7,
        ShareAdmin = 0x8,
        Directory = 0x9,
        Tree = 0xa,
        NDSContainer = 0xb
    private enum WNetConnect : int
        CONNECT_UPDATE_PROFILE = 0x00000001,
        CONNECT_UPDATE_RECENT = 0x00000002,
        CONNECT_TEMPORARY = 0x00000004,
        CONNECT_INTERACTIVE = 0x00000008,
        CONNECT_PROMPT = 0x00000010,
        CONNECT_REDIRECT = 0x00000080,
        CONNECT_CURRENT_MEDIA = 0x00000200,
        CONNECT_COMMANDLINE = 0x00000800,
        CONNECT_CMD_SAVECRED = 0x00001000,
        CONNECT_CRED_RESET = 0x00002000
    private enum WNetDisconnect
        DISCONNECT=0,
        DISCONNECT_AND_REMOVE=1
    #endregion
    private string ServerShare = null;
    private NETRESOURCE serverNR = null;
    private bool bConnected = false;
    public IPCShareSession(string ServerName, string ShareName = "IPC$")
        ServerShare = string.Format(@"\\{0}\{1}", ServerName, ShareName);
        //Setup nr to be a IPC share session
        serverNR = new NETRESOURCE
            dwType = WNetResourceType.Disk,
            lpLocalName = null,
            lpRemoteName = ServerShare,
            lpProvider = null,
            dwDisplayType = WNetResourceDisplayType.ShareAdmin,
            dwUsage = WNetResourceUsage.Connectable,
    public int Connect()
        if(!bConnected)
            IntPtr pNR = Marshal.AllocHGlobal(Marshal.SizeOf(serverNR));
            Marshal.StructureToPtr(serverNR, pNR, false);
            int ret = WNetAddConnection2(pNR, null, null, WNetConnect.CONNECT_TEMPORARY);
            //Free our unmanaged pointer now that we're done with it.
            Marshal.FreeHGlobal(pNR);
            bConnected = ret == 0;
            return ret;
            return -1;
    public int Disconnect()
        int ret = 0;
        if (bConnected)
            ret = WNetCancelConnection2(ServerShare, WNetDisconnect.DISCONNECT, true);
            bConnected = false;
            ret = -1;
        return ret;
    #region IDisposable Support
    private bool disposedValue = false; // To detect redundant calls
    protected virtual void Dispose(bool disposing)
        if (!disposedValue)
            if (disposing)
                if (bConnected)
                    Disconnect();
            disposedValue = true;
    public void Dispose()
        Dispose(true);
    #endregion
        

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.