Not able to access some keys in registry using Win32::TieRegistry - windows

I am on Windows 10 using Strawberry Perl version 5.30 and trying to print out the values of the Registry key HKEY_CLASSES_ROOT/Directory/Background/shell/WSL using the Perl module Win32::TieRegistry. Here is a screen shot from the Registry Editor app in Windows 10:
I am using this Perl script to print the value:
use feature qw(say);
use warnings;
use strict;
use Data::Dumper qw(Dumper);
use Win32::TieRegistry( Delimiter=>"/", ArrayValues=>0 );
{
dump_keys("HKEY_CLASSES_ROOT/Directory/Background/shell/WSL");
dump_keys("HKEY_CLASSES_ROOT/Directory/Background/shell/AnyCode");
}
sub dump_keys {
my ($key) = #_;
say "Dumping keys for $key:";
my $tree= $Registry->{$key};
my #keys = keys %$tree;
print Dumper(\#keys);
}
The output is (running from a CMD terminal with adminstration privileges):
>perl p.pl
Dumping keys for HKEY_CLASSES_ROOT/Directory/Background/shell/WSL:
$VAR1 = [];
Dumping keys for HKEY_CLASSES_ROOT/Directory/Background/shell/AnyCode:
$VAR1 = [
'command/',
'/'
];
as seen it prints the AnyCode subkey but not the WSL subkey. I also checked with running reg query from the same CMD:
>reg query HKEY_CLASSES_ROOT\Directory\Background\shell\WSL
HKEY_CLASSES_ROOT\Directory\Background\shell\WSL
(Default) REG_SZ #wsl.exe,-2
Extended REG_SZ
NoWorkingDirectory REG_SZ
So that works fine, but why doesn't the Perl script print the value of the WSL subkey?

It turns out that even if you run the Perl script as admin, you do not necessarily have write access to a given registry key, see this blog post for more information.
According to the documentation for Win32::TieRegistry, the default $Registry (the tied hash exported by Win32::TieRegistry) is opened with both read and write access:
The virtual root of the Registry pretends it was opened with access
KEY_READ()|KEY_WRITE() so this is the default access when opening keys
directory via $Registry
This explains why some keys cannot be accessed from $Registry since it when accessing a key the write permission is required.
As explained in the blog post it is possible to grant yourself write access to any key in the registry by using the regedit app in Windows 10.
Another approach is to only require read access (not write access) when opening a tied hash with Win32::TieRegistry:
use feature qw(say);
use warnings;
use strict;
use Data::Dumper qw(Dumper);
use Win32::RunAsAdmin qw(force);
use Win32API::Registry qw(regLastError KEY_READ);
use Win32::TieRegistry( Delimiter=>"/", ArrayValues=>0 );
{
my $reg = $Registry->Open("HKEY_CLASSES_ROOT/Directory",
{ Access=>KEY_READ(), Delimiter=>"/" }
);
dump_keys($reg, "Background/shell/WSL");
dump_keys($reg, "Background/shell/AnyCode");
}
sub dump_keys {
my ($reg, $key) = #_;
my $tree= $reg->{$key};
if (!$tree) {
say "failed: $^E";
}
else {
my #keys = keys %$tree;
print Dumper(\#keys);
}
}

Related

Is it possible programmatically add folders to the Windows 10 Quick Access panel in the explorer window?

Apparently Microsoft has (sort of) replaced the "Favorites" Windows explorer item with the Quick Access item. But I haven't been able to find a way to programmatically add folders to it (neither on Google not MSDN). Is there no way to do this yet?
There is a simple way to do it in powershell (at least) :
$o = new-object -com shell.application
$o.Namespace('c:\My Folder').Self.InvokeVerb("pintohome")
Hope it helps.
Yohan Ney's answer for pinning an item is correct. To unpin an item you can do this:
$QuickAccess = New-Object -ComObject shell.application 
($QuickAccess.Namespace("shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}").Items() | where {$_.Path -eq "C:\Temp"}).InvokeVerb("unpinfromhome")
Here's a script I wrote to make pin/unpin a little easier:
https://gallery.technet.microsoft.com/Set-QuickAccess-117e9a89
Maybe it will help someone until MS releases an API.
I ran procmon and it seems that these registry keys are involved
Pin to Quick access:
HKEY_CLASSES_ROOT\Folder\shell\pintohome
When unpin:
HKEY_CLASSES_ROOT\PinnedFrequentPlace\shell\unpinfromhome\command
Also this resource is used when pinning: (EDIT1: can't find it any longer..)
AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations\{SOME_SORT_OF_GUID}.automaticDestinations-ms
You can try opening it with 7-zip, there are several files in there which fit the destination
EDIT2: I found running this in the 'Run' opens up Quick access:
shell:::{679F85CB-0220-4080-B29B-5540CC05AAB6}
I got an answer here:
Windows 10 - Programmatically use Quick Access
Apparently, it's not possible yet, but a proposition for such an API has been made.
I like Johan's answer but I added a little bit to make not remove some of the items that were already in there. I had a ton pinned in there by accident I must have selected pin folder or something to quick access.
$QuickAccess = New-Object -ComObject shell.application
$okItems = #("Desktop","Downloads","Documents","Pictures","iCloud Photos","iCloud Drive","PhpstormProjects","Wallpapers 5","Videos", "Schedules for testing")
($QuickAccess.Namespace("shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}").Items() | where {$_.name -notin $okItems}).InvokeVerb("unpinfromhome");
Building on what others have said... This allows you to remove all pinned folders (not just all/recent folders/items):
$o = new-object -com shell.application
$($o.Namespace("shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}").Items() | where { $_.IsFolder -eq "True" -and ($($_.Verbs() | Where-Object {$_.Name -in "Unpin from Quick access"}) -ne $null)}).InvokeVerb("unpinfromhome")
I needed this so I could backup / restore my list of Quick Access links quickly. So I put this at the top of my script (to remove all pinned items, then the rest of the script re-adds them. This ensures the order is correct.
And yes, I'm sure there's a better syntax for the above code.
EDIT: After further investigation, I have realized Quick Access contains two "sections". One is Pinned Items, and the other is Frequent Folders. For some reason, Music and Videos come by default on the second section (at least in 1909), unlike the rest (Desktop/Downloads/Documents/Pictures). So the verb to invoke changes from unpinfromhome to removefromhome (defined in HKEY_CLASSES_ROOT\FrequentPlace, CLSID: {b918dbc4-162c-43e5-85bf-19059a776e9e}). In PowerShell:
$Unpin = #("$env:USERPROFILE\Videos","$env:USERPROFILE\Music")
$qa = New-Object -ComObject shell.application
$ob = $qa.Namespace('shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}').Items() | ? {$_.Path -in $Unpin}
$ob.InvokeVerb('removefromhome')
In Windows 1909, you can't unpin the Music or Videos links from Quick Access with the proposed PowerShell solution. It seems they're special because they don't include the "pin" icon, unlike the rest.
The solution is to pin and unpin them. I don't know much about the Windows API or PowerShell so there may be a less convoluted way.
$Unpin = #("$env:USERPROFILE\Videos","$env:USERPROFILE\Music")
$qa = New-Object -ComObject shell.application
ForEach ($dir in $Unpin) { $qa.Namespace($dir).Self.InvokeVerb('pintohome') }
$ob = $qa.Namespace('shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}').Items() | ? {$_.Path -in $Unpin}
$ob.InvokeVerb('unpinfromhome')
Another way is renaming f01b4d95cf55d32a.automaticDestinations-ms, then logging off/rebooting so that it's recreated. But I don't know if it has side effects. Batch script:
:: f01b4d95cf55d32a => Frequent Folders
:: 5f7b5f1e01b83767 => Recent Files
rename "%APPDATA%\Microsoft\Windows\Recent\AutomaticDestinations\f01b4d95cf55d32a.automaticDestinations-ms" f01b4d95cf55d32a.automaticDestinations-ms.bak
void PinToHome(const std::wstring& folder)
{
ShellExecute(0, L"pintohome", folder.c_str(), L"", L"", SW_HIDE);
}
that was the easy part, still unable to do an unpinfromhome
I was able to get this to work in C# using shell32 based on the information in this post and some info on shell32 from this post https://stackoverflow.com/a/19035049
You need to add a reference to "Microsoft Shell Controls and Automation".
This will add a link
Type shellAppType = Type.GetTypeFromProgID("Shell.Application");
Object shell = Activator.CreateInstance(shellAppType);
Shell32.Folder2 f = (Shell32.Folder2)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { "C:\\temp" });
f.Self.InvokeVerb("pintohome");
This will remove a link by name
Type shellAppType = Type.GetTypeFromProgID("Shell.Application");
Object shell = Activator.CreateInstance(shellAppType);
Shell32.Folder2 f2 = (Shell32.Folder2)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { "shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}" });
Console.WriteLine("item count: " + f2.Items().Count);
foreach (FolderItem fi in f2.Items())
{
Console.WriteLine(fi.Name);
if (fi.Name == "temp")
{
((FolderItem)fi).InvokeVerb("unpinfromhome");
}
}
For those that work with .NET Core:
Sadly, you cannot include a reference to "Microsoft Shell Controls and Automation" in the build-process.
But you can instead use dynamic, and omit the reference:
public static void PinToQuickAccess(string folder)
{
// You need to include "Microsoft Shell Controls and Automation" reference
// Cannot include reference in .NET Core
System.Type shellAppType = System.Type.GetTypeFromProgID("Shell.Application");
object shell = System.Activator.CreateInstance(shellAppType);
// Shell32.Folder2 f = (Shell32.Folder2)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { folder });
dynamic f = shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { folder });
f.Self.InvokeVerb("pintohome");
}
And to unpin:
public static void UnpinFromQuickAccess(string folder)
{
// You need to include "Microsoft Shell Controls and Automation" reference
System.Type shellAppType = System.Type.GetTypeFromProgID("Shell.Application");
object shell = System.Activator.CreateInstance(shellAppType);
// Shell32.Folder2 f2 = (Shell32.Folder2)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { "shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}" });
dynamic f2 = shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { "shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}" });
foreach (dynamic fi in f2.Items())
{
if (string.Equals(fi.Path, folder))
{
fi.InvokeVerb("unpinfromhome");
}
}
}

perl connectivity issue for newbie

friends could one of the perl expert tell me what I'm doing wrong here?
I'm still learning perl so newbie with this..whatever I do my connection string doesn't work.
trying to connect oracle database with perl script with below argument on cmd prompt.
$ list_tables /#testdb
Query dba_tables and list tables of user ABC
Also get output in logfile
#!/usr/local/bin/perl -w
use strict;
use Getopt::Std;
use OracleAgent;
use OracleLoginString;
my exitStatus = 0;
my %options = ();
my $oracleLogin;
getopts("o",\%options);
if (defined $options{o}) {
$oracleLogin = $options{o};
}
else {
exitWithError());
}
my $db = DBI->connect('dbi:Oracle:',$oracleLogin,'')
or die "Can't connect to Oracle database: $DBI::errstr\n";
exit($exitStatus);
Basically when I execute script I just want to provide instance name and not password.
I can connect from sqlplus prompt without password since using oracle login e.g. $sqlplus "/#testdb"
add DBD::Oracle
use DBD::Oracle;
Write a proper connection string:
my $db = DBI->connect("dbi:Oracle:host=$host;sid=$sid", $user, $passwd);

simple rest api server to pass arguments to a shell script and execute it on shell

I simply want to have a rest api server which I can call to update a file via a URL, that's it.
Here is the file:
mytextfile:
key1 = value1
key2 = value2
On the client, a script will be run which sends a string or strings to the API server.
The API server will receive them, for example /update.script?string1="blah"&string2="fun" (pretend its url encoded)
The server should then parse these strings, and then call an exec function, or another script even on the system which does some sed command to update a file
Language or implementation doesn't matter.
Looking for fresh ideas.
All suggestions are appreciated.
I don't get it: What exactly is your problem/question?
My approach to the problem "modifying a file from inside a cgi script using url-encoded arguments" would be:
Pick a language you like and start coding, in my case with Perl.
#!/usr/bin/perl
use strict; use warnings;
Fetch all your arguments. I will use the CGI module of Perl here:
use CGI::Carp;
use CGI;
my $cgi = CGI->new;
# assuming we don't have multivalued fields:
my %arguments = $cgi->Values; # handles (almost) *all* decoding and splitting
# validate arguments
# send back CGI header to acknowledge the request
# the server will make a HTTP header from that
Now either call a special subroutine / function with them …
updateHandler(%arguments);
...;
my $filename = 'path to yer file name.txt';
sub updateHandler {
my %arguments = #_;
# open yer file, loop over yer arguments, whatever
# read in file
open my $fileIn, '<', $filename or die "Can't open file for reading";
my #lines = <$fileIn>;
close $fileIn;
# open the file for writing, completely ignoring concurrency issues:
open my $fileOut, '>', $filename or die "Can't open file for writing";
# loop over all lines, make substitutions, and print it out
foreach my $line (#lines) {
# assuming a file format with key-value pairs
# keys start at the first column
# and are seperated from values by an '=',
# surrounded by any number of whitespace characters
my ($key, $value) = split /\s*=\s*/, $line, 2;
$value = $arguments{$key} // $value;
# you might want to make sure $value ends with a newline
print $fileOut $key, " = ", $value;
}
}
Please don't use this rather insecure and suboptimal code! I just wrote this as a demonstration that this isn't really complicated.
… or contrieve a way to send your arguments to another script (although Perl is more than well suited for file manipulation tasks). Choose one of the qw{}, system or exec commands, depending on what output you need from your script, or decide to pipe your arguments to the script using the open my $fh, '|-', $command mode of open.
As for the server to run this script on: Apache looks fine to me, unless you have very special needs (your own protocol, single-threading, low security, low performance) in which case you might want to code your own server. Using the HTTP::Daemon module you might manage <50 lines for a simplicistic server.
When using Apache, I'd strongly suggest using mod_rewrite to put the /path into the PATH_INFO environment variable. When using one script to represent your whole REST API, you could use the PATH_INFO to choose one of many methods/subroutines/functions. This also eliminates the need to name the script in the URL.
For example, turn the URL
http://example.com/rest/modify/filename?key1=value1
into
/cgi-bin/rest-handler.pl/modify/filename?key1=value1
Inside the Perl script, we would then have $ENV{PATH_INFO} containing /modify/filename.
This is a bit Perl-centric, but just pick any language you are comfortable with and start coding, leveraging whatever module you can use on the way.
I would use a newer Perl framework, like Mojolicious. If I make a file (test.pl):
#!/usr/bin/env perl
use Mojolicious::Lite;
use Data::Dumper;
my $file = 'file.txt';
any '/' => sub {
my $self = shift;
my #params = $self->param;
my $data = do $file;
$data->{$_} = $self->param($_) for #params;
open my $fh, '>', $file or die "Cannot open $file";
local $Data::Dumper::Terse = 1;
print $fh Dumper $data;
$self->render( text => "File Updated\n" );
};
app->start;
Then run morbo test.pl
and visit http://localhost:3000/?hello=world (or run ./test.pl get /?hello=world)
then I get in file.txt:
{
'hello' => 'world'
}
and so on.

