如果我理解正确,这里的问题是您正在复制像素值(调色板索引)而不是调色板本身。我没有找到使用纯 C# 复制调色板的方法,但是使用 P/Invoke 和 DirectX 的替代方案......因为两者都添加了对 Windows 的依赖项,所以我选择了 P/Invoke,因为它更容易而且不会依赖于 DirectX。
我有两种方法可以提供给你...
第一种方法:
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
private static extern IntPtr SelectPalette(
IntPtr hdc,
IntPtr htPalette,
bool bForceBackground);
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
private static extern int RealizePalette(IntPtr hdc);
private void ScreenShot()
{
IntPtr htPalette = Graphics.GetHalftonePalette();
using (var screenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (var graphics = Graphics.FromImage(screenshot))
{
IntPtr hdc = graphics.GetHdc();
SelectPalette(hdc, htPalette, true);
RealizePalette(hdc);
graphics.ReleaseHdc(hdc);
graphics.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size);
}
screenshot.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);
}
}
此方法取决于GetHalftonePalette
为您提供正确的调色板。我从来没有用调色板测试过(我太年轻了)......所以我决定编写第二种方法,基于 Windows 应该在某些情况下自动处理调色板。注意:不确定是否将对 CopyFromScreen 的调用移动到 using 块的开头更好,或者忽略对 RealizePalette 的调用(我太年轻了)。
第二种方法:
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
private void ScreenShot()
{
int width = Screen.PrimaryScreen.Bounds.Width;
int height = Screen.PrimaryScreen.Bounds.Height;
using (var screenshot = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
using (var fromHwnd = Graphics.FromHwnd(new IntPtr(0)))
using (var graphics = Graphics.FromImage(screenshot))
{
IntPtr hdc_screen = fromHwnd.GetHdc();
IntPtr hdc_screenshot = graphics.GetHdc();
BitBlt(hdc_screenshot, 0, 0, width, height, hdc_screen, 0, 0, 0x00CC0020); /*SRCCOPY = 0x00CC0020*/
graphics.ReleaseHdc(hdc_screenshot);
fromHwnd.ReleaseHdc(hdc_screen);
}
screenshot.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);
}
}
这两种方法都适用于正常条件。请进行您的测试,您可能需要通过添加复制原始调色板来执行第二种方法(即...当 Windows 不自动处理它们时),但我不确定如何这样做(我太年轻)并希望这种方法有效。
最后,如果您要承担额外的依赖并希望它与 Windows 不同,也许 GTK#(有一个 Windows 可用的版本)是一个不错的选择。请参阅如何使用 Mono C# 截屏?因为它解决了类似的问题,并提供了 GTK# 的示例代码。
注意:
以下代码在这里似乎运行良好,您是否通过此代码得到任何异常?
System.Drawing.Image image = Clipboard.GetImage();
image.Save("screenshot.png", System.Drawing.Imaging.ImageFormat.Png);