Changing the desktop wallpaper using C# currently displays tiny image - c#

I am trying to make a program in C#.net that changes the desktop wallpaper every time that it runs. I have made a program to change the target image and that works perfectly, however when the desktop wallpaper is changed, it only shows a very small image with very large, black borders. Here is the related code:
const int setDesktopWallpaper = 20;
const int updateIniFile = 0x01;
const int sendWinIniChange = 0x02;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
public static void setWallpaper()
{
string userName = WindowsIdentity.GetCurrent().Name; // returns COMPUTERNAME\Username
string[] badUserName = userName.Split('\\');
string goodUserName = badUserName[1];
string folderName = #"C:\Users\" + goodUserName + #"\Documents\DesktopPictures";
string[] images = File.ReadAllLines(folderName + #"\Images.txt");
string image = images[0];
string extension = Path.GetExtension(image);
string fileName = folderName + #"\0" + extension;
RegistryKey key = Registry.CurrentUser.OpenSubKey(#"Control Panel\Desktop", true);
SystemParametersInfo(setDesktopWallpaper, 0, fileName, updateIniFile | sendWinIniChange);
}
The first three variables are taken from a YouTube tutorial, as are the next two lines. In the setWallpaper class the first 8 lines get the path to the image then the rest is from the tutorial that actually changes the wallpaper.
Basically what I want to know is how to change the desktop wallpaper using C#.net where the image covers the entire desktop background.
Thank you very much :)

You need to use your registry key to set the style of background to "Stretched".
After this line
RegistryKey key = Registry.CurrentUser.OpenSubKey(#"Control Panel\Desktop", true);
Add
key.SetValue("WallpaperStyle", "2");
key.SetValue("TileWallpaper", "0");

Related

C# Setting Wallpaper from Current working Directory, getting Error: System.IO.FileNotFoundException: 'C:\Saitama.png'

Hello so i ran in a Problem that i could't fix since some days and i thought you people could help me. I want to set the Wallpaper Image from my working directory. It's like this
Directory how i pasted the Image that should be the Wallpaper
and my Method to set it as a Wallpaper is this
public sealed class Wallpaper
{
const int SPI_SETDESKWALLPAPER = 20;
const int SPIF_UPDATEINIFILE = 0x01;
const int SPIF_SENDWININICHANGE = 0x02;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
public enum Style : int
{
Tiled,
Centered,
Stretched
}
public static void Set(string imgPath)
{
string exeDir = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
Directory.SetCurrentDirectory(exeDir);
var img = System.Drawing.Image.FromFile(imgPath);
string tempPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "wallpaper.bmp");
img.Save(tempPath, ImageFormat.Bmp);
var key = Registry.CurrentUser.OpenSubKey(#"Control Panel\Desktop", true);
key.SetValue(#"WallpaperStyle", 1.ToString());
key.SetValue(#"TileWallpaper", 0.ToString());
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, tempPath, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
}
}
And i want to set the Image Wallpaper like this
Wallpaper.Set(#"/Saitama.png");
But everytime i get this Error
System.IO.FileNotFoundException: 'C:\Saitama.png'
That C:\Saitama.png works when i want to set that Image as the Source on XAML but not on this code i have to give a full Path why? And how could I make it that i can give the path like above?
Please help thanks :D
Well i didn't found a good Answer but this still makes it's job. I just made Setup Wizard and that Wizard just downloades the Images in the selected Folder and then the Program just uses the Full Path of the Destination where the Image is. I hope i could help someone!

How to get the original location of AppData\Roaming after the user has changed it?

I need to access contents in the folder %AppData%\Roaming\Microsoft.
This usually works fine by doing the following:
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Microsoft");
The problem is that now the explorer lets you change the location of %AppData% by right clicking the Roaming folder and setting the location to some other place. However, this doesn't change the location of the Microsoft folder, which will remain in the original %AppData%.
I've thought about doing something like this:
string roaming = "C:\Users\" + Environment.UserName + #"\AppData\Roaming";
Though this just looks bad and looks like it could break easily.
Any suggestions?
I don't know if .NET can do it but WinAPI can. PInvoke SHGetFolderPath with the SHGFP_TYPE_DEFAULT flag:
using System;
using System.Runtime.InteropServices;
namespace Test { class TestApp {
public class WinApi
{
public const int CSIDL_APPDATA = 0x1a;
public const int SHGFP_TYPE_DEFAULT = 1;
[DllImport("shell32.dll")]
public static extern int SHGetFolderPath(IntPtr hwnd, int csidl, IntPtr hToken, uint flags, [Out] System.Text.StringBuilder Path);
}
[STAThread]
static void Main()
{
System.Text.StringBuilder builder = new System.Text.StringBuilder(260);
int result = WinApi.SHGetFolderPath(IntPtr.Zero, WinApi.CSIDL_APPDATA, IntPtr.Zero, WinApi.SHGFP_TYPE_DEFAULT, builder);
string path = "";
if (result == 0) path = builder.ToString();
Console.WriteLine(string.Format("{0}:{1}", result, path));
}
} }
You can try use following code to access %AppData%\Roaming\Microsoft:
string appData= Environment.ExpandEnvironmentVariables("%AppData%");
string roamingMicrosoft = Path.Combine(appData, #"Microsoft");
But I'm not really sure if Windows changes environment variable %AppData% by default when user changes path to AppData by it own.

Display Count (Inc. Disabled Displays)

Hello everyone new to stackoverflow.com. Not sure how would question be asked here, but doing my best. Done, quite some research on subject, but couldn't find a single thing to resolve the detection.
Example Case:
Trying to identify count of active AND disabled displays in Windows 7-10 systems.
Code GPU[Availability]:
private int MonCount;
[DllImport("User32.dll")]
private static extern bool EnumDisplayDevices(
string lpDevice, int iDevNum,
ref DISPLAY_DEVICE lpDisplayDevice, int dwFlags);
[StructLayout(LayoutKind.Sequential)]
public struct DISPLAY_DEVICE
{
public int cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
public int StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;
public DISPLAY_DEVICE(int flags)
{
cb = 0;
StateFlags = flags;
DeviceName = new string((char)32, 32);
DeviceString = new string((char)32, 128);
DeviceID = new string((char)32, 128);
DeviceKey = new string((char)32, 128);
cb = Marshal.SizeOf(this);
}
}
public void MonitorCheck()
{
DISPLAY_DEVICE lpDisplayDevice = new DISPLAY_DEVICE(0); // OUT
DISPLAY_DEVICE monitor_name = new DISPLAY_DEVICE(0); // OUT
int devNum = 0;
while (EnumDisplayDevices(null, devNum, ref lpDisplayDevice, 0))
{
listBox1.Items.Add("\ndevNum =" + devNum);
listBox1.Items.Add("cb =" + lpDisplayDevice.cb);
listBox1.Items.Add("DeviceID =" + lpDisplayDevice.DeviceID);
listBox1.Items.Add("DeviceKey =" + lpDisplayDevice.DeviceKey);
listBox1.Items.Add("DeviceName =" + lpDisplayDevice.DeviceName.Trim());
listBox1.Items.Add("DeviceString =" + lpDisplayDevice.DeviceString.Trim());
// Show monitor name:
EnumDisplayDevices(lpDisplayDevice.DeviceName, 0, ref monitor_name, 0);
listBox1.Items.Add("Monitor name =" + monitor_name.DeviceString.Trim());
++devNum;
}
}
Source: C# how to get the Windows monitor name
Return: All attachable monitors by Graphic Adapter Information. Count is always as how many monitors can be attached in display adapter.
Code Active Displays:
Screen.AllScreens.Count();
Return: Active monitor count.
Problem:
If GPU Adapter has 3 outputs:
* Above GPU[Availability] code returns: 3
(no matter how many displays are attached to it.)
If 2 Monitors are attached (Example: Laptop Monitor + External HDMI Monitor), but display setup is set to: Laptop Monitor ONLY (External HDMI Monitor is not active) -> Active Displays code Returns: 1
Question:
How do I return 2 as there is non-active display and active display attached (Laptop Monitor + Non-active External Monitor)?
Solutions:
There's few ideas how to resolve this:
Activate all displays by extending them and simply redo: Screen.AllScreens.Count();
Idea:
Compare output of GPU[Availability] output and count all which gives any value as monitor_name.DeviceString.Trim() (However, I've tested this at desktop having NVIDIA adapter: outcome is null, but in laptop Intel HD adapter: output is always "Generic PnP-Monitor", so, this is no solution).
Stupid me... Never though to use all 3 methods to solve problem. (Seems damn hard to get code to work in code tags here at forums.
Maximum attachable displays
Currently attached displays
Currently Active displays
Solution:
Hmm, can't seem to be able to attach my code to stackoverflow reply, so:
Pastebin: Monitors counting

list view column text manupulation on resizing

I have a list view and there are multiple columns with long text values, like a column with destination file path it has a value like c:\users\kavya\new\coding\img1000.jpg
something very big.
I want to adjust the text according to size of the column when the users uses the scroll bar:
with width something very big all the data c:\users\kavya\new\coding\img1000.jpg
should be visible and when he scrolls the column header to very small only the c:\img1000.jpg has to be viewed but the memory should have the entire path
actualy we see something like c:\users\kavya…...
How can I do this?
By doing Windows API call PathCompactPathEx,
[DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
static extern bool PathCompactPathEx([Out] StringBuilder pszOut, string szPath, int cchMax, int dwFlags);
static string PathShortener(string path, int length)
{
StringBuilder sb = new StringBuilder();
PathCompactPathEx(sb, path, length, 0);
return sb.ToString();
}
OR You can try like this :
string PathShortener(string path)
{
const string pattern = #"^(\w+:|\\)(\\[^\\]+\\[^\\]+\\).*(\\[^\\]+\\[^\\]+)$";
const string replacement = "$1$2...$3";
if (Regex.IsMatch(path, pattern))
{
return Regex.Replace(path, pattern, replacement);
}
else
{
return path;
}
}
OR You can use like below :
string ellipsisedPath = OriginalPath + '\0';
visit: Add Ellipsis to a Path in a WinForms Program without Win32 API call (revisited)

Find out username(who) modified file in C#

I am using a FileSystemWatcher to monitor a folder. But when there is some event happening in the directory, I don't know how to search who made a impact on that file. I tried to use EventLog. It just couldn't work. Is there another way to do it?
I cant remember where I found this code but its an alternative to using pInvoke which I think is a bit overkill for this task. Use the FileSystemWatcher to watch the folder and when an event fires you can work out which user made the file change using this code:
private string GetSpecificFileProperties(string file, params int[] indexes)
{
string fileName = Path.GetFileName(file);
string folderName = Path.GetDirectoryName(file);
Shell32.Shell shell = new Shell32.Shell();
Shell32.Folder objFolder;
objFolder = shell.NameSpace(folderName);
StringBuilder sb = new StringBuilder();
foreach (Shell32.FolderItem2 item in objFolder.Items())
{
if (fileName == item.Name)
{
for (int i = 0; i < indexes.Length; i++)
{
sb.Append(objFolder.GetDetailsOf(item, indexes[i]) + ",");
}
break;
}
}
string result = sb.ToString().Trim();
//Protection for no results causing an exception on the `SubString` method
if (result.Length == 0)
{
return string.Empty;
}
return result.Substring(0, result.Length - 1);
}
Shell32 is a reference to the DLL: Microsoft Shell Controls And Automation - its a COM reference
Here is some example's of how you call the method:
string Type = GetSpecificFileProperties(filePath, 2);
string ObjectKind = GetSpecificFileProperties(filePath, 11);
DateTime CreatedDate = Convert.ToDateTime(GetSpecificFileProperties(filePath, 4));
DateTime LastModifiedDate = Convert.ToDateTime(GetSpecificFileProperties(filePath, 3));
DateTime LastAccessDate = Convert.ToDateTime(GetSpecificFileProperties(filePath, 5));
string LastUser = GetSpecificFileProperties(filePath, 10);
string ComputerName = GetSpecificFileProperties(filePath, 53);
string FileSize = GetSpecificFileProperties(filePath, 1);
Or get multiple comma separated properties together:
string SizeTypeAndLastModDate = GetSpecificFileProperties(filePath, new int[] {1, 2, 3});
Note: This solution has been tested on Windows 7 and Windows 10. It wont work unless running in a STA as per Exception when using Shell32 to get File extended properties and you will see the following error:
Unable to cast COM object of type 'Shell32.ShellClass' to interface type 'Shell32.IShellDispatch6'
You need to enable auditing on the file system (and auditing is only available on NTFS). You do this by applying a group policy or local security policy. You will also have to enable auditing on the file you want to monitor. You do it the same way as you modify the permissions on the file.
Auditing events are then written to the security event log. You will have to monitor this event log for the auditing events you are interested in. One way to do this is to create a scheduled task that starts an application when the events you are interested in are logged. Starting a new process for each event is only viable if events aren't logged at a very high rate though. Otherwise you will likely experience performance problems.
Basically, you don't want to look at the contents or attributes of the file (which the shell function GetFileDetails does). Also, you don't want to use a file sharing API to get the network user that has the file open (which NetGetFileInfo does). You want to know the user of the process that last modified the file. This information is not normally recorded by Windows because it would require too many resources to do that for all file activities. Instead you can selectively enable auditing for specific users doing specifc actions on specific files (and folders).
It seems that you'll need to invoke Windows API functions to get what you want, which involves PInvoke. Some people on another forum have been looking into it and figured something out, you can find their solution here. However, it seems to work only with files on network shares (not on your local machine).
For future reference, this is the code posted by dave4dl:
[DllImport("Netapi32.dll", SetLastError = true)]
static extern int NetApiBufferFree(IntPtr Buffer);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
struct FILE_INFO_3
{
public int fi3_id;
public int fi3_permission;
public int fi3_num_locks;
public string fi3_pathname;
public string fi3_username;
}
[DllImport("netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int NetFileEnum(
string servername,
string basepath,
string username,
int level,
ref IntPtr bufptr,
int prefmaxlen,
out int entriesread,
out int totalentries,
IntPtr resume_handle
);
[DllImport("netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int NetFileGetInfo(
string servername,
int fileid,
int level,
ref IntPtr bufptr
);
private int GetFileIdFromPath(string filePath)
{
const int MAX_PREFERRED_LENGTH = -1;
int dwReadEntries;
int dwTotalEntries;
IntPtr pBuffer = IntPtr.Zero;
FILE_INFO_3 pCurrent = new FILE_INFO_3();
int dwStatus = NetFileEnum(null, filePath, null, 3, ref pBuffer, MAX_PREFERRED_LENGTH, out dwReadEntries, out dwTotalEntries, IntPtr.Zero);
if (dwStatus == 0)
{
for (int dwIndex = 0; dwIndex < dwReadEntries; dwIndex++)
{
IntPtr iPtr = new IntPtr(pBuffer.ToInt32() + (dwIndex * Marshal.SizeOf(pCurrent)));
pCurrent = (FILE_INFO_3)Marshal.PtrToStructure(iPtr, typeof(FILE_INFO_3));
int fileId = pCurrent.fi3_id;
//because of the path filter in the NetFileEnum function call, the first (and hopefully only) entry should be the correct one
NetApiBufferFree(pBuffer);
return fileId;
}
}
NetApiBufferFree(pBuffer);
return -1; //should probably do something else here like throw an error
}
private string GetUsernameHandlingFile(int fileId)
{
string defaultValue = "[Unknown User]";
if (fileId == -1)
{
return defaultValue;
}
IntPtr pBuffer_Info = IntPtr.Zero;
int dwStatus_Info = NetFileGetInfo(null, fileId, 3, ref pBuffer_Info);
if (dwStatus_Info == 0)
{
IntPtr iPtr_Info = new IntPtr(pBuffer_Info.ToInt32());
FILE_INFO_3 pCurrent_Info = (FILE_INFO_3)Marshal.PtrToStructure(iPtr_Info, typeof(FILE_INFO_3));
NetApiBufferFree(pBuffer_Info);
return pCurrent_Info.fi3_username;
}
NetApiBufferFree(pBuffer_Info);
return defaultValue; //default if not successfull above
}
private string GetUsernameHandlingFile(string filePath)
{
int fileId = GetFileIdFromPath(filePath);
return GetUsernameHandlingFile(fileId);
}
This has been discussed many times. My answer from the same question:
You can't do this asynchronously with FileSystemWatcher, however you can do this synchronously using file system filter driver. The driver lets you get the user name of the account performing the operation.
Use code posted by dave4dl and update declare struct FILE_INFO_3 as following,
you can monitor user name of create and update file action(It is like to combination of FileSystemWatcher and OpenFiles.exe's functions of FileSharing Server)
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct FILE_INFO_3
{
public int fi3_id;
public int fi3_permission;
public int fi3_num_locks;
[MarshalAs(UnmanagedType.LPWStr)]
public string fi3_pathname;
[MarshalAs(UnmanagedType.LPWStr)]
public string fi3_username;
}

Categories

Resources