如何调用Windows自带画图软件将BMP转换为单色位图?
调用Windows画图软件转换BMP为单色位图的方案
一、核心实现:调用画图程序并自动完成转换
Windows自带的画图程序(mspaint.exe)没有直接的命令行转换参数,需要通过UI模拟操作实现。以下提供两种可行方式:
方式1:SendKeys模拟按键操作(简单但依赖系统环境)
通过启动画图程序,模拟键盘操作完成打开、另存为单色位图的流程,适合快速实现但稳定性受窗口状态、系统语言影响:
using System.Diagnostics; using System.Threading; using System.Windows.Forms; public void ConvertBmpWithPaint(string inputPath, string outputPath) { // 启动画图并打开目标图片 var paintProcess = Process.Start("mspaint.exe", $"\"{inputPath}\""); paintProcess.WaitForInputIdle(); Thread.Sleep(1000); // 等待图片加载完成,可根据实际调整 // 模拟Alt+F打开文件菜单 SendKeys.SendWait("%F"); Thread.Sleep(200); // 选择"另存为" SendKeys.SendWait("S"); Thread.Sleep(200); // 选择"单色位图"(中文系统快捷键为M,英文系统需改为对应按键) SendKeys.SendWait("M"); Thread.Sleep(500); // 输入保存路径 SendKeys.SendWait($"\"{outputPath}\""); Thread.Sleep(200); // 确认保存 SendKeys.SendWait("{ENTER}"); Thread.Sleep(200); // 覆盖已有文件则按Y确认 SendKeys.SendWait("Y"); Thread.Sleep(200); // 关闭画图程序 paintProcess.CloseMainWindow(); paintProcess.WaitForExit(); }
方式2:UI Automation控件定位(更稳定)
通过Windows UI Automation库定位画图程序的控件,模拟点击操作,不依赖按键,适配性更强:
using System; using System.Diagnostics; using System.Threading; using System.Windows.Automation; public void ConvertBmpWithPaintAutomation(string inputPath, string outputPath) { var paintProcess = Process.Start("mspaint.exe", $"\"{inputPath}\""); paintProcess.WaitForInputIdle(); Thread.Sleep(1000); // 获取画图主窗口 var mainWindow = AutomationElement.FromHandle(paintProcess.MainWindowHandle); // 打开文件菜单 var fileMenu = mainWindow.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, "文件")); fileMenu?.GetCurrentPattern(InvokePattern.Pattern)?.Invoke(); Thread.Sleep(200); // 点击"另存为" var saveAsMenu = mainWindow.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, "另存为")); saveAsMenu?.GetCurrentPattern(InvokePattern.Pattern)?.Invoke(); Thread.Sleep(500); // 选择"单色位图"格式 var monoOption = mainWindow.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, "单色位图")); monoOption?.GetCurrentPattern(InvokePattern.Pattern)?.Invoke(); Thread.Sleep(500); // 在保存对话框输入路径并确认 var saveDialog = mainWindow.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.ClassNameProperty, "#32770")); if (saveDialog != null) { // 输入保存路径 var pathInput = saveDialog.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)); (pathInput?.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern)?.SetValue(outputPath); Thread.Sleep(200); // 点击保存按钮 var saveBtn = saveDialog.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, "保存")); saveBtn?.GetCurrentPattern(InvokePattern.Pattern)?.Invoke(); Thread.Sleep(200); // 处理覆盖提示 var overwriteDialog = AutomationElement.FromHandle(paintProcess.MainWindowHandle).FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, "确认另存为")); var yesBtn = overwriteDialog?.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, "是")); yesBtn?.GetCurrentPattern(InvokePattern.Pattern)?.Invoke(); Thread.Sleep(200); } // 关闭画图 paintProcess.CloseMainWindow(); paintProcess.WaitForExit(); }
二、替代方案:修复自定义1bpp转换代码
调用外部程序存在不稳定风险,更优方案是修复你现有代码的格式问题。画图保存的单色位图符合打印机要求的核心原因是:正确的黑白调色板、扫描线字节对齐、标准DPI设置。以下是修复后的转换代码:
using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; public static Bitmap ConvertToValid1Bit(Bitmap input) { int width = input.Width; int height = input.Height; // 创建1bpp索引位图 Bitmap output = new Bitmap(width, height, PixelFormat.Format1bppIndexed); // 设置标准黑白调色板 ColorPalette palette = output.Palette; palette.Entries[0] = Color.Black; palette.Entries[1] = Color.White; output.Palette = palette; // 锁定图像数据 BitmapData inputData = input.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); BitmapData outputData = output.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed); try { int inputStride = inputData.Stride; int outputStride = outputData.Stride; IntPtr inputScan = inputData.Scan0; IntPtr outputScan = outputData.Scan0; // 逐行处理图像 for (int y = 0; y < height; y++) { byte[] inputLine = new byte[inputStride]; byte[] outputLine = new byte[outputStride]; Marshal.Copy(inputScan, inputLine, 0, inputStride); Marshal.Copy(outputScan, outputLine, 0, outputStride); for (int x = 0; x < width; x++) { // 计算灰度值 byte r = inputLine[x * 3 + 2]; byte g = inputLine[x * 3 + 1]; byte b = inputLine[x * 3 + 0]; int gray = (int)(r * 0.299 + g * 0.587 + b * 0.114); // 阈值转黑白,写入对应位 int bitPos = x % 8; int bytePos = x / 8; if (gray > 127) { outputLine[bytePos] |= (byte)(0x80 >> bitPos); } else { outputLine[bytePos] &= (byte)~(0x80 >> bitPos); } } Marshal.Copy(outputLine, 0, outputScan, outputStride); inputScan += inputStride; outputScan += outputStride; } } finally { input.UnlockBits(inputData); output.UnlockBits(outputData); } // 设置打印机兼容的DPI(例如300DPI) output.SetResolution(300, 300); return output; }
内容的提问来源于stack exchange,提问作者Wenbin Geng
相关产品推荐
相关产品推荐

