编辑
2025-10-14
C#
00

屏幕录制现在已经变得非常常见了。不管是做教程视频、游戏直播,还是记录会议内容,一款好用的录屏工具都能让我们轻松抓取画面,并分享重要的瞬间。接下来,本文会一步步教你如何用 C# 和 FFmpeg 打造一个简单却实用的屏幕录制程序。

FFmpeg 是一个强大的开源音频与视频处理工具,支持几乎所有的音视频格式。结合 C# 的界面设计,我们可以快速构建出一个用户友好的录屏应用程序。本文将逐步引导您如何实现这一功能。

环境准备

在开始之前,请确保您已经安装了以下软件:

  1. Visual Studio - 用于 C# 开发的集成开发环境。
  2. FFmpeg - 下载并解压 FFmpeg,记录下其可执行文件的路径。

创建 C# 应用程序

  1. 创建项目:打开 Visual Studio,新建一个 Windows Forms 应用程序项目,命名为 AppRecordScreen
  2. 添加控件:在窗口中添加两个按钮,分别用于“开始录制”和“停止录制”。
编辑
2025-10-14
C#
00

在多媒体处理中,将一系列图片转换为视频是一个常见的需求。本文将详细介绍如何使用 SharpAvi库在 C# 中实现这一功能。

安装必要的 NuGet 包

C#
Install-Package SharpAvi Install-Package System.Drawing.Common

核心实现代码

完整的图片转 AVI 方法

C#
using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using SharpAvi; using SharpAvi.Output; using Image = System.Drawing.Image; namespace AppSharpAvi { public class VideoCreator { /// <summary> /// 将图片序列转换为 AVI 视频 /// </summary> /// <param name="imagePaths">图片路径数组</param> /// <param name="outputPath">输出的 AVI 文件路径</param> /// <param name="frameRate">视频帧率</param> public static void CreateAviFromImages( string[] imagePaths, string outputPath, int frameRate = 30 ) { // 参数有效性检查 if (imagePaths == null || imagePaths.Length == 0) throw new ArgumentException("图片路径数组不能为空"); // 读取第一张图片获取尺寸 using (var firstImage = Image.FromFile(imagePaths[0])) { int width = firstImage.Width; int height = firstImage.Height; // 创建 AVI 写入器 using (var writer = new AviWriter(outputPath) { FramesPerSecond = frameRate, EmitIndex1 = true }) { // 添加视频流 var videoStream = writer.AddVideoStream( width, height, BitsPerPixel.Bpp24 ); // 逐帧处理 ProcessFrames(imagePaths, videoStream, width, height); } } } /// <summary> /// 处理每一帧图像 /// </summary> private static void ProcessFrames( string[] imagePaths, IAviVideoStream videoStream, int width, int height) { foreach (var imagePath in imagePaths) { using (var bitmap = new Bitmap(imagePath)) { // 调整图像大小 using (var resizedBitmap = new Bitmap(bitmap, width, height)) { // 锁定图像数据 var bitmapData = resizedBitmap.LockBits( new Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb ); try { // 准备帧数据 int frameDataLength = bitmapData.Stride * height; byte[] frameData = new byte[frameDataLength]; // 垂直翻转图像数据 for (int y = 0; y < height; y++) { Marshal.Copy( new IntPtr(bitmapData.Scan0.ToInt64() + bitmapData.Stride * (height - 1 - y)), frameData, y * bitmapData.Stride, bitmapData.Stride ); } // 写入帧 videoStream.WriteFrame( true, // 关键帧 frameData, 0, frameDataLength ); } finally { resizedBitmap.UnlockBits(bitmapData); } } } } } } }

4. 使用示例

C#
namespace AppSharpAvi { internal class Program { static void Main(string[] args) { VideoCreator.CreateAviFromImages(new string[] { "1.jpg", "2.jpg", "3.jpg","4.jpg" }, "output.avi", 30); } } }

image.png

编辑
2025-10-13
C#
00

在现代异步编程里,CancellationTokenSource`是一个既强大又灵活的工具,能帮助我们管理并取消耗时较长的任务。接下来,我们会详细聊聊它的使用场景、一些推荐的做法,以及实际的例子。

CancellationTokenSource 基本概念

CancellationTokenSource 是 .NET 中用于协作式取消操作的核心类。它允许您:

  • 创建可取消的操作
  • 发送取消信号
  • 优雅地中断长时间运行的任务
  • 管理资源释放

基本使用场景

网络请求取消

C#
namespace AppCancellation { internal class Program { private static async Task<string> DownloadWebContentAsync(string url, CancellationToken cancellationToken) { using (var client = new HttpClient()) { try { // 设置超时时间 client.Timeout = TimeSpan.FromSeconds(10); // 发起异步GET请求,并传入取消令牌 var response = await client.GetAsync(url, cancellationToken); // 确保请求成功 response.EnsureSuccessStatusCode(); // 读取响应内容 return await response.Content.ReadAsStringAsync(cancellationToken); } catch (OperationCanceledException) { // 捕获操作取消异常 Console.WriteLine("下载已被取消"); return string.Empty; } } } static async Task Main(string[] args) { // 创建取消令牌源 using (var cts = new CancellationTokenSource()) { // 可以在另一个线程中取消操作 _ = Task.Run(() => { // 例如3秒后取消 Thread.Sleep(3000); cts.Cancel(); }); try { string result = await DownloadWebContentAsync("http://blog.idiosoft.com", cts.Token); Console.WriteLine(result); } catch (Exception ex) { Console.WriteLine($"发生错误: {ex.Message}"); } } } } }

image.png

编辑
2025-10-13
C#
00

什么是闭包?

闭包(Closure)是一种强大的编程概念,它允许函数捕获并记住其定义时的环境上下文。在C#中,闭包使得函数可以访问并操作其词法作用域之外的变量。这个东西在JS中用的太多了。

闭包的基本原理

闭包本质上是一个函数,它能够"记住"并访问其创建时的环境变量,即使这些变量已经超出了原始作用域。

简单闭包示例

C#
public class ClosureDemo { public static Func<int> CreateCounter() { // count 是被捕获的局部变量 int count = 0; // 返回一个匿名函数(闭包) return () => { count++; // 闭包可以修改外部变量 return count; }; } public static void Main() { // 创建两个独立的计数器 var counter1 = CreateCounter(); var counter2 = CreateCounter(); Console.WriteLine(counter1()); // 输出: 1 Console.WriteLine(counter1()); // 输出: 2 Console.WriteLine(counter2()); // 输出: 1 (独立的计数器) } }

image.png

编辑
2025-10-13
C#
00

引言

在Windows应用程序中,播放系统声音是一个常见的需求。本文将详细介绍在C#中调用系统声音的多种方法,并提供具体的代码示例。

使用 System.Media.SystemSounds 类

基本使用方法

System.Media.SystemSounds 类提供了最简单的系统声音播放方式,包括常见的系统提示音。

C#
using System.Media; // 播放不同类型的系统声音 SystemSounds.Asterisk.Play(); // 信息提示音 SystemSounds.Beep.Play(); // 基本蜂鸣声 SystemSounds.Exclamation.Play();// 警告声 SystemSounds.Hand.Play(); // 错误提示音 SystemSounds.Question.Play(); // 询问声

完整示例代码

C#
using System.Media; namespace AppSystemSound { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void btnPlayAsterisk_Click(object sender, EventArgs e) { SystemSounds.Asterisk.Play(); } private void btnPlayBeep_Click(object sender, EventArgs e) { SystemSounds.Beep.Play(); } private void btnPlayExclamation_Click(object sender, EventArgs e) { SystemSounds.Exclamation.Play(); } } }

image.png