gpt4 book ai didi

c# - 从桌面应用程序获取 "modern"Windows 应用程序的图标?

转载 作者:可可西里 更新时间:2023-11-01 08:59:12 27 4
gpt4 key购买 nike

我开发了一个函数,它返回给定窗口句柄的窗口图标。看起来像这样。

private static BitmapSource GetWindowIcon(IntPtr windowHandle)
{
var hIcon = default(IntPtr);
hIcon = SendMessage(windowHandle, WM_GETICON, ICON_BIG, IntPtr.Zero);

if (hIcon == IntPtr.Zero)
hIcon = GetClassLongPtr(windowHandle, GCL_HICON);

if (hIcon == IntPtr.Zero)
{
hIcon = LoadIcon(IntPtr.Zero, (IntPtr)0x7F00/*IDI_APPLICATION*/);
}

if (hIcon != IntPtr.Zero)
{
return Imaging.CreateBitmapSourceFromHIcon(hIcon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
} else {
throw new InvalidOperationException("Could not load window icon.");
}
}

我将此函数与 GetForegroundWindow 结合使用以获取事件窗口的图标。

但是,它似乎为通用应用程序生成了同样沉闷的图标。

是否有可能以某种方式从正在运行的通用应用程序中获取图 block 图像或图标?

最佳答案

下面是一些示例代码,演示了如何完成此操作。请注意:

  1. 你应该运行这个升级版,否则你将无法访问包含应用程序资源的文件夹(我想,我并没有真正检查过自己,因为为了调查我授予自己访问该文件夹的权限)。
  2. 现代应用程序在 ApplicationFrameHost 主机进程下运行。您将需要一些技巧才能获得实际的可执行文件(如 Calculator.exe),这些技巧在代码中有注释。
  3. 现代应用程序 list 包含 Logo 路径,但可能有多个 Logo (例如黑色、白色、白色)。您将需要一些逻辑来选择一个。我自己没有对此进行详细调查。
  4. 我在 Windows 10 的计算器应用程序上对此进行了测试,它运行良好。但是,当然需要对更多应用进行更多测试以确保一切正常。

代码如下:

public static class IconHelper {
public static BitmapSource GetForegroundWindowIcon() {
var hwnd = GetForegroundWindow();
uint pid;
GetWindowThreadProcessId(hwnd, out pid);
Process proc = Process.GetProcessById((int) pid);
// modern apps run under ApplicationFrameHost host process in windows 10
// don't forget to check if that is true for windows 8 - maybe they use another host there
if (proc.MainModule.ModuleName == "ApplicationFrameHost.exe") {
// this should be modern app
return GetModernAppLogo(hwnd);
}
return GetWindowIcon(hwnd);
}

public static BitmapSource GetModernAppLogo(IntPtr hwnd) {
// get folder where actual app resides
var exePath = GetModernAppProcessPath(hwnd);
var dir = System.IO.Path.GetDirectoryName(exePath);
var manifestPath = System.IO.Path.Combine(dir, "AppxManifest.xml");
if (File.Exists(manifestPath)) {
// this is manifest file
string pathToLogo;
using (var fs = File.OpenRead(manifestPath)) {
var manifest = XDocument.Load(fs);
const string ns = "http://schemas.microsoft.com/appx/manifest/foundation/windows10";
// rude parsing - take more care here
pathToLogo = manifest.Root.Element(XName.Get("Properties", ns)).Element(XName.Get("Logo", ns)).Value;
}
// now here it is tricky again - there are several files that match logo, for example
// black, white, contrast white. Here we choose first, but you might do differently
string finalLogo = null;
// serach for all files that match file name in Logo element but with any suffix (like "Logo.black.png, Logo.white.png etc)
foreach (var logoFile in Directory.GetFiles(System.IO.Path.Combine(dir, System.IO.Path.GetDirectoryName(pathToLogo)),
System.IO.Path.GetFileNameWithoutExtension(pathToLogo) + "*" + System.IO.Path.GetExtension(pathToLogo))) {
finalLogo = logoFile;
break;
}

if (System.IO.File.Exists(finalLogo)) {
using (var fs = File.OpenRead(finalLogo)) {
var img = new BitmapImage() {
};
img.BeginInit();
img.StreamSource = fs;
img.CacheOption = BitmapCacheOption.OnLoad;
img.EndInit();
return img;
}
}
}
return null;
}

private static string GetModernAppProcessPath(IntPtr hwnd) {
uint pid = 0;
GetWindowThreadProcessId(hwnd, out pid);
// now this is a bit tricky. Modern apps are hosted inside ApplicationFrameHost process, so we need to find
// child window which does NOT belong to this process. This should be the process we need
var children = GetChildWindows(hwnd);
foreach (var childHwnd in children) {
uint childPid = 0;
GetWindowThreadProcessId(childHwnd, out childPid);
if (childPid != pid) {
// here we are
Process childProc = Process.GetProcessById((int) childPid);
return childProc.MainModule.FileName;
}
}

throw new Exception("Cannot find a path to Modern App executable file");
}

public static BitmapSource GetWindowIcon(IntPtr windowHandle) {
var hIcon = default(IntPtr);
hIcon = SendMessage(windowHandle, WM_GETICON, (IntPtr) ICON_BIG, IntPtr.Zero);

if (hIcon == IntPtr.Zero)
hIcon = GetClassLongPtr(windowHandle, GCL_HICON);

if (hIcon == IntPtr.Zero) {
hIcon = LoadIcon(IntPtr.Zero, (IntPtr) 0x7F00 /*IDI_APPLICATION*/);
}

if (hIcon != IntPtr.Zero) {
return Imaging.CreateBitmapSourceFromHIcon(hIcon, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
}
else {
throw new InvalidOperationException("Could not load window icon.");
}
}

#region Helper methods
const UInt32 WM_GETICON = 0x007F;
const int ICON_BIG = 1;
const int GCL_HICON = -14;

private static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}

private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
// You can modify this to check to see if you want to cancel the operation, then return a null here
return true;
}

public delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.Dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr parentHandle, EnumWindowProc callback, IntPtr lParam);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowThreadProcessId(IntPtr handle, out uint processId);

[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

private static IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex)
{
if (IntPtr.Size > 4)
return GetClassLongPtr64(hWnd, nIndex);
else
return new IntPtr(GetClassLongPtr32(hWnd, nIndex));
}

[DllImport("user32.dll", EntryPoint = "GetClassLong")]
public static extern uint GetClassLongPtr32(IntPtr hWnd, int nIndex);

[DllImport("user32.dll", EntryPoint = "GetClassLongPtr")]
public static extern IntPtr GetClassLongPtr64(IntPtr hWnd, int nIndex);
#endregion

}

用法是:

var icon = IconHelper.GetForegroundWindowIcon();

关于c# - 从桌面应用程序获取 "modern"Windows 应用程序的图标?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32122679/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com