Ken*_*enD 7 c# macos .net-core
在 Windows 上获取 .Net Framework 中打开的窗口列表相对容易。如何在 macOS 上的 .Net Core/.Net 5 或更高版本中执行相同操作?
为了澄清这一点,我正在寻找一种方法来检索任何正在运行的应用程序/进程拥有的所有打开的窗口的列表。我没有太多 macOS 开发经验 - 我是一名 Windows 开发人员 - 但我尝试使用此答案NSApplication建议的方法。
我在 macOS Monterey (12.2) 上的 VS2022 中创建了一个 .Net 6.0 控制台应用程序,添加了对 和 的引用,Xamarin.Mac如下libxammac.dylib所述-描述了在 Xamarin 而不是 .Net 中执行此操作,但我没有看到任何其他选项来创建控制台应用。用简单的代码:
static void Main(string[] args)
{
NSApplication.Init();
}
Run Code Online (Sandbox Code Playgroud)
我得到输出
Xamarin.Mac:dlopen错误:dlsym(RTLD_DEFAULT,mono_get_runtime_build_info):找不到符号
我不知道这意味着什么。我什至不确定这种方法有什么优点。
有谁知道是否可以从 .Net Core/6.0 应用程序使用 NSApplication,如果可以,NSApplication 是否能让我读取系统范围内打开窗口的列表?如果没有,还有其他方法可以实现这一点吗?
这仅供我自己的内部使用,它不需要在我自己的环境之外以任何方式便携或稳定。
在您引用的链接中,有一个重要说明:
...由于 Xamarin.Mac.dll 不在 .NET Core 运行时下运行,它仅在 Mono 运行时运行。
因为您尝试Xamarin.Mac.dll在 下运行.net-core,所以会收到此dlopen错误。
没有通过 NSApplication 的系统范围列表
如果您想读取打开窗口的系统范围列表,则与 NSApplication.shared.windows 的链接答案是不正确的。它只能用于确定发出调用的应用程序的所有当前现有窗口,请参阅Apple 的文档。
替代解决方案
尽管如此,有多种方法可以访问 macOS 中的窗口信息。其中之一可能是一个小型的非托管 C-lib,它通过 CoreFoundation 和 CoreGraphics 获取必要的信息,并通过 Platform Invoke (P/Invoke) 将其返回到 C#。
本机代码
下面是 C-Lib 的示例代码,用于确定并返回窗口所有者的名称。
Windows列表库.h
extern char const **windowList(void);
extern void freeWindowList(char const **list);
Run Code Online (Sandbox Code Playgroud)
该库的接口仅包含两个函数。调用的第一个函数windowList返回一个包含窗口所有者名称的列表。列表的最后一个元素必须为 NULL,以便您可以检测列表在托管 C# 端的结束位置。由于字符串列表的内存是动态分配的,因此必须freeWindowList在处理后使用该函数释放关联的内存。
WindowsListLib.c
#include "WindowListLib.h"
#include <CoreFoundation/CoreFoundation.h>
#include <CoreGraphics/CoreGraphics.h>
static void errorExit(char *msg) {
fprintf(stderr, "%s\n", msg);
exit(1);
}
static char *copyUTF8String(CFStringRef string) {
CFIndex length = CFStringGetLength(string);
CFIndex size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
char *buf = malloc(size);
if(!buf) {
errorExit("malloc failed");
}
if(!CFStringGetCString(string, buf, size, kCFStringEncodingUTF8)) {
errorExit("copyUTF8String with utf8 encoding failed");
}
return buf;
}
char const **windowList(void) {
CFArrayRef cfWindowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
CFIndex count = CFArrayGetCount(cfWindowList);
char const **list = malloc(sizeof(char *) * (count + 1));
if(!list) {
errorExit("malloc failed");
}
list[count] = NULL;
for(CFIndex i = 0; i < count; i++) {
CFDictionaryRef windowInfo = CFArrayGetValueAtIndex(cfWindowList, i);
CFStringRef name = CFDictionaryGetValue(windowInfo, kCGWindowOwnerName);
if(name) {
list[i] = copyUTF8String(name);
} else {
list[i] = strdup("unknown");
}
}
CFRelease(cfWindowList);
return list;
}
void freeWindowList(char const **list) {
const char **ptr = list;
while(*ptr++) {
free((void *)*ptr);
}
free(list);
}
Run Code Online (Sandbox Code Playgroud)
CGWindowListCopyWindowInfo是获取窗口信息的实际函数。它返回包含详细信息的字典列表。我们从中提取kCGWindowOwnerName. CFStringRef函数将其转换为动态分配的 UTF-8 字符串copyUTF8String。
CGWindowListCopyWindowInfo按照惯例,包含单词copy(或)的调用create必须在使用 with 后释放CFRelease,以避免造成内存泄漏。
C# 代码
然后可以在 C# 端调用整个过程,如下所示:
using System.Runtime.InteropServices;
namespace WindowList
{
public static class Program
{
[DllImport("WindowListLib", EntryPoint = "windowList")]
private static extern IntPtr WindowList();
[DllImport("WindowListLib", EntryPoint = "freeWindowList")]
private static extern void FreeWindowList(IntPtr list);
private static List<string> GetWindows()
{
var nativeWindowList = WindowList();
var windows = new List<string>();
var nativeWindowPtr = nativeWindowList;
string? windowName;
do
{
var strPtr = Marshal.ReadIntPtr(nativeWindowPtr);
windowName = Marshal.PtrToStringUTF8(strPtr);
if (windowName == null) continue;
windows.Add(windowName);
nativeWindowPtr += Marshal.SizeOf(typeof(IntPtr));
} while (windowName != null);
FreeWindowList(nativeWindowList);
return windows;
}
static void Main()
{
foreach (var winName in GetWindows())
{
Console.WriteLine(winName);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
该GetWindows方法通过本机调用获取数据WindowList并将 C 字符串转换为托管字符串,然后通过调用释放本机资源FreeWindowList。
该函数仅返回所有者名称,例如Finder、Xcode、Safari等。如果有多个窗口,所有者也会被多次返回等。具体应该确定的逻辑可能需要根据根据您的要求。然而,上面的代码至少应该展示一种可能的方法来实现这一点。
截屏
| 归档时间: |
|
| 查看次数: |
663 次 |
| 最近记录: |