Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

IActiveDesktop::SetWallpaper from Perl?

by bbfu (Curate)
on Oct 18, 2002 at 18:18 UTC ( [id://206394]=perlquestion: print w/replies, xml ) Need Help??

bbfu has asked for the wisdom of the Perl Monks concerning the following question:

First, my question. :)

Is there any way to use IActiveDesktop::SetWallpaper from Perl? I've tried Win32::API but, probably because it's an object method, I can't seem to import it directly. I can import CoCreateInstance but I can't really figure out how to use it, nor would I know how to call the method on the resulting object.

Now, some background. :)

I am currently using, via Win32::API, SystemParametersInfo to set the wallpaper. It works, but it will only accept Windows bitmap files. I have been converting the files (which I store as JPEG) as needed, using ImageMagick, but I ran into a problem.

There are a handful of files that, when converted to bitmap with ImageMagick's convert program, don't work as wallpapers in Windows XP. When I open the bitmaps in any other program, they display fine but if I try to set them as the wallpaper (either using SystemParametersInfo, or by manually setting the desktop wallpaper via the Control Panel), it won't set. It acts like it takes it, but the background doesn't change, and as you open / close / move windows, it redraws parts of the desktop in the background color (ie, it doesn't redraw the whole desktop in the background color at once, as you would expect). The strange thing is that I can copy these exact same bitmaps over to a Windows 2000 Pro machine, and they work fine as the wallpaper. And if I manually set the wallpaper to the original JPEG file for these images, it works fine in XP as well.

It seems to be something between ImageMagick's convert and Windows XP reading the resulting bitmaps. I can't see anything about either the source JPEG or the resulting bitmap that seems any different from any of the others, that work fine. It is very consistant, though; it's always the same files that cause the problem. (If anyone wants a copy of one of the problem files, reply here, or /msg me.)

So, anyway, I wanted to use IActiveDesktop::SetWallpaper, to take advantage of the built-in conversion routines. I don't know if it's feasable but, if so, I would greatly appreciate any suggestions. I'd also appreciate any recommendations on another good command-line driven conversion program that I could try.

For what it's worth, here's the code I'm currently using to set the wallpaper, along with my poor attempt at using CoCreateInstance:

#!/win2k/Perl/bin/perl use Win32::API; use constant SPI_SETDESKWALLPAPER => 20; use constant SPIF_UPDATEANDSENDINI => 3; use constant NULL => 0; my $syspinf = Win32::API->new('user32','SystemParametersInfo', [I,I,P, +I], I) or die "Could not import function.\n"; $syspinf->Call(SPI_SETDESKWALLPAPER, 0, $ARGV[0], SPIF_UPDATEANDSENDIN +I); exit; ############################################################### # This is my muddled attempts at using CoCreateInstance... :( use constant CLSID_ACTIVEDESKTOP => '{75048700-EF1F-11D0-9888-006097 +DEACF9}'; my $coci = Win32::API->new('ole32','CoCreateInstance', [I,P,I,I,P], I) or die "Could not import SetWallpaper.\n"; my $setwall; my $res = $coci->Call(CLSID_ACTIVEDESKTOP, undef, 0, 0, \$setwall); print "Got: `$res'\n"; print "Setwall: `$setwall'\n"; #$setwall->($ARGV[0], 0); exit;

And here's the snippet where I call convert.exe:

$target = "BMP"; $current = "JPEG"; $Config{'Options'}{'Converted File'} = "converted.bmp"; $Config{'Conversions BMP'}{'JPEG'} = 'convert "$infile" "$outfile"'; my $ofile = $Config{'Options'}{'Converted File'}; my $cmd = $Config{"Conversions $target"}{$current}; $cmd =~ s/\$infile/$filename/g; $cmd =~ s/\$outfile/$ofile/g; system $cmd and die "Error converting $filename -> $ofile: $!\n";

Note that the same problem occurs if I do everything manually (ie, I manually run convert.exe on the file, and manually set the wallpaper to the resulting bitmap). So the problem really doesn't seem to be in my code, unless I'm really missing something. *shrug* :)

bbfu
Black flowers blossum
Fearless on my breath

Replies are listed 'Best First'.
Re: IActiveDesktop::SetWallpaper from Perl?
by blm (Hermit) on Oct 19, 2002 at 14:16 UTC

    I can't find the ProgID that matches that CLSID but according to a post by Jan Dubois it is alright to specify a CLSID instead. Win32::OLE determines which it has been passed by the presence of digits. However to my disappointment I found that this does not work:

    use strict; use warnings; use Win32::OLE; use Win32::OLE::Variant; use constant CLSID_ACTIVEDESKTOP => '{75048700-EF1F-11D0-9888-006097 +DEACF9}'; my $actdesktop = Win32::OLE->new(CLSID_ACTIVEDESKTOP) or die(); $actdesktop->SetWallpaper('C:\WINNT\winnt256.bmp');
    It gives an error

    C:\>"C:\Documents and Settings\blm\Desktop\test1212.pl" Win32::OLE(0.1501) error 0x80004002: "No such interface supported" at +C:\Documen ts and Settings\blm\Desktop\test1212.pl line 8 eval {...} called at C:\Documents and Settings\blm\Desktop\tes +t1212.pl l ine 8 Died at C:\Documents and Settings\blm\Desktop\test1212.pl line 8. C:\>

    Nothing I tried could make it work. However I may be missing something simple

    Anyway, I ended up writing a COM object that I knew would be scriptable using perl that just proxy's between perl and the actual IActiveDesktop. If you have Visual Studio it is relatively easy to create. Create an ATL COM project remembering the name (eg damnit). Select Insert ... ATL Object and give it a name MyDesktop(eg MyDesktop). Then add methods to the interface by right clicking on the Interface in Class view and selecting add method. Here is the code for my SetWallpaper method:

    STDMETHODIMP CMyDesktop::SetWallpaper(BSTR bstrPath) { HRESULT hr; IActiveDesktop *pActiveDesktop; CoInitialize(NULL); //Create an instance of the Active Desktop hr = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SER +VER, IID_IActiveDesktop, (void**)&pActiveDesktop); hr = pActiveDesktop->SetWallpaper(bstrPath, 0); if (hr != S_OK) { goto error; } hr = pActiveDesktop->ApplyChanges(AD_APPLY_ALL); pActiveDesktop->Release(); return S_OK; error: return hr; }

    I could call this code with the following perl code:

    use strict; use warnings; use Win32::OLE; use Win32::OLE::Variant; my $adtest = Win32::OLE->new('damnit.MyDesktop') or die(); $adtest->SetWallpaper('C:\WINNT\winnt256.bmp');

    Upon running this perl script my desktop wallpaper changed as expected.

    Readers please note: I am not a ATL COM guru so use this code at your risk. Thanks

    Oh Larry please forgive me for posting C++

    --blm--

      Thanks, blm. This seems like the best solution so far to me. Unfortunately, I don't have Visual Studio. :( Do you know if it's possible to create a COM object, or even just create an IActiveDesktop instance, with a normal C++ compiler? I have Bloodshed Dev C++ (basically an IDE for the mingw compiler). Would it be impractical to create a COM object with that? Any pointers on how to go about it if it's feasable?

      Anyway, thanks again! :)

      bbfu
      Black flowers blossum
      Fearless on my breath

        From the FAQ

        Is support provided for COM? MinGW has some support for COM programs. Programmers have had much better luck writing COM applications in C than C++. Work is in progress to improve support. Check the MinGW mailing list archives for more details on COM and more links to example files.

        I am sorry I can't offer more then that. All I can say is good luck if you go this way

        --blm--
Re: IActiveDesktop::SetWallpaper from Perl?
by Mr. Muskrat (Canon) on Oct 19, 2002 at 18:22 UTC

    I decided to try a different method of acheiving the goal. I used Win32::TieRegistry and Win32::GUITest to do it.

    It still needs to have some file checks done to see if the input is a valid desktop wallpaper but I'll leave that as an exercise for someone else. I've had way too much fun today!

    #!/usr/bin/perl use strict; use warnings; use Win32::TieRegistry; use Win32::GuiTest qw(FindWindowLike SetForegroundWindow PushButton); # Is Active Desktop enabled? Get the (REG_BINARY) value from the regis +try my $AD = $Registry->{'CUser\Software\Microsoft\Windows\CurrentVersion\ +Explorer\ShellState'}; $AD = unpack("x4 C", $AD ); # A BIG thanks to tye for this line! if ($AD == 19 or $AD == 99) { print "Active Desktop enabled.\n"; } elsif ($AD == 35 or $AD == 83) { print "Active Desktop disabled. Only bitmaps will work.\n"; } else { die "Unknown registry value"; } my $input; if (@ARGV == 0) { print "Enter the full name of the image to use: "; $input = <STDIN>; chomp($input); exit if ($input eq ""); } else { $input = $ARGV[0]; } print "\nUsing $input\n"; # add file tests to see if the input is a valid desktop wallpaper $Registry->{'CUser\Control Panel\Desktop\WallPaper'} = $input; system("control","desk.cpl") or die "oopsie"; my @windows = FindWindowLike(0, "^Display Properties"); # find all win +dows that match die "oopsie" unless @windows; my $hwnd = $windows[0]; # we'll just the first match SetForegroundWindow($hwnd); # Bring it the foreground PushButton("OK");
Re: IActiveDesktop::SetWallpaper from Perl?
by Mr. Muskrat (Canon) on Oct 19, 2002 at 15:16 UTC
    #!/usr/bin/perl use strict; use warnings; use Win32::API; use constant CLSID_ACTIVEDESKTOP => '{75048700-EF1F-11D0-9888-006097 +DEACF9}'; my $coci = Win32::API->new('ole32','CoCreateInstance', [qw(I P I I P)] +, 'I') or die "Could not create CoCreateInstance object"; my $setwall; my $res = $coci->Call(CLSID_ACTIVEDESKTOP, undef, 0, 0, \$setwall); print "Got: $res\n";

    Errors returned:

    Argument "{75048700-EF1F-11D0-9888-006097DEACF9}" isn't numeric in sub +routine entry at noname.pl line 11. Use of uninitialized value in subroutine entry at noname.pl line 11.
    And it outputs:
    Got: -2147024809

     

    Update: Fixed the die message in my code

    Edit by tye to change PRE to CODE tags around long lines

      IMHO you are getting that error message because the first parameter of

      STDAPI CoCreateInstance( REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv );
      is a struct not a string
      typedef struct _GUID { // size is 16 DWORD Data1; WORD Data2; WORD Data3; BYTE Data4[8]; } GUID;

      defined on my machine in WINNT.H

      --blm--

        Thanks. I don't play with the system calls too often. I was using the code that bbfu provided. I didn't know if it was correct or not. Perhaps I'll have time to play with this more later in the week. It's becoming quite fun.

(bbfu) (IE's convert) Re: IActiveDesktop::SetWallpaper from Perl?
by bbfu (Curate) on Oct 18, 2002 at 19:21 UTC

    Oh, of course if anyone has any pointers on how to hook into IE's image conversion routines directly, that would be great, too. :)

    bbfu
    Black flowers blossum
    Fearless on my breath

Re: IActiveDesktop::SetWallpaper from Perl?
by blogan (Monk) on Oct 19, 2002 at 17:16 UTC
    You could try setting the Active Desktop to display an HTML file with a <BODY BACKGROUND="bg.jpg"> and then just change bg.jpg. Of course, you'd need to somehow figure out how to refresh it.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://206394]
Approved by krisahoch
Front-paged by RhetTbull
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (3)
As of 2025-06-19 03:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.