Ideas

Jun 20, 2012 at 10:21 AM

I was looking at your project as a replacement for BGInfo for the company I work at, great work but it lacks some things to be perfect.


Selecting the image with the closest resolution seems a good idea, but I think it's more important to keep the original aspect ratio and not have the image stretched.

In my case, the logo is in the center with borders at the top and bottom edge of the image so I could just use a very wide image with the 'Fill' option.

Of course you could have an image for each different resolution, but I think the best way to adapt any background to any screen (16/9, 16/10, 4/3, 5/4 although they tend to disappear) might be to adjust your logic to use the closest aspect ratio image (even if it's much bigger, I think downsizing is ok) so you could have one image per aspect ratio if you wish but then I would still use the fill effect to make sure the image is not stretched.

For multiple monitors, it seems Microsoft didn't think about those people with monitors of different resolutions (and we have a lot of people using their laptop screen plus a bigger external screen in my company) so in that case the only solution is to stitch the images with appropriate resolution for each screen together and apply it as tiled.
Looking into that problem, I stumbled upon another CodePlex project : WallMaster, which does just that (although with the possibility to use different pictures).
I think it is worth looking into it and reuse some code for this project. I have looked at the source code and it shouldn't be too difficult to use WallpaperCreator.cs (which takes care of adjusting the image to each monitor and stitching them according to their positions) and also ImageResizer.cs (which has the different resizing methods : center, centerfit, stretch, fit and fill) if it's ok with the author of course. But then I am not a developper (only did some C, PHP and VBS) so it takes me a very long time to figure out which part of the code I need, where the variables come from and how to change it to use the settings and variables from your project.
But then I don't know if you're actually thinking about spending more time on this project.

The other things I would like to add would be :
Use the generated wallpaper as logon screen for Windows 7 : it has to be saved as C:\Windows\System32\Oobe\info\backgrounds\backgroundDefault.jpg and change the reg key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\Background\OEMBackground to 1.
Ability to display an environment variable (in my case the logon server but it would be better if you could just specify any of them I guess).
Ability to display a registry key value (in my case the TeamViewer ID but same as withe the reg key).
And then fix some of the bugs :
Sometimes after modifying the text, some of the info (Operating system for example) will generate an error.
And when you align the text to the right, it's not properly aligned (but then I could just left align and adjust the margin for it to be close to the right edge.

Well don't hesitate to tell me if I can help in any way.
BTW I speak french.

Jul 19, 2012 at 7:37 AM
Edited Jul 19, 2012 at 7:40 AM

So using ImageResizer.cs from WallMaster was easy enough after you add the class and change the namespace :

g.DrawImage(bestImage, ImageResizer.ResizeImage(bestImage.Size, bounds, ImageResizer.WallpaperStretchStyle.Fill));

And for now I decided I would leave it at that for multi monitors : the image will be calculated based on the main screen and fill it keeping the correct aspect ratio (I use a very wide image to make sure I'll get the whole height not matter the monitor and even on VM's) and I use the Tile option so as to not get black bars on the other screens but of course you'll get your image cropped or tiled depending if the second screen is smaller or bigger than the main screen.

Saving the image as logon screen for Windows 7 was probably the hardest part : to be able to access System32 on Windows 7 x64, the program has to be 64 bits so I had to change the project to Any CPU (which is complicated on Visual C# Express). Saving as a Jpeg is also more complicated than it should be but then I realized it also works with a PNG picture with the .jpg extension which will get you a better quality.

Of course you should be careful as it might not work with an image bigger than 256KB so either you have to test your picture or maybe add a check and change to jpeg if necessary and I also didn't make any elevation mechanism, I my case I create the backgroundDefault.jpg file and set the appropriate permissions for users through Group Policy, I just have an exception catch to make sure the program doesn't crash in case it doesn't have permissions (you can also start the program as admin).

            //Save Windows 7 Logon Screen
            Version WinVersion = Environment.OSVersion.Version;
            if (WinVersion.Major == 6 && WinVersion.Minor == 1)
            {
                string Win7LogonScreen = @"%SystemRoot%\System32\oobe\info\backgrounds\backgroundDefault.jpg";
                
                try
                {
                    b.Save(Win7LogonScreen, ImageFormat.Png);
                }
                catch (ExternalException)
                {
                    //No permission. 
                    renderForm.Close();
                }
            }

            renderForm.Close();

I also added the information I wanted but that was probably the easiest part, you can find most things on stackoverflow :

I changed user (added domain) :

returnValue.Add("User Name", new string[] { Environment.UserDomainName.ToUpper() + @"\\" + Environment.UserName });

Free Space (free space and total size) :

freeSpace.Add(string.Format(@"{0} {1} GB / {2} GB", drive.Name, (drive.AvailableFreeSpace / (1024 * 1024 * 1024)).ToString("###,#"), (drive.TotalSize / (1024 * 1024 * 1024)).ToString("###,#")));

OS (to get all the information on one line) :

            string FullOpSystem = null;
            ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem");
            ManagementObjectCollection objectCollection = objectSearcher.Get();

            foreach (ManagementObject managementObject in objectCollection)
            {
                //returnValue.Add("Operating System", new string[] { managementObject.GetPropertyValue("Caption").ToString().Replace("Microsoft ", "") });
                FullOpSystem = managementObject.GetPropertyValue("Caption").ToString().Replace("Microsoft ", "");
            }

            FullOpSystem = FullOpSystem + Environment.OSVersion.ServicePack.Replace("Service Pack ", "SP") ;
            string OSProcessorArch = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
            FullOpSystem = FullOpSystem + " (x" + ((String.IsNullOrEmpty(OSProcessorArch) || String.Compare(OSProcessorArch, 0, "x86", 0, 3, true) == 0) ? 86 : 64).ToString() + ")";
            returnValue.Add("Operating System", new string[] { FullOpSystem });

Added some :

DNS (IPv4) :

            addresses.Clear();
            NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
           
            foreach (NetworkInterfa2ce networkInterface in networkInterfaces)
            {
                if (networkInterface.OperationalStatus == OperationalStatus.Up)
                {
                    IPInterfaceProperties ipProperties = networkInterface.GetIPProperties();
                    IPAddressCollection dnsAddresses = ipProperties.DnsAddresses;

                    foreach (IPAddress address in dnsAddresses)
                    {
                        if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) addresses.Add(address.ToString());
                    }
                }
            }
            if (addresses.Count == 0)
            {
                addresses.Add("<no dns configured>");
            }
            returnValue.Add("DNSv4 Addresses", addresses.ToArray());

Boot Time :

            foreach (ManagementObject managementObject in objectCollection)
            {
                returnValue.Add("Boot Time", new string[] { ManagementDateTimeConverter.ToDateTime(managementObject.GetPropertyValue("LastBootUpTime").ToString()).ToString() });
            }

Computer Manufacturer & Model

            objectSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystem");
            objectCollection = objectSearcher.Get();
            foreach (ManagementObject managementObject in objectCollection)
            {
                returnValue.Add("Computer Model", new string[] { managementObject.GetPropertyValue("manufacturer").ToString().Replace(" Inc.", "").Replace("System manufacturer", "<not specified>") + " " + managementObject.GetPropertyValue("model").ToString().Replace("System Product Name", "") });
            }

Computer Serial Number

            objectSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_ComputerSystemProduct");
            objectCollection = objectSearcher.Get();
            foreach (ManagementObject managementObject in objectCollection)
            {
                returnValue.Add("Computer Serial", new string[] { Regex.Replace(managementObject.GetPropertyValue("IdentifyingNumber").ToString().Replace("System Serial Number", "<not specified>"), "VMware.+", "<virtual machine>") });
            }

Teamviewer6 ID

            const string TeamviewerReg = @"\TeamViewer\Version6";
            string TeamviewerID = "<not installed>";
            if (Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE" + TeamviewerReg, "ClientID", "") != null)
            {
                TeamviewerID = string.Format(@"{0:### ### ###}", Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE" + TeamviewerReg, "ClientID", ""));
            }
            else
                if (Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node" + TeamviewerReg, "ClientID", "") != null)
                {
                    TeamviewerID = string.Format(@"{0:### ### ###}", Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node" + TeamviewerReg, "ClientID", ""));
                }

            returnValue.Add("Teamviewer6 ID", new string[] { TeamviewerID });

But then I hardcoded most of the stuff, ideally it should be made available through settings.

I was even thinking it would be great if you could make your definitions in the XML settings file, like

<Label>

<Type> : Environment, Registry, WMI, ...

<Definition> : The parameter, registry key, WMI definition or even piece of code

<Default> : In case the value is not found

So it would be much easier to add information, but then you have to plan for errors and special cases like the registry on 32 and 64 bits systems, etc.

Hope this is useful to anybody as I don't see much reaction here.

Coordinator
Jul 20, 2012 at 2:06 PM

Wow, that's a lot of changes! If you'd like to send a patch, I can merge it to the code and everyone can easily benefit.

I had started looking into rewriting WPInfo from scratch with multi monitor support and a much more extensible, and flexible, way to show information. Unfortunately I'm too busy with other projects to push forward anytime soon.

Feb 26, 2013 at 4:26 PM
Edited Feb 26, 2013 at 4:32 PM
Sorry I haven't had much time, I wanted to go through my modifications and clean things up to submit something that would be useful to everybody but I don't know if I'm up to the task.
About rewriting from scratch, it seems to me it will only be a longer delay until a newer version, the software works pretty well, I think the biggest improvement would be to make an easy way to add definitions like registry keys, wmi, etc. although that seems quite a lot of work but I would focus on that rather than on the interface. The way I see it, you could just do the configuration in xml and launch the program to update the wallpaper.
Another useful thing might be a system tray icon with the info but that's another nice to have feature.

My version works for my needs, I just changed the definitions a bit to support newer versions of Teamviewer but that's about it, I'll look into changing the Windows 8 lock screen one of these days though.

One thing that bothers me (and that's why I haven't put it in the whole firm just yet) is sometimes the program takes a really long time to apply the wallpaper on bootup (I configured it as a logon script). It runs hidden for a bit but then it shows the wallpaper with the info just like the preview and it will stay there for 30 seconds up to 1 minute and it's in front of everything else so you can't do anything in the meantime (well on the second monitor you can actually).
It might be some of the changes I made but after first bootup it always applies the wallpaper super fast.
It's not a big deal but I know people will complain about it and since it's a bit complicated to find the exact cause, do you know if there's a way to make the wallpaper apply in the background and not block the user to be sure ?