I'm working on a tool that needs to get the current user's wallpaper path.
On Windows 7, I can get that by reading
HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Desktop\General\WallpaperSource.
On my Windows 8 installation, that key always has the value
C:\Users\Peter\AppData\Roaming\Microsoft\Windows Photo Viewer\Windows Photo Viewer Wallpaper.jpg
which is not even the wallpaper that's currently set.
Is there any other key I can rely on?
You are FAR better off calling SystemParametersInfo with the SPI_SETDESKWALLPAPER option to set the desktop wallpaper. As far as I can tell, registry key you're using is undocumented and thus can change at any time without warning.
See this stack overflow question for an example of how to call the SystemParametersInfo with SPI_SETDESKWALLPAPER.
Based heavily on the code available at pinvoke.net, the correct way to retrieve the current users desktop wallpaper is to use the SystemParametersInfo function. A sample of doing this is as follows:
using System;
using System.Runtime.InteropServices;
namespace WallpaperPathRetrieval
{
class Program
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern Int32 SystemParametersInfo(UInt32 action,
UInt32 uParam, string vParam, UInt32 winIni);
private static readonly UInt32 SPI_GETDESKWALLPAPER = 0x73;
private static uint MAX_PATH = 260;
static void Main(string[] args)
{
string wallpaper = new string('\0', (int)MAX_PATH);
SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, wallpaper, 0);
wallpaper = wallpaper.Substring(0, wallpaper.IndexOf('\0'));
}
}
}
The key you are mentioning isn't the correct one. Sounds to me that you've placed an image as your desktop background from Internet Explorer, and that key was opened to register it.
The correct key to get the desktop background location is: Confirmed on: XP, CE, Vista, 7, 8
HKEY_CURRENT_USER\Control Panel\Desktop\Wallpaper
Details:
Main key: HKEY_CURRENT_USER
Sub key: Control Panel\Desktop
Value name: WallPaper
Value type: REG_SZ
Value data: full path for the image being used as the desktop background
Also, under HKEY_CURRENT_USER\Control Panel\Desktop\ you will find other wallpaper related options to apply different styles: Center, Tile, and Stretch.
HKEY_CURRENT_USER\Control Panel\Desktop\WallpaperStyle
HKEY_CURRENT_USER\Control Panel\Desktop\TileWallpaper
In order to apply the styles use the following guide:
Center
WallpaperStyle = 0
TileWallpaper = 0
Tile
WallpaperStyle = 0
TileWallpaper = 1
Stretch
WallpaperStyle = 2
TileWallpaper = 0
It's stored in a value named TranscodedImageCache (REG_BINARY). Here is a VBScript that reads/converts to plain text and outputs the value.
How to Determine the Current Wallpaper File Name and Path in Windows 8 - The Winhelponline Blog
Related
The only information I can find is this undocumented method in shell32.dll, which applies to Windows 7 only:
[DllImport("shell32.dll", EntryPoint = "#262", CharSet = CharSet.Unicode, PreserveSig = false)]
public static extern void SetUserTile(string username, int whatever, string picpath);
Is there a way (PowerShell, C#, command line etc.) to programmatically set the user picture of a Local Account (i.e. not Active Directory account, not Microsoft account) on the login page of Windows 8?
I'm sure i've come across this before and the solution was to copy the desired photo to the following location, the picture must be named user.bmp and it has to be a 128x128, 24-bit bitmap picture.
%ProgramData%\Microsoft\User Account Pictures\
I'm trying to import winmm.dll on a WP8.1 app to try and control device volume. Based on research from Google, I have created a Windows Runtime Component to wrap the actual function call, and then I call this from the main app. Since the issue is clearly in the wrapper, here's the code:
public sealed class VolumeControl
{
[DllImport("winmm.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
internal static extern int waveOutSetVolume(IntPtr uDeviceID, int dwVolume);
public static void Set(int volume)
{
// get volume as proportion of maximum
double newVolume = ushort.MaxValue * volume / 10.0;
// convert this into volume for two channels
uint v = ((uint)newVolume) & 0xffff;
uint vAll = v | (v << 16);
// set volume
waveOutSetVolume(IntPtr.Zero, (int)vAll);
}
I have also enabled unsafe code in the wrapper's project properties. DllImport is possible for native libraries in WP8.1, as far as I understand. I don't expect this app to pass certification on the Windows Store, but still I can't see why this code wouldn't work on a developer unlocked device.
Any idea if I've missed something here?
On Windows Mobile, all waveform audio function are implemented in 'coredll.dll'. Use this DLL instead of 'winmm.dll'.
The documentation has the answer:
Requirements
Minimum supported client
Windows 2000 Professional [desktop apps only]
In other words this function is not available from Windows Store app.
Turns out that WP8.1 has a WinMMBase.dll rather than plain old winmm.dll.
I found this by running a web server hack to browse the System32 folder (see xda-developers). After downloading the dll and inspecting it with DLL Export (http://www.nirsoft.net/utils/dll_export_viewer.html), I found that it does have the waveSetOutVolume function. The function itself doesn't seem to affect the volume though, but that wasn't the point of the question I guess :)
I'm trying to set a system environment variable in my application, but get an SecurityException. I tested everything I found in google - without success.
Here is my code (note, that I'm administrator of my pc and run VS2012 as admin):
Attempt 1
new EnvironmentPermission(EnvironmentPermissionAccess.Write, "TEST1").Demand();
Environment.SetEnvironmentVariable("TEST1", "MyTest", EnvironmentVariableTarget.Machine);
Attempt 2
new EnvironmentPermission(EnvironmentPermissionAccess.Write, "TEST1").Demand();
using (var envKey = Registry.LocalMachine.OpenSubKey(#"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", true))
{
Contract.Assert(envKey != null, #"HKLM\System\CurrentControlSet\Control\Session Manager\Environment is missing!");
envKey.SetValue("TEST1", "TestValue");
}
Attempt 3
Also I tried to fit out my app with administrator priviliges.
Do you have any other suggestions?
The documentation tells you how to do this.
Calling SetEnvironmentVariable has no effect on the system environment variables. To programmatically add or modify system environment variables, add them to the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment registry key, then broadcast a WM_SETTINGCHANGE message with lParam set to the string "Environment". This allows applications, such as the shell, to pick up your updates.
So, you need to write to the registry setting that you are already attempting to write to. And then broadcast a WM_SETTINGCHANGE message as detailed above. You will need to be running with elevated rights in order for this to succeed.
Some example code:
using Microsoft.Win32;
using System;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
const int HWND_BROADCAST = 0xffff;
const uint WM_SETTINGCHANGE = 0x001a;
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool SendNotifyMessage(IntPtr hWnd, uint Msg,
UIntPtr wParam, string lParam);
static void Main(string[] args)
{
using (var envKey = Registry.LocalMachine.OpenSubKey(
#"SYSTEM\CurrentControlSet\Control\Session Manager\Environment",
true))
{
Contract.Assert(envKey != null, #"registry key is missing!");
envKey.SetValue("TEST1", "TestValue");
SendNotifyMessage((IntPtr)HWND_BROADCAST, WM_SETTINGCHANGE,
(UIntPtr)0, "Environment");
}
}
}
}
However, whilst this code does work, the .net framework provides functionality to perform the same task much more simply.
Environment.SetEnvironmentVariable("TEST1", "TestValue",
EnvironmentVariableTarget.Machine);
The documentation for the three argument Environment.SetEnvironmentVariable overload says:
If target is EnvironmentVariableTarget.Machine, the environment variable is stored in the HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment key of the local computer's registry. It is also copied to all instances of File Explorer. The environment variable is then inherited by any new processes that are launched from File Explorer.
If target is User or Machine, other applications are notified of the set operation by a Windows WM_SETTINGCHANGE message.
I am using Tamas Szekeres builds of GDAL including the C# bindings in a desktop GIS application using C# and .net 4.0
I am including the entire GDAL distribution in a sub-directory of my executable with the following folder structure:
\Plugins\GDAL
\Plugins\GDAL\gdal
\Plugins\GDAL\gdal-data
\Plugins\GDAL\proj
We are using EPSG:4326, and the software is built using 32-bit target since the GDAL C# API is using p/invoke to the 32-bit libraries (could try 64 bit since Tamas provides these, haven't gotten around to it yet).
When I run my application I get the following error
This error typically happens when software tries to access a device that is no longer attached, such as a removable drive. It is not possible to "catch" this exception because it pops up a system dialog.
After dismissing the dialog using any of the buttons, the software continues to execute as designed.
The error occurs the first time I call the following method
OSGeo.OSR.CoordinateTransformation.TransformPoint(double[] inout);
The strange stuff:
The error occurs on one, and only one computer (so far)
I've run this software in several other computers both 32 and 64 bit without problems
The error does not ocurr on the first run after compiling the GDAL shim library I am using, it only occurrs on each subsequent run
it happens regardless of release, or debug builds
it happens regardless of whether the debugger is attached or not
it happens regardless of whether I turn on or off Gdal.UseExceptions or Osr.UseExceptions();
disabling removable drives causes the bug to disappear. This is not what I consider a real solution as I will not be able to ask a customer to do this.
I have tried the following:
catching the error
changing GDAL directories and environment settings
changing computers and operating systems: this worked
used SysInternals ProcMon to trace what files are being opened with no luck, they all appear to be files that exist
I re-built the computer in question when the hard drive failed, to no avail.
"cleaning" the registry using CCleaner
files in GDAL Directory are unchanged on execution
Assumptions
Error is happening in unmanaged code
During GDAL initialization, some path is referring to a drive on the computer that is no longer attached.
I am also working on the assumption this is limited to a computer configuration error
Configuration
Windows 7 Pro
Intel Core i7 920 # 2,67GHz
12.0 GB RAM
64-bit OS
Drive C: 120 GB SSD with OS, development (Visual Studio 10), etc
Drive D: 1 TB WD 10,000k with data, not being accessed for data.
The Question
I either need a direction to trap the error, or a tool or technique that will allow me to figure out what is causing it. I don't want to release the software with the possibility that some systems will have this behaviour.
I have no experience with this library, but perhaps some fresh eyes might give you a brainwave...
Firstly, WELL WRITTEN QUESTION! Obviously this problem really has you stumped...
Your note about the error not occurring after a rebuild screams out: Does this library generate some kind of state file, in its binary directory, after it runs?
If so, it is possible that it is saving incorrect path information into that 'configuration' file, in a misguided attempt to accelerate its next start-up.
Perhaps scan this directory for changes between a 'fresh build' and 'first run'?
At very least you might find a file you can clean up on shut-down to avoid this alert...
HTH
Maybe you can try this:
Run diskmgmt.msc
Change the driveletter for Disk 2 (right click) if my assumption that Disk 2 is a Removable Disk is true
Run your application
If this removes the error, something in the application is referring to the old driveletter
It could be in the p/invoked libs
Maybe see: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46501 It talks about gcc somehow compiling a driveletter into a binary
+1 Great question, but It is not possible to "catch"
Its one of these awful solutions that will turn up on DailyWTF in 5 years. But for now it is stored here http://www.pinvoke.net/default.aspx/user32.senddlgitemmessage
using Microsoft.VisualBasic; //this reference is for the Constants.vbNo;
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern IntPtr SendDlgItemMessage(IntPtr hDlg, int nIDDlgItem, uint Msg, UIntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetActiveWindow(IntPtr hWnd);
// For Windows Mobile, replace user32.dll with coredll.dll
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetDlgItemText(IntPtr hDlg, int nIDDlgItem,[Out] StringBuilder lpString, int nMaxCount);
public void ClickSaveBoxNoButton()
{
//In this example, we've opened a Notepad instance, entered some text, and clicked the 'X' to close Notepad.
//Of course we received the 'Do you want to save...' message, and we left it sitting there. Now on to the code...
//
//Note: this example also uses API calls to FindWindow, GetDlgItemText, and SetActiveWindow.
// You'll have to find those separately.
//Find the dialog box (no need to find a "parent" first)
//classname is #32770 (dialog box), dialog box title is Notepad
IntPtr theDialogBoxHandle; // = null;
string theDialogBoxClassName = "#32770";
string theDialogBoxTitle = "Notepad";
int theDialogItemId = Convert.ToInt32("0xFFFF", 16);
StringBuilder theDialogTextHolder = new StringBuilder(1000);
//hardcoding capacity - represents maximum text length
string theDialogText = string.Empty;
string textToLookFor = "Do you want to save changes to Untitled?";
bool isChangeMessage = false;
IntPtr theNoButtonHandle; // = null;
int theNoButtonItemId = (int)Constants.vbNo;
//actual Item ID = 7
uint theClickMessage = Convert.ToUInt32("0x00F5", 16);
//= BM_CLICK value
uint wParam = 0;
uint lParam = 0;
//Get a dialog box described by the specified info
theDialogBoxHandle = FindWindow(theDialogBoxClassName, theDialogBoxTitle);
//a matching dialog box was found, so continue
if (theDialogBoxHandle != IntPtr.Zero)
{
//then get the text
GetDlgItemText(theDialogBoxHandle, theDialogItemId, theDialogTextHolder, theDialogTextHolder.Capacity);
theDialogText = theDialogTextHolder.ToString();
}
//Make sure it's the right dialog box, based on the text we got.
isChangeMessage = Regex.IsMatch(theDialogText, textToLookFor);
if ((isChangeMessage))
{
//Set the dialog box as the active window
SetActiveWindow(theDialogBoxHandle);
//And, click the No button
SendDlgItemMessage(theDialogBoxHandle, theNoButtonItemId, theClickMessage, (System.UIntPtr)wParam, (System.IntPtr)lParam);
}
}
It turns out there was no way to definitely answer this question.
I ended up "solving" the problem by figuring out that there was some hardware registered on the system that wasn't present. It is still a mystery to me why, after several years, only GDAL managed to provoke this bug.
I will put the inability to catch this exception down to the idiosyncrasies involved with p/invoke and the hardware error thrown at a very low level on the system.
You could add custom error handlers to gdal. This may help:
Link
http://trac.osgeo.org/gdal/ticket/2895
I need to read/write some information in the Windows registry from my BHO. On Windows Vista/7, I create a new key under HKEY_CURRENT_USER\Software\AppDataLow\Software. This works fine, even in protected mode.
However, it does not work on XP. I tried to change the registry to HKEY_CURRENT_USER\Software\Classes\Software or HKEY_CURRENT_USER\Software, no luck.
What is the right registry key to use on Windows XP from a BHO?
IEGetWriteableHKCU does not exist on Windows XP, it was first added in Windows Vista
Before Vista you will have to use a different approach... during installation of the BHO you need to tell Windows/IE which key(s) you want to be writable from the BHO...
There is a whole API family to handle this (supported from WinXP SP2 and up according to MSDN):
IERegisterWritableRegistryKey is used at installation time
IERegisterWritableRegistryValue is used at installation time
IERegCreateKeyEx is used at runtime
IERegSetValueEx is used at runtime
IE 7,8,9,(desktop)10 run tabs in "Protected Mode" which limits registry writes to special "writable" section. You need to ask IE for a pointer to it.
(C#)
// C# PInvoke declaration for needed IE method.
[DllImport("ieframe.dll")]
public static extern int IEGetWriteableHKCU(ref IntPtr phKey);
// ...
// somewhere inside other method:
IntPtr phKey = new IntPtr();
var answer = IEGetWriteableHKCU(ref phKey);
RegistryKey writeable_registry = RegistryKey.FromHandle(
new Microsoft.Win32.SafeHandles.SafeRegistryHandle(phKey, true)
);
RegistryKey registryKey = writeable_registry.OpenSubKey(RegistryPathString, true);
if (registryKey == null) {
registryKey = writeable_registry.CreateSubKey(RegistryPathString);
}
registryKey.SetValue("Mode", mode);
writeable_registry.Close();
See:
About Protected Mode:
http://www.codeproject.com/Articles/18866/A-Developer-s-Survival-Guide-to-IE-Protected-Mode
About Enhanced Protected Mode:
http://blogs.msdn.com/b/ieinternals/archive/2012/03/23/understanding-ie10-enhanced-protected-mode-network-security-addons-cookies-metro-desktop.aspx