Dart FFI调用FindWindowA返回0无法获取文件资源管理器窗口句柄
问题描述
以下Dart代码预期获取文件资源管理器的窗口句柄,但在文件资源管理器窗口正常打开的情况下,函数始终返回0:
import 'package:ffi/ffi.dart'; import 'dart:ffi'; void main() { DynamicLibrary dl = DynamicLibrary.open('user32.dll'); final func = dl.lookupFunction< IntPtr Function(Pointer<Utf16>?, Pointer<Utf16>?), int Function(Pointer<Utf16>?, Pointer<Utf16>?) >('FindWindowA'); print(func(nullptr, 'File Explorer'.toNativeUtf16())); }
已验证的现象:
- C++程序中调用
FindWindowA,传入参数NULL和'File Explorer'时可正常返回预期句柄 - Dart环境中直接给第一个参数传
null会抛出错误Invalid argument(s): argument value for ':ffi_param1' is null,因此代码中使用nullptr作为第一个入参
根本原因
核心问题是调用的Win32 API版本和传入的字符串编码不匹配:
- Win32窗口查找API分两个版本:后缀为
A的FindWindowA是ANSI版本,要求传入单字节ANSI编码的字符串;后缀为W的FindWindowW是宽字符版本,要求传入UTF-16(双字节)编码的字符串。 - 代码中通过
toNativeUtf16()生成的是UTF-16编码的字符串指针,却传给了只接受ANSI字符串的FindWindowA,API解析传入的字符串时识别不到正确的窗口标题,自然查找失败返回0。 - 第一个参数使用
nullptr的写法是正确的,不存在问题。
解决方案
优先选择使用FindWindowW宽字符版本,匹配UTF-16编码的字符串,这也是现代Windows系统原生支持的API版本,不需要内部做编码转换,不会出现非英文标题乱码问题,性能更好。
修正后的可运行代码如下:
import 'package:ffi/ffi.dart'; import 'dart:ffi'; void main() { DynamicLibrary dl = DynamicLibrary.open('user32.dll'); // 查找宽字符版本的FindWindowW函数 final findWindow = dl.lookupFunction< IntPtr Function(Pointer<Utf16>?, Pointer<Utf16>?), int Function(Pointer<Utf16>?, Pointer<Utf16>?) >('FindWindowW'); // 分配native内存存储UTF-16编码的窗口标题 final windowTitle = 'File Explorer'.toNativeUtf16(); // 传入nullptr代表不限制窗口类,匹配标题为File Explorer的窗口 final hwnd = findWindow(nullptr, windowTitle); print(hwnd); // 手动释放native堆上分配的字符串内存,避免内存泄漏 calloc.free(windowTitle); }
如果一定要使用FindWindowA,需要将传入的字符串转为ANSI编码的native指针,不推荐这种写法,遇到中文等非ASCII字符的窗口标题时很容易出现乱码导致查找失败。
内容的提问来源于stack exchange,提问作者Sanmay Kant
相关产品推荐
相关产品推荐

