I'm creating a tool that shows your current branch in Unity window title.
But I cannot get or set the title. So far I managed to set title property, I used Internal_SetTitle (using Reflection), but the values does not show up in the actual window title.
On the contrary, I can set position property of main window just fine using the same method.
Did anybody manage to alter the title using UnityEditor classes?
(Probably I could work the issue around by creating native plugins for that, but I'd prefer a cross-platform / Mono solution if there is any)
I know this isn't quite what you're after but could complement your solution, or help others in a similar situation, here is Windows only PInvoke approach to get the window handle and title of that window, associated with your current thread.
You could add use of the SetWindowText WinAPI as well to set the title, although you may need to do this whenever there is a configuration change as you've mentioned.
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll", EntryPoint = "GetWindowText")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount);
delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
private static string GetUnityEditorWindowName()
{
var processId = (uint)Process.GetCurrentProcess().Id;
var windowHandle = IntPtr.Zero;
string result = "";
EnumWindows((hWnd, lParam) =>
{
GetWindowThreadProcessId(hWnd, out var windowProcessId);
if (windowProcessId != processId)
return true;
var titleBuilder = new StringBuilder(256);
GetWindowText(hWnd, titleBuilder, titleBuilder.Capacity);
if (titleBuilder.Length <= 0)
return true;
result = titleBuilder.ToString();
windowHandle = hWnd;
return false;
}, IntPtr.Zero);
if (windowHandle == IntPtr.Zero)
{
Debug.LogError("Failed to find Unity Editor Window");
}
return result;
}
Related
I have a Windows Forms application with a button that's supposed to open an exe file.
The default Windows behavior is that the exe file, once opened, will have its own icon appear in the taskbar, however the requirement is for it not to have the icon, and for the window to appear "nested" inside the icon of the Windows Form application from which it originates, so to hide the location of the exe file from users.
I have looked around for a solution, and my understanding is that in order to achieve this, I would have to use the user32.dll library.
I have found some code online that attempts something similar (no window, no taskbar icon), and I am trying to tweak it so that it fits my needs, but so far I am stuck.
This is what I have:
private int GWL_STYLE = -16;
private int GWL_EXSTYLE = -20;
private int WS_EX_APPWINDOW = 262144;
private UInt32 WS_POPUP = 0x80000000;
private UInt32 WS_CHILD = 0x40000000;
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
public Process HideProcess(string executablesPath, System.Windows.Forms.Form parentForm) {
Process valueToReturn = null;
if (executablesPath != null && executablesPath.Equals(String.Empty) == false && File.Exists(executablesPath) == true) {
ProcessStartInfo startInfo = new ProcessStartInfo(executablesPath);
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
valueToReturn = Process.Start(startInfo);
int style = GetWindowLong(valueToReturn.MainWindowHandle, GWL_EXSTYLE) & ~WS_EX_APPWINDOW;
SetWindowLong(valueToReturn.MainWindowHandle, GWL_EXSTYLE, style);
SetParent(valueToReturn.MainWindowHandle, parentForm.Handle);
}
return valueToReturn;
}
I have tried several different variations of this method, but I always get close but not exactly where I'd like to be.
The documentation I have found seems a little bit confusing, so I am a little bit stuck and would appreciate some help.
How can I show/hide the desktop icons programmatically, using C#?
I'm trying to create an alternative desktop, which uses widgets, and I need to hide the old icons.
You can do this using the Windows API. Here is sample code in C# that will toggle desktop icons.
[DllImport("user32.dll", SetLastError = true)] static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)] static extern IntPtr GetWindow(IntPtr hWnd, GetWindow_Cmd uCmd);
enum GetWindow_Cmd : uint
{
GW_HWNDFIRST = 0,
GW_HWNDLAST = 1,
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
GW_OWNER = 4,
GW_CHILD = 5,
GW_ENABLEDPOPUP = 6
}
[DllImport("user32.dll", CharSet = CharSet.Auto)] static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private const int WM_COMMAND = 0x111;
static void ToggleDesktopIcons()
{
var toggleDesktopCommand = new IntPtr(0x7402);
IntPtr hWnd = GetWindow(FindWindow("Progman", "Program Manager"), GetWindow_Cmd.GW_CHILD);
SendMessage(hWnd, WM_COMMAND, toggleDesktopCommand, IntPtr.Zero);
}
This sends a message to the SHELLDLL_DefView child window of Progman, which tells it to toggle visibility (by adding or removing the WS_VISIBLE style) of it's only child, "FolderView". "FolderView" is the actual window that contains the icons.
To test to see if icons are visible or not, you can query for the WS_VISIBLE style by using the GetWindowInfo function, shown below:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
private static extern bool GetWindowInfo(IntPtr hwnd, ref WINDOWINFO pwi);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
private int _Left;
private int _Top;
private int _Right;
private int _Bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct WINDOWINFO
{
public uint cbSize;
public RECT rcWindow;
public RECT rcClient;
public uint dwStyle;
public uint dwExStyle;
public uint dwWindowStatus;
public uint cxWindowBorders;
public uint cyWindowBorders;
public ushort atomWindowType;
public ushort wCreatorVersion;
public WINDOWINFO(Boolean? filler)
: this() // Allows automatic initialization of "cbSize" with "new WINDOWINFO(null/true/false)".
{
cbSize = (UInt32)(Marshal.SizeOf(typeof(WINDOWINFO)));
}
}
Here is a function that calls the above code and returns true if the window is visible, false if not.
static bool IsVisible()
{
IntPtr hWnd = GetWindow(GetWindow(FindWindow("Progman", "Program Manager"), GetWindow_Cmd.GW_CHILD), GetWindow_Cmd.GW_CHILD);
WINDOWINFO info = new WINDOWINFO();
info.cbSize = (uint)Marshal.SizeOf(info);
GetWindowInfo(hWnd, ref info);
return (info.dwStyle & 0x10000000) == 0x10000000;
}
The windows API code along with more information about the window styles can be found here: http://www.pinvoke.net/default.aspx/user32/GetWindowInfo.html
Even though this is quite old when I tried Ondrej Balas's answer, one problem I found with this solution is that it does not work if the ToggleDesktop command is used to show the desktop ( also if wallpaper rotation is enabled ).
In both of these cases the SHELLDLL_DefView window, which is the recipient of the toggleDesktopCommand in the ToggleDesktopIcons function, is not a child of the "Program manager" window but of a 'WorkerW" window. (see WinApi - How to obtain SHELLDLL_DefView and Windows Desktop ListView Handle.
Based on those and building upon Ondrej Balas's earlier answer change the ToggleDesktopIcons function to be :
static void ToggleDesktopIcons()
{
var toggleDesktopCommand = new IntPtr(0x7402);
SendMessage(GetDesktopSHELLDLL_DefView(), WM_COMMAND, toggleDesktopCommand, IntPtr.Zero);
}
And add a GetDesktopSHELLDLL_DefView function:
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
[DllImport("user32.dll", SetLastError = false)]
static extern IntPtr GetDesktopWindow();
static IntPtr GetDesktopSHELLDLL_DefView()
{
var hShellViewWin = IntPtr.Zero;
var hWorkerW = IntPtr.Zero;
var hProgman = FindWindow("Progman", "Program Manager");
var hDesktopWnd = GetDesktopWindow();
// If the main Program Manager window is found
if (hProgman != IntPtr.Zero)
{
// Get and load the main List view window containing the icons.
hShellViewWin = FindWindowEx(hProgman, IntPtr.Zero, "SHELLDLL_DefView", null);
if (hShellViewWin == IntPtr.Zero)
{
// When this fails (picture rotation is turned ON, toggledesktop shell cmd used ), then look for the WorkerW windows list to get the
// correct desktop list handle.
// As there can be multiple WorkerW windows, iterate through all to get the correct one
do
{
hWorkerW = FindWindowEx(hDesktopWnd, hWorkerW, "WorkerW", null);
hShellViewWin = FindWindowEx(hWorkerW, IntPtr.Zero, "SHELLDLL_DefView", null);
} while (hShellViewWin == IntPtr.Zero && hWorkerW != IntPtr.Zero);
}
}
return hShellViewWin;
}
Now regardless of the desktop toggle or wallpaper rotation the ToggleDesktopIcons should always work.
For reference this is my toggle desktop function which caused the issue with the original ToggleDesktopIcons function
static public void ToggleDesktop(object sender, EventArgs e)
{
var shellObject = new Shell32.Shell();
shellObject.ToggleDesktop();
}
In response to James M, this function returns the current state:
bool IconsVisible()
{
var hWnd = GetDesktopListView();
var info = new User32.WINDOWINFO(null);
User32.GetWindowInfo(hWnd, ref info);
return (info.dwStyle & User32.WindowStyle.WS_VISIBLE) == User32.WindowStyle.WS_VISIBLE;
}
A different approach is to create a separate desktop and show it instead. It will not have icons.
Application running itself on a separate desktop
You can do this in RegEdit
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced
change HideIcons to 1
static void HideIcons()
{
RegistryKey myKey = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced", true);
if (myKey != null)
{
myKey.SetValue("HideIcons", 1);
myKey.Close();
}
}
Use the Registry class as described here.
http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.aspx
You can create a full screen view application and make it the top most window.
Then make your application to be start up with windows.
You are going about this the wrong way. What you are really trying to do is to replace the shell. Windows provides for this so you should just take advantage of it. Write your own shell to replace explorer.
Nice topic. Without actually creating a different desktop it would be visually pleasant to have the running applications minimized in the same swoop.
I have a Windows application. Where can I find the handle of the window?
I am unable to find the handle of the controls placed within that window. How can I scrape such controls? If it's a textbox or a button, I can automate it using the move position and send message. How does this work with a data grid/ table?
You have to go to the Win32 API for that.
Import EnumChildWindows like this:
[DllImport("user32.dll")]
private static extern bool EnumChildWindows(Intptr parent, EnumWindowsProc enumProc, IntPtr lParam);
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
And then call it from you main windows with this.Handle as the parent's windows handle. It will call the delegate for every child control in the form. This will show a messagebox for every control in the window:
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
private static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
EnumChildWindows(this.Handle, (handle, b) => {
int length = GetWindowTextLength(handle);
System.Text.StringBuilder sb = new System.Text.StringBuilder(length + 1);
GetWindowText(handle, sb, sb.Capacity);
MessageBox.Show(sb.ToString()); return true;
}, IntPtr.Zero);
How can I show/hide the desktop icons programmatically, using C#?
I'm trying to create an alternative desktop, which uses widgets, and I need to hide the old icons.
You can do this using the Windows API. Here is sample code in C# that will toggle desktop icons.
[DllImport("user32.dll", SetLastError = true)] static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)] static extern IntPtr GetWindow(IntPtr hWnd, GetWindow_Cmd uCmd);
enum GetWindow_Cmd : uint
{
GW_HWNDFIRST = 0,
GW_HWNDLAST = 1,
GW_HWNDNEXT = 2,
GW_HWNDPREV = 3,
GW_OWNER = 4,
GW_CHILD = 5,
GW_ENABLEDPOPUP = 6
}
[DllImport("user32.dll", CharSet = CharSet.Auto)] static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private const int WM_COMMAND = 0x111;
static void ToggleDesktopIcons()
{
var toggleDesktopCommand = new IntPtr(0x7402);
IntPtr hWnd = GetWindow(FindWindow("Progman", "Program Manager"), GetWindow_Cmd.GW_CHILD);
SendMessage(hWnd, WM_COMMAND, toggleDesktopCommand, IntPtr.Zero);
}
This sends a message to the SHELLDLL_DefView child window of Progman, which tells it to toggle visibility (by adding or removing the WS_VISIBLE style) of it's only child, "FolderView". "FolderView" is the actual window that contains the icons.
To test to see if icons are visible or not, you can query for the WS_VISIBLE style by using the GetWindowInfo function, shown below:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
private static extern bool GetWindowInfo(IntPtr hwnd, ref WINDOWINFO pwi);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
private int _Left;
private int _Top;
private int _Right;
private int _Bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct WINDOWINFO
{
public uint cbSize;
public RECT rcWindow;
public RECT rcClient;
public uint dwStyle;
public uint dwExStyle;
public uint dwWindowStatus;
public uint cxWindowBorders;
public uint cyWindowBorders;
public ushort atomWindowType;
public ushort wCreatorVersion;
public WINDOWINFO(Boolean? filler)
: this() // Allows automatic initialization of "cbSize" with "new WINDOWINFO(null/true/false)".
{
cbSize = (UInt32)(Marshal.SizeOf(typeof(WINDOWINFO)));
}
}
Here is a function that calls the above code and returns true if the window is visible, false if not.
static bool IsVisible()
{
IntPtr hWnd = GetWindow(GetWindow(FindWindow("Progman", "Program Manager"), GetWindow_Cmd.GW_CHILD), GetWindow_Cmd.GW_CHILD);
WINDOWINFO info = new WINDOWINFO();
info.cbSize = (uint)Marshal.SizeOf(info);
GetWindowInfo(hWnd, ref info);
return (info.dwStyle & 0x10000000) == 0x10000000;
}
The windows API code along with more information about the window styles can be found here: http://www.pinvoke.net/default.aspx/user32/GetWindowInfo.html
Even though this is quite old when I tried Ondrej Balas's answer, one problem I found with this solution is that it does not work if the ToggleDesktop command is used to show the desktop ( also if wallpaper rotation is enabled ).
In both of these cases the SHELLDLL_DefView window, which is the recipient of the toggleDesktopCommand in the ToggleDesktopIcons function, is not a child of the "Program manager" window but of a 'WorkerW" window. (see WinApi - How to obtain SHELLDLL_DefView and Windows Desktop ListView Handle.
Based on those and building upon Ondrej Balas's earlier answer change the ToggleDesktopIcons function to be :
static void ToggleDesktopIcons()
{
var toggleDesktopCommand = new IntPtr(0x7402);
SendMessage(GetDesktopSHELLDLL_DefView(), WM_COMMAND, toggleDesktopCommand, IntPtr.Zero);
}
And add a GetDesktopSHELLDLL_DefView function:
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
[DllImport("user32.dll", SetLastError = false)]
static extern IntPtr GetDesktopWindow();
static IntPtr GetDesktopSHELLDLL_DefView()
{
var hShellViewWin = IntPtr.Zero;
var hWorkerW = IntPtr.Zero;
var hProgman = FindWindow("Progman", "Program Manager");
var hDesktopWnd = GetDesktopWindow();
// If the main Program Manager window is found
if (hProgman != IntPtr.Zero)
{
// Get and load the main List view window containing the icons.
hShellViewWin = FindWindowEx(hProgman, IntPtr.Zero, "SHELLDLL_DefView", null);
if (hShellViewWin == IntPtr.Zero)
{
// When this fails (picture rotation is turned ON, toggledesktop shell cmd used ), then look for the WorkerW windows list to get the
// correct desktop list handle.
// As there can be multiple WorkerW windows, iterate through all to get the correct one
do
{
hWorkerW = FindWindowEx(hDesktopWnd, hWorkerW, "WorkerW", null);
hShellViewWin = FindWindowEx(hWorkerW, IntPtr.Zero, "SHELLDLL_DefView", null);
} while (hShellViewWin == IntPtr.Zero && hWorkerW != IntPtr.Zero);
}
}
return hShellViewWin;
}
Now regardless of the desktop toggle or wallpaper rotation the ToggleDesktopIcons should always work.
For reference this is my toggle desktop function which caused the issue with the original ToggleDesktopIcons function
static public void ToggleDesktop(object sender, EventArgs e)
{
var shellObject = new Shell32.Shell();
shellObject.ToggleDesktop();
}
In response to James M, this function returns the current state:
bool IconsVisible()
{
var hWnd = GetDesktopListView();
var info = new User32.WINDOWINFO(null);
User32.GetWindowInfo(hWnd, ref info);
return (info.dwStyle & User32.WindowStyle.WS_VISIBLE) == User32.WindowStyle.WS_VISIBLE;
}
A different approach is to create a separate desktop and show it instead. It will not have icons.
Application running itself on a separate desktop
You can do this in RegEdit
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced
change HideIcons to 1
static void HideIcons()
{
RegistryKey myKey = Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced", true);
if (myKey != null)
{
myKey.SetValue("HideIcons", 1);
myKey.Close();
}
}
Use the Registry class as described here.
http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.aspx
You can create a full screen view application and make it the top most window.
Then make your application to be start up with windows.
You are going about this the wrong way. What you are really trying to do is to replace the shell. Windows provides for this so you should just take advantage of it. Write your own shell to replace explorer.
Nice topic. Without actually creating a different desktop it would be visually pleasant to have the running applications minimized in the same swoop.
How to read the highlighted/Selected Text from any window using c#.
i tried 2 approaches.
Send "^c" whenever user selects some thing. But in this case my clipboard is flooded with lots of unnecessary data. Sometime it copied passwords also.
so i switched my approach to 2nd method, send message method.
see this sample code
[DllImport("user32.dll")]
static extern int GetFocus();
[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(int hWnd, int ProcessId);
[DllImport("user32.dll") ]
static extern int GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
static extern int SendMessage(int hWnd, int Msg, int wParam, StringBuilder lParam);
// second overload of SendMessage
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, uint Msg, out int wParam, out int lParam);
const int WM_SETTEXT = 12;
const int WM_GETTEXT = 13;
private string PerformCopy()
{
try
{
//Wait 5 seconds to give us a chance to give focus to some edit window,
//notepad for example
System.Threading.Thread.Sleep(5000);
StringBuilder builder = new StringBuilder(500);
int foregroundWindowHandle = GetForegroundWindow();
uint remoteThreadId = GetWindowThreadProcessId(foregroundWindowHandle, 0);
uint currentThreadId = GetCurrentThreadId();
//AttachTrheadInput is needed so we can get the handle of a focused window in another app
AttachThreadInput(remoteThreadId, currentThreadId, true);
//Get the handle of a focused window
int focused = GetFocus();
//Now detach since we got the focused handle
AttachThreadInput(remoteThreadId, currentThreadId, false);
//Get the text from the active window into the stringbuilder
SendMessage(focused, WM_GETTEXT, builder.Capacity, builder);
return builder.ToString();
}
catch (System.Exception oException)
{
throw oException;
}
}
this code working fine in Notepad. But if i try to capture from another applications like Mozilla firefox, or Visual Studio IDE, it's not returning the text.
Can anybody please help me, where i am doing wrong? First of all, i have chosen the right approach?
That's because both Firefox and Visual Studio don't use the built-in Win32 controls for displaying/editing text.
It is not possible in general to be able to get the value of "any" selected text, because of the fact that programs can re-implement their own version of the Win32 controls any way they see fit, and your program cannot possibly expect to work with all of them.
However, you can use the UI Automation APIs which will allow you to interact with the majority of 3rd-party controls (at least, all the good ones - such as Visual Studio and Firefox - will likely work with the UI Automation APIs since it's a requirement for accessibility)