C# WPF使用Windows临时路径调用Process.Start返回null的问题解决
在Windows 10 Enterprise系统中,以Administrator身份用C# WPF开发程序,程序从SQL Server读取二进制文件写入Windows临时路径,比如把数据库里的图片用File.WriteAllBytes写入临时目录后,调用Process.Start启动Windows照片查看器打开该图片。
为监控进程状态、知晓窗口何时关闭,调用MyProcess.WaitForExit()时触发System.NullReferenceException异常——MyProcess对象为null,但文件已成功打开。
测试代码
string TempyPath = @"C:\Users\ADMINI~1\AppData\Local\Temp\tmpDCD8.2e0ea3b3-5b93-40f0-a227-40c85fd0a06b.png"; // 返回null的Process路径 string NormalPath = @"C:\tmpFB0A.5e2525f3-d4ef-4eb8-a920-c5c5b5d8402f.png"; //Test1 var MyProcess0 = Process.Start(new ProcessStartInfo() { FileName = TempyPath, WindowStyle = ProcessWindowStyle.Maximized, UseShellExecute = true }); //Test2 var MyProcess = Process.Start(TempyPath); MyProcess.WaitForExit(); if (MyProcess.HasExited) { MessageBox.Show("You Cloes the Microsoft.Photos"); }
错误信息
System.NullReferenceException: 'Object reference not set to an instance of an object.' MyProcess was null.
原因分析
当使用Process.Start(string fileName)或UseShellExecute=true的ProcessStartInfo启动文件时,如果系统复用了已在后台运行的关联进程(比如Microsoft Photos已经启动),Process.Start会返回null——因为没有创建新进程,只是让已有进程打开了目标文件。临时目录的文件更容易触发这种复用逻辑,而普通目录的文件可能不会。
解决方案
方法1:直接指定Photos程序路径启动,避免进程复用
找到Microsoft.Photos.exe的安装路径(通常位于C:\Program Files\WindowsApps\Microsoft.Windows.Photos_*_x64__8wekyb3d8bbwe,版本号会随系统更新变化,建议通过注册表或Windows API动态获取),启动时明确指定程序路径和图片路径作为参数:
// 替换为实际的Photos程序路径 string photoExePath = @"C:\Program Files\WindowsApps\Microsoft.Windows.Photos_2024.11100.2001.0_x64__8wekyb3d8bbwe\Microsoft.Photos.exe"; string tempImagePath = @"C:\Users\ADMINI~1\AppData\Local\Temp\tmpDCD8.2e0ea3b3-5b93-40f0-a227-40c85fd0a06b.png"; var process = Process.Start(new ProcessStartInfo { FileName = photoExePath, Arguments = $"\"{tempImagePath}\"", UseShellExecute = false }); // 先判断process是否为null,再调用WaitForExit if (process != null) { process.WaitForExit(); if (process.HasExited) { MessageBox.Show("You Closed the Microsoft.Photos"); } }
注意:WindowsApps目录权限限制严格,管理员身份运行时可能需要调整权限,或者将UseShellExecute设为true。
方法2:通过窗口句柄追踪关闭事件
如果无法固定Photos程序路径,可以通过Windows API枚举窗口,找到对应图片的Photos窗口,监控其关闭事件:
- 引入Windows API声明:
using System.Runtime.InteropServices; using System.Threading; [DllImport("user32.dll")] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32.dll")] private static extern bool GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
- 打开文件后查找窗口并等待:
string tempImagePath = @"C:\Users\ADMINI~1\AppData\Local\Temp\tmpDCD8.2e0ea3b3-5b93-40f0-a227-40c85fd0a06b.png"; // 打开图片 Process.Start(tempImagePath); // 等待窗口创建,需根据实际情况调整延迟时间 Thread.Sleep(1000); // 图片文件名作为窗口标题的一部分 string windowTitle = Path.GetFileName(tempImagePath); IntPtr hWnd = FindWindow(null, windowTitle); if (hWnd != IntPtr.Zero) { GetWindowThreadProcessId(hWnd, out uint processId); Process targetProcess = Process.GetProcessById((int)processId); targetProcess.WaitForExit(); MessageBox.Show("You Closed the Microsoft.Photos"); }
注意:这种方法依赖窗口标题,若存在多个相同文件名的窗口,或Photos修改了窗口标题格式,可能出现不准确的情况。
额外注意事项
- 临时文件路径建议用
Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".png")生成,避免手动拼接路径出错。 - 在WPF中调用
WaitForExit()会阻塞当前UI线程,导致界面冻结,建议放在后台线程执行,比如用Task.Run:
Task.Run(() => { string photoExePath = @"C:\Program Files\WindowsApps\Microsoft.Windows.Photos_2024.11100.2001.0_x64__8wekyb3d8bbwe\Microsoft.Photos.exe"; string tempImagePath = @"C:\Users\ADMINI~1\AppData\Local\Temp\tmpDCD8.2e0ea3b3-5b93-40f0-a227-40c85fd0a06b.png"; var process = Process.Start(new ProcessStartInfo { FileName = photoExePath, Arguments = $"\"{tempImagePath}\"", UseShellExecute = false }); if (process != null) { process.WaitForExit(); // 回到UI线程显示消息 Application.Current.Dispatcher.Invoke(() => { MessageBox.Show("You Closed the Microsoft.Photos"); }); } });
内容的提问来源于stack exchange,提问作者Mojtabah