How can I automate an existing instance of Internet Explorer using Perl?

I am struggling to get control of an IE preview control which is 'Internet Explorer_Server' class on an external windows application with perl.
Internet Explorer_Server is the class name of the window, I've found it with Spy++. and here’s my assertion code of it
$className = Win32::GUI::GetClassName($window);
if ($className eq "Internet Explorer_Server") {
...
}
I can get a handle of that 'Internet Explorer_Server' with Win32::GUI::GetWindow, but have no idea what to do next.
Updated: You are going down the wrong path. What you need is Win32::OLE.
#!/usr/bin/perl
use strict;
use warnings;
use Win32::OLE;
$Win32::OLE::Warn = 3;
my $shell = get_shell();
my $windows = $shell->Windows;
my $count = $windows->{Count};
for my $item ( 1 .. $count ) {
my $window = $windows->Item( $item );
my $doc = $window->{Document};
next unless $doc;
print $doc->{body}->innerHTML;
}
sub get_shell {
my $shell;
eval {
$shell = Win32::OLE->GetActiveObject('Shell.Application');
};
die "$#\n" if $#;
return $shell if defined $shell;
$shell = Win32::OLE->new('Shell.Application')
or die "Cannot get Shell.Application: ",
Win32::OLE->LastError, "\n";
}
__END__
So, this code finds a window with a Document property and prints the HTML. You will have to decide on what criteria you want to use to find the window you are interested in.
ShellWindows documentation.
You may want to have a look at Win32::IE::Mechanize. I am not sure whether you can control an existing IE window with this module, but accessing a single URL should be possible in about five lines of code.
Have you looked at Samie http://samie.sourceforge.net/ as this is a perl module to control IE

Is there is a way to change a Windows folder icon using a Perl script?

Is there is a way to change a Windows folder icon using a Perl script?
My intention is to change the ordinary icon of the "xxx_documents" folder to some other icon. I have to run the script in such a way that it takes care for the whole drive.
The drive contains many folders. I have to search for each folder named "documents" (e.g. "xxx_documents" or simply "documents") and change its icon to one from the "%SystemRoot%\system32\SHELL32.dll" library.
Is that possible in Perl? Thanks to all who help me with this.
You sure can do it with Perl. Windows controls directory icons by use of a hidden systemDekstop.ini file in each folder. The contents looks something like this:
[.ShellClassInfo]
IconFile=%SystemRoot%\system32\SHELL32.dll
IconIndex=41
On Windows XP (and I assume on other systems), icon 41 is a tree. Windows requires this file be explicitly set as a system file for it to work, this means we'll need to dig down into Win32API::File to create it:
#!/usr/bin/perl
use strict;
use warnings;
use Win32API::File qw(createFile WriteFile fileLastError CloseHandle);
my $file = createFile(
'Desktop.ini',
{
Access => 'w', # Write access
Attributes => 'hs', # Hidden system file
Create => 'tc', # Truncate/create
}
) or die "Can't create Desktop.ini - " . fileLastError();
WriteFile(
$file,
"[.ShellClassInfo]\r\n" .
"IconFile=%SystemRoot%\\system32\\SHELL32.dll\r\n" .
"IconIndex=41\r\n",
0, [], []
) or die "Can't write Desktop.ini - " . fileLastError();
CloseHandle($file) or die "Can't close Desktop.ini - " . fileLastError();
If you run the code above, it should set the icon for the current directory to a tree. You may need to refresh your directory listing before explorer picks up the change.
Now that we have a way to change icons, we can now just walk through a whole drive and change every folder that matches our pattern. We can do this pretty easily with File::Find, or one of its alternatives (eg, File::Find::Rule, or File::Next):
#!/usr/bin/perl
use strict;
use warnings;
use File::Find qw(find);
use Win32API::File qw(createFile WriteFile fileLastError CloseHandle);
my $topdir = $ARGV[0] or die "Usage: $0 path\n";
find( \&changeIcon, $topdir);
sub changeIcon {
return if not /documents$/i; # Skip non-documents folders
return if not -d; # Skip non-directories.
my $file = createFile(
"$_\\Desktop.ini",
{
Access => 'w', # Write access
Attributes => 'hs', # Hidden system file
Create => 'tc', # Truncate/create
}
) or die "Can't create Desktop.ini - " . fileLastError();
WriteFile(
$file,
"[.ShellClassInfo]\r\n" .
"IconFile=%SystemRoot%\\system32\\SHELL32.dll\r\n" .
"IconIndex=41\r\n",
0, [], []
) or die "Can't write Desktop.ini - " . fileLastError();
CloseHandle($file) or die "Can't close Desktop.ini - " . fileLastError();
}
Unfortunately, I've just discovered that the icon only gets changed if the directory currently has, or once had, an icon... There's clearly an attribute that's being set on the directory itself that causes Windows to look for a Desktop.ini file, but I can't for the life of me figure out what it is. As such, the above solution is incomplete; we also need to find and fix the attributes on the directory where we're adding the icon.
Paul
1.
[.ShellClassInfo]
LocalizedResourceName=#%SystemRoot%\system32\shell32.dll,-21790
InfoTip=#%SystemRoot%\system32\shell32.dll,-12689
IconResource=%SystemRoot%\system32\imageres.dll,-108
IconFile=%SystemRoot%\system32\shell32.dll
IconIndex=-237
2.
[.ShellClassInfo]
LocalizedResourceName=#%SystemRoot%\system32\shell32.dll,-21803
InfoTip=#%SystemRoot%\system32\shell32.dll,-12689
IconResource=%SystemRoot%\system32\imageres.dll,-3
To get the icon to refresh, you will have to invoke some SHChangeNotify voodoo (C++ example, but you get the idea):
int imageIndex = Shell_GetCachedImageIndexW(wPath, GetSyncFolderIconIndex(), 0);
if (imageIndex != -1)
{
// If we don't do this, and we EVER change our icon, Explorer will likely keep
// using the old one that it's already got in the system cache.
SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_DWORD | SHCNF_FLUSHNOWAIT, &imageIndex, NULL);
}
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW | SHCNF_FLUSHNOWAIT, wPath, NULL);

Resources