Как программно отобразить/скрыть значки на рабочем столе, используя С#?
Я пытаюсь создать альтернативный рабочий стол, который использует виджеты, и мне нужно скрыть старые значки.
Как программно отобразить/скрыть значки на рабочем столе, используя С#?
Я пытаюсь создать альтернативный рабочий стол, который использует виджеты, и мне нужно скрыть старые значки.
Вы можете сделать это, используя Windows API. Вот пример кода на С#, который будет переключать значки рабочего стола.
[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);
}
Это отправляет сообщение в дочернее окно SHELLDLL_DefView Progman, которое сообщает ему переключать видимость (путем добавления или удаления стиля WS_VISIBLE) только дочернего, "FolderView". "FolderView" - это фактическое окно, содержащее значки.
Чтобы проверить, видны ли значки или нет, вы можете запросить стиль WS_VISIBLE с помощью функции GetWindowInfo, показанной ниже:
[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)));
}
}
Вот функция, вызывающая вышеуказанный код и возвращающая значение true, если окно видимо, false, если нет.
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;
}
Код API Windows, а также дополнительную информацию о стилях окна можно найти здесь: http://www.pinvoke.net/default.aspx/user32/GetWindowInfo.html
Другой подход - создать отдельный рабочий стол и показать его. У него не будет значков.
Вы можете создать приложение с полным экраном и сделать его самым большим окном.
Затем заставьте приложение запускаться с окнами.
Вы можете сделать это в RegEdit HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced изменить HideIcons на 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();
}
}
Используйте класс реестра, как описано здесь.
http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.aspx
Вы делаете это неправильно. То, что вы действительно пытаетесь сделать, это заменить оболочку. Windows обеспечивает это, поэтому вы должны просто воспользоваться им. Напишите свою собственную оболочку для замены проводника.
Существует официальный API для подобных действий, он называется API-интерфейсом оболочки. Это должно быть предпочтительнее, чем манипулирование реестром и подход FindWindow
/SendMessage
, как очень хрупкие "решения", которые, как правило, ломаются с будущими версиями Windows (для последних это уже было подтверждено комментаторами).
Шаги:
IFolderView2
на рабочем столе (поддерживается с Windows Vista).IFolderView2::SetCurrentFolderFlags()
с FWF_NOICONS
для параметров dwMask
и dwFlags
.Эффект флага отображается сразу. Нет необходимости перезагружать компьютер или "explorer.exe". Флаг также сохраняется после выхода из системы или перезагрузки.
Трудная вещь - шаг 1). Раймонд Чен показывает код C++ для этого в своей статье "Манипулирование положениями значков рабочего стола", в частности, в его функции FindDesktopFolderView()
.
Вот полный пример C++ в виде консольного приложения. Он основан на коде Raymond Chen. Программа переключает видимость значков рабочего стола при каждом запуске.
Код был протестирован под Windows 10 Build 17134.
#include <ShlObj.h> // Shell API
#include <atlcomcli.h> // CComPtr & Co.
#include <string>
#include <iostream>
#include <system_error>
// Throw a std::system_error if the HRESULT indicates failure.
template< typename T >
void ThrowIfFailed( HRESULT hr, T&& msg )
{
if( FAILED( hr ) )
throw std::system_error{ hr, std::system_category(), std::forward<T>( msg ) };
}
// RAII wrapper to initialize/uninitialize COM
struct CComInit
{
CComInit() { ThrowIfFailed( ::CoInitialize( nullptr ), "CoInitialize failed" ); }
~CComInit() { ::CoUninitialize(); }
};
// Query an interface from the desktop shell view.
void FindDesktopFolderView( REFIID riid, void **ppv, std::string const& interfaceName )
{
CComPtr<IShellWindows> spShellWindows;
ThrowIfFailed(
spShellWindows.CoCreateInstance( CLSID_ShellWindows ),
"Failed to create IShellWindows instance" );
CComVariant vtLoc( CSIDL_DESKTOP );
CComVariant vtEmpty;
long lhwnd;
CComPtr<IDispatch> spdisp;
ThrowIfFailed(
spShellWindows->FindWindowSW(
&vtLoc, &vtEmpty, SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp ),
"Failed to find desktop window" );
CComQIPtr<IServiceProvider> spProv( spdisp );
if( ! spProv )
ThrowIfFailed( E_NOINTERFACE, "Failed to get IServiceProvider interface for desktop" );
CComPtr<IShellBrowser> spBrowser;
ThrowIfFailed(
spProv->QueryService( SID_STopLevelBrowser, IID_PPV_ARGS( &spBrowser ) ),
"Failed to get IShellBrowser for desktop" );
CComPtr<IShellView> spView;
ThrowIfFailed(
spBrowser->QueryActiveShellView( &spView ),
"Failed to query IShellView for desktop" );
ThrowIfFailed(
spView->QueryInterface( riid, ppv ),
"Could not query desktop IShellView for interface " + interfaceName );
}
void ToggleDesktopIcons()
{
CComPtr<IFolderView2> spView;
FindDesktopFolderView( IID_PPV_ARGS(&spView), "IFolderView2" );
DWORD flags = 0;
ThrowIfFailed(
spView->GetCurrentFolderFlags( &flags ),
"GetCurrentFolderFlags failed" );
ThrowIfFailed(
spView->SetCurrentFolderFlags( FWF_NOICONS, flags ^ FWF_NOICONS ),
"SetCurrentFolderFlags failed" );
}
int wmain(int argc, wchar_t **argv)
{
try
{
CComInit init;
ToggleDesktopIcons();
std::cout << "Desktop icons have been toggled.\n";
}
catch( std::system_error const& e )
{
std::cout << "ERROR: " << e.what() << ", error code: " << e.code() << "\n";
return 1;
}
return 0;
}
Несмотря на то, что он довольно старый, когда я пытался ответить Ondrej Balas, одна проблема, которую я нашел с этим решением, заключается в том, что оно не работает, если команда ToggleDesktop используется для отображения рабочего стола (также, если вращение обоев включено).
В обоих этих случаях окно SHELLDLL_DefView, которое является получателем функции toggleDesktopCommand в функции ToggleDesktopIcons, является не дочерним элементом окна "Диспетчер программ", а окна "WorkerW" (см. WinApi - Как получить SHELLDLL_DefView и Windows Рабочий стол ListView Handle.
Исходя из этого и основываясь на предыдущем ответе Ондрея Баласа, измените функцию ToggleDesktopIcons на:
static void ToggleDesktopIcons()
{
var toggleDesktopCommand = new IntPtr(0x7402);
SendMessage(GetDesktopSHELLDLL_DefView(), WM_COMMAND, toggleDesktopCommand, IntPtr.Zero);
}
И добавьте функцию GetDesktopSHELLDLL_DefView:
[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;
}
Теперь независимо от переключения рабочего стола или поворота обоев ToggleDesktopIcons должен работать всегда.
Для справки: это моя функция рабочего стола, которая вызвала проблему с оригинальной функцией ToggleDesktopIcons
static public void ToggleDesktop(object sender, EventArgs e)
{
var shellObject = new Shell32.Shell();
shellObject.ToggleDesktop();
}