Windows 7 comes with several built-in themes. They can be accessed by right-clicking the desktop and choosing Personalize. Under Personalize, there is a section names "Aero Themes" containing themes like "Architecture" "Nature" and so on.
I tried using uxtheme.dll's GetCurrentThemeName, but it's actually giving the style name:
"C:\Windows\resources\Themes\Aero\Aero.msstyles" unless my current theme is set to Windows Basic, in which case it returns an empty string. Is there an API that actually returns the theme name, like "Nature" "Architecture" etc...?
The code I tried is as follows:
[DllImport("uxtheme", ExactSpelling = true, CharSet = CharSet.Unicode)]
public extern static Int32 GetCurrentThemeName(StringBuilder stringThemeName,
int lengthThemeName, StringBuilder stringColorName, int lengthColorName,
StringBuilder stringSizeName, int lengthSizeName);
StringBuilder stringThemeName = new StringBuilder(260);
StringBuilder stringColorName = new StringBuilder(260);
StringBuilder stringSizeName = new StringBuilder(260);
Int32 s = GetCurrentThemeName(stringThemeName, 260,stringColorName, 260,stringSizeName, 260);
After taking a look at the MSDN documentation it looks like GetThemeDocumentationProperty might be what you are looking for.
You'll want to use it in conjunction with the theme file (which you alreayd have found in the registry) as well as by passing in the SZ_THDOCPROP_DISPLAYNAME as the second parameter of the method.
In addition here is a site that has the c# method wrapper for the p/invoke call: http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System.Windows.Forms/System/Windows/Forms/VisualStyles/UXTheme.cs.htm
Hope that helps.
Related
I'm running a small tool (on Windows 7, 32 Bit) that I would like to see what document is open in another application I've tried this, which works for NotePad on Windows.
var myProcess = Process.GetProcessesByName("NotePad");
string title = myProcess[0].MainWindowTitle;
MessageBox.Show(title);
the output is:
"New Text Document - Notepad"
Now, if I try another application it doesn't always give me the correct title but I noticed that most microsoft applications seem to be fine - NotePad, WordPad, EXCEL etc. It's this other software that is an issue. It has a long title but just returns a very simple name.
Here's what I get from my application which has processName = "FooBar"
The actual running window has this up the top:
"FooBar Software Verson 1.2 - [Results]"
and my code gives:
"FooBar"
any ideas?
[EDIT} 2012-11-19
The very crux of this issue is that I was trying to get the name of the open file from the window. It now seems that the software I'm using doesn't display it there. What I've discovered is that a program called "AutoIT3 Window Spy" can get the text I need as the text of the open file is on the window and not only in the title. I downloaded the source (it's part of http://www.autohotkey.com/ which is open source. It seems to rely on many of the suggestions already made but I'm not able to figure it out just yet.) The source code that I"m looking at is c++ and is located here https://github.com/AutoHotkey/AutoHotkey
So I think the solution to my problem may lay elsewhere. This one may go unanswered.
The main window title is what you see when you go in to task manager and look at the Description column, not the window title itself.
It's the title of the process, not the title of a particular window in the process. A given process may have any number of windows open at one time.
If you need the actual window title, you have to hook user32 something like this:
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Security;
namespace Application
{
public class Program
{
public static void Main ( )
{
IntPtr hwnd = UnsafeNativeMethods.FindWindow("Notepad", null);
StringBuilder stringBuilder = new StringBuilder(256);
UnsafeNativeMethods.GetWindowText(hwnd, stringBuilder, stringBuilder.Capacity);
Console.WriteLine(stringBuilder.ToString());
}
}
[SuppressUnmanagedCodeSecurity]
internal static class UnsafeNativeMethods
{
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern int GetWindowText ( IntPtr hWnd, [Out] StringBuilder lpString, int nMaxCount );
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr FindWindow ( string lpClassName, string lpWindowName );
}
}
It's possible that the 'title' you're seeing is some kind of owner-drawn UI element which is overriding the visual representation of the title, while the Windows APIs are likely to ignore that sort of thing.
I'd recommend examining the window with a tool like Spy++ to see if that's the case.
It's also possible that the author of the application decided to override the WM_GETTEXT message and is returning that instead of what's actually in the titlebar, although I'm not 100% sure if GetWindowText() is being called here and whether or not it sends the message explicitly or works some other way.
The developer has stated the following:
"I think the failure may be due to a discontinued property Form.Caption in VB 6.0 that was replaced with a Form.Text in. NET"
Thank you all for your valuable suggestions along the way!
How can I get the window class name of a certain process?
I want to achieve this in c#.
I've tried the process class in c# but I can only get the window name of the process.
Thanks
I assume you mean you want to get the class name of the main window of a process.
To do this, you will need to get the handle to the main window using the MainWindowHandle of your Process object, and then use the following interop method to obtain the class name:
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
see pinvoke.net for sample code and MSDN for details on the function.
You can also use the windows ui automation framework to achieve this without getting into pinvoke.
int pidToSearch = 316;
//Init a condition indicating that you want to search by process id.
var condition = new PropertyCondition(AutomationElementIdentifiers.ProcessIdProperty,
pidToSearch);
//Find the automation element matching the criteria
AutomationElement element = AutomationElement.RootElement.FindFirst(
TreeScope.Children, condition);
//get the classname
var className = element.Current.ClassName;
This old stackoverflow post is asking pretty much the same question that I have, however using Spy++ I have obtained the controls handle ID. Now what? :)
I am not sure what this process is called where I can obtain the contents of another applications control from a .net application, therefore I am not having much success with results on the old google machine.
I have an MFC application with a listbox that contains data I need to automate a task using a WPF C# application. I would prefer not to use an external lib and don't think it would be too labour intensive once I have found the process and have my C# app take visibility of the respective list control to do what I need.
Can anyone please point me in the right direction as to what I should be looking up or provide some code to get me started. At this point I'm stuck and my little project relies pretty heavily on this. I don't want to use an OCR either.
Thanks,
Ash
To get text from Win32 ListBox control you have to use messages and functions specially for that control, here is a reference :
http://msdn.microsoft.com/en-us/library/windows/desktop/ff485971%28v=vs.85%29.aspx
In your case you should first see how many items are in the listbox with LB_GETCOUNT, and then for each item get text with LB_GETTEXT.
Here is the method that will return items in a list, parameter is ListBox control window handle :
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, StringBuilder lParam);
const int LB_GETCOUNT = 0x018B;
const int LB_GETTEXT = 0x0189;
private List<string> GetListBoxContents(IntPtr listBoxHwnd)
{
int cnt = (int)SendMessage(listBoxHwnd, LB_GETCOUNT, IntPtr.Zero, null);
List<string> listBoxContent = new List<string>();
for (int i = 0; i < cnt; i++)
{
StringBuilder sb = new StringBuilder(256);
IntPtr getText = SendMessage(listBoxHwnd, LB_GETTEXT, (IntPtr)i, sb);
listBoxContent.Add(sb.ToString());
}
return listBoxContent;
}
This question's answer should get you started. Google P/Invoke and FindWindow() / GetWindowText() family of methods.
Hope that helps.
I was trying to use windows api to find out the version info of an installed application.
I used the upgrade code to find out the product code using MsiEnumRelatedProducts api, but when I try to use MsiGetProductInfo using the product code, the version info comes back as garbage.
Here is my MsiGetProductInfo api:
[DllImport("msi.dll", CharSet = CharSet.Unicode)]
private static extern Int32 MsiGetProductInfo(
string product, string property, [Out] StringBuilder valueBuf,
ref Int32 len);
MsiGetProductInfo(sbProductCode, "INSTALLPROPERTY_INSTALLVERSION", builder, ref len);
Any thoughts on what I'm doing wrong?
In response to #JoshHetland the string to pass is the CamelCase postfix of the INSTALLPROPERTY_VERSIONSTRING - remember that MSI is case sensitive.
So:
INSTALLPROPERTY_VERSIONSTRING becomes VersionString
INSTALLPROPERTY_INSTALLDATE becomes InstallDate
and so on.
Complete list of properties available is on the MSDN page for the MsiGetProductInfo function .
Here is what I did that my solved my problem.
Int32 m_len = 11512;
StringBuilder m_versionInfo = new StringBuilder(m_len);
StringBuilder m_sbProductCode = GetProductCodeFromMsiUpgradeCode();
MsiGetProductInfo(m_sbProductCode.ToString(), "**VersionString**", m_versionInfo, ref m_len);
return m_versionInfo.ToString();
This did return me the version string ,and also converted from decimal into string format like 1.4.3.
Application.ProductVersion works for me, no need to call WinAPI manually (I am still in .Net 1.1 though)
I'm trying to use ShSetFolderPath function in C#. I work on Win7, I've managed to use ShSetKnownFolderPath and it works fine.
Since this function is unavaible in WinXP, i tried to invoke ShSetFolderPath. Because i'm not familiar with invoking, I've done some searching and found something on some French forum. I don't speak French, but this declaration makes sense (as written in Remarks of function documentation in MSDN library):
[DllImport( "Shell32.dll", CharSet = CharSet.Unicode, EntryPoint = "#232" ) ]
private static extern int SHSetFolderPath( int csidl, IntPtr hToken, uint flags, string path );
I call it like that:
private static int CSIDL_DESKTOP = 0x0000;
public static void SetDesktopPath(string path)
{
int ret;
ret = SHSetFolderPath(CSIDL_DESKTOP, IntPtr.Zero, 0, path);
if (ret != 0)
{
Console.WriteLine(ret);
Console.WriteLine(Marshal.GetExceptionForHR(ret));
}
}
It works in Win7, but in XP function returns -2147024809, which means "Value does not fall within the expected range".
My guess is, it's something wrong with Dll importing. Any idea?
Funny thing.
I've taken another look at CSIDL list. And I've realized I was trying to change some "low-level" reference (i guess) to desktop:
CSIDL_DESKTOP = 0x0000, // <desktop>
While I actually wanted to change just folder location, and i should've use this:
CSIDL_DESKTOPDIRECTORY = 0x0010, // <user name>\Desktop.
And THIS works.
It explains everything. Shame on me.
Nah, that's not it. The error code, converted to hex, is 0x80070057. The 7 indicates a Windows error, 57 is error code 87, ERROR_INVALID_PARAMETER, "The parameter is incorrect".
A couple of possible reasons. First is that entry point #232 isn't actually the entry point for SHSetFolderPath(). You might be calling a different function, it wouldn't know what to do with the argument values you pass. Hard to say, it is an unnamed entry point on XP's version of shell32.dll. Or it could be that XP just isn't happy about you changing the desktop folder path. Not that surprising, there's a heckofalot it has to do to actually implement that, refreshing all Explorer.exe views, rebuilding the desktop contents and whatnot.
Check this thread for possible help.