简介:这份资源面向使用 C# 开发相机控制程序的工程师与爱好者,基于 WinForms 编写,通过佳能 EOS SDK 实现相机拍照、实时传输等核心功能,并附带最新版 EOS SDK 组件,可直接用于二次开发或项目集成。压缩包共 1792 个文件,约 134.97MB,其中 1518 个 bin 文件与 104 个 dll 构成 SDK 运行库与依赖组件,18 个 cs 源码文件承载主要业务逻辑,另有 icc、icm 色彩配置文件及 csproj、sln 工程文件,便于在 Visual Studio 中直接打开编译。SDK 支持 EOS-1D Mark III、EOS 40D、EOS 5D Mark II、EOS 7D、EOS 60D、EOS 550D 等多款机型,覆盖从早期到较新的单反产品线。目前已有 1330 人学习下载,读者可借此掌握相机连接、拍照触发、图像回传的完整实现思路,并参考工程结构快速搭建自己的拍摄控制工具。1. 从一台相机到一块屏幕:这套 C# 源码到底解决了什么问题如果你接过那种“相机拍完,人跑到电脑前拷卡,再打开文件夹找图”的活儿,就知道中间那几分钟有多磨人。这套 C# 控制佳能相机拍照并实时传输的源码,干的就是把这段路彻底砍掉:相机快门一响,图片直接落到你指定的目录,界面里同步出现缩略图,连拍、参数调节、实时取景都能在代码里控制。它基于佳能官方 EOS SDK 封装了一套 C# 调用层,核心是 EDSDK.Dll 的互操作,适合两类人:一类是想做影棚联机拍摄工具的开发者,另一类是需要把相机集成进自动化检测、档案采集流程的工程人员。源码里带了最新版 EOS SDK 的引用和示例,拿到手改改相机型号和保存路径就能跑,不用从零啃官方那几百页的 C 接口文档。2. 技术栈与目录结构:EDSDP 互操作是怎么搭起来的2.1 为什么是 C# 调 EOS SDK,而不是纯 C++佳能 EOS SDK 本身是 C 接口的动态库,官方示例清一色 C++。这套源码选择 C# 做上层,原因很实际:业务系统、界面、文件管理大多已经在 .NET 生态里,为了控制相机再养一个 C++ 模块,维护成本翻倍。C# 通过 P/Invoke 调用 EDSDK.Dll,把相机句柄、回调、属性读写都包成托管对象,业务代码里看不到指针和手动内存释放。常见做法是用DllImport声明入口,再用一个静态类集中管理 SDK 的初始化和释放,避免到处写Marshal。2.2 目录结构长什么样源码包解压后大致是这几块,不同版本可能略有出入,但核心目录跑不掉:CanonCameraDemo/ ├── EDSDK/ # 佳能官方 SDK 的 .h 和 .lib/.dll │ ├── EDSDK.h │ ├── EDSDKTypes.h │ └── EDSDK.dll ├── EDSDKWrapper/ # C# 对 SDK 的封装层 │ ├── EdsCamera.cs # 相机对象,连接/拍照/取景 │ ├── EdsObject.cs # 图像对象、目录对象 │ ├── EdsProperty.cs # 属性读写(ISO、光圈、快门) │ └── EdsException.cs # 错误码转异常 ├── CameraApp/ # 主程序,WinForm 或 WPF │ ├── MainForm.cs │ ├── LiveViewControl.cs # 实时取景显示 │ └── TransferService.cs # 拍照后文件传输 └── packages/ # NuGet 依赖EDSDKWrapper是整套代码最值钱的部分,它把 C 接口的EdsSendCommand、EdsSetCapacity、EdsDownload这些函数包成了Camera.Capture()、Camera.DownloadLastImage()这样的方法。你二次开发时基本只动CameraApp,封装层除非要加新功能,否则不用碰。2.3 核心依赖和运行环境跑起来需要三样东西:佳能 EOS SDK 的 DLL(源码包里已带)、.NET Framework 4.6.1 以上或 .NET 6/8 的 Windows 桌面运行时、以及一台支持 EDSDK 的佳能相机。注意 SDK 分 32 位和 64 位,你的主程序目标平台必须和EDSDK.dll一致,否则一连接就报DllNotFoundException。我一般会在项目属性里把目标平台固定成 x64,然后把对应版本的 DLL 放到输出目录,省得运行时找不到。3. 核心模块拆解:连接、拍照、实时传输的代码链路3.1 初始化与相机连接一切从EdsInitializeSDK()开始,没这一步后面全是空指针。封装层里通常这样写:// EdsCamera.cs public class EdsCamera : IDisposable { private IntPtr _camera = IntPtr.Zero; // 初始化 SDK,整个进程只需调用一次 public static void InitializeSdk() { uint err = EDSDK.EdsInitializeSDK(); if (err != EDSDK.EDS_ERR_OK) throw new EdsException(err, "SDK 初始化失败"); } // 连接第一台检测到的相机 public void Connect() { // 获取相机列表,返回一个 IntPtr 数组 IntPtr cameraList; Check(EDSDK.EdsGetCameraList(out cameraList)); int count; Check(EDSDK.EdsGetChildCount(cameraList, out count)); if (count == 0) throw new EdsException(EDSDK.EDS_ERR_DEVICE_NOT_FOUND, "未检测到相机"); // 取第一台相机的句柄 Check(EDSDK.EdsGetChildAtIndex(cameraList, 0, out _camera)); // 打开会话,之后才能读写属性和拍照 Check(EDSDK.EdsOpenSession(_camera)); // 设置保存目标为电脑,而不是相机存储卡 Check(EDSDK.EdsSetCapacity(_camera, new EdsCapacity { NumberOfFreeClusters = 0x7FFFFFFF, BytesPerCluster = 0x1000, Reset = 1 })); } private static void Check(uint err) { if (err != EDSDK.EDS_ERR_OK) throw new EdsException(err); } public void Dispose() { if (_camera != IntPtr.Zero) { EDSDK.EdsCloseSession(_camera); EDSDK.EdsRelease(_camera); _camera = IntPtr.Zero; } } }这段代码的逻辑很直白:先初始化 SDK,再拿相机列表,取第一台打开会话。EdsSetCapacity那一步是关键,它告诉相机“照片别存卡里,直接往电脑送”,后面实时传输才成立。参数NumberOfFreeClusters和BytesPerCluster设成极大值,意思是电脑端容量无限,相机不用等确认。实际项目里如果要多台相机,就把EdsGetChildAtIndex的索引换成循环,每台相机一个EdsCamera实例。3.2 拍照与文件落盘拍照本身是发一条命令,但真正让图片出现在硬盘上的是后续的下载逻辑。SDK 在拍照完成后会触发一个对象事件,回调里拿到新图像的对象句柄,再调用下载:// TransferService.cs public class TransferService { private readonly EdsCamera _camera; private readonly string _saveDir; public TransferService(EdsCamera camera, string saveDir) { _camera = camera; _saveDir = saveDir; Directory.CreateDirectory(saveDir); } // 触发拍照,SDK 会异步回调 public void Capture() { _camera.SendCommand(EDSDK.CameraCommand_TakePicture); } // 在对象事件回调中调用,处理新生成的图像 public void OnNewObject(IntPtr imageObj) { // 读取图像信息,拿到文件名建议 var info = _camera.GetImageInfo(imageObj); string fileName = $"IMG_{DateTime.Now:yyyyMMdd_HHmmss_fff}.jpg"; string fullPath = Path.Combine(_saveDir, fileName); // 下载到指定路径,SDK 内部走的是相机到电脑的传输通道 _camera.DownloadImage(imageObj, fullPath); // 下载完必须释放对象,否则内存会涨 EDSDK.EdsRelease(imageObj); // 通知界面刷新缩略图 ImageSaved?.Invoke(this, fullPath); } public event EventHandler<string> ImageSaved; }Capture()只是发命令,真正的重活在OnNewObject里。DownloadImage封装了EdsCreateFileStream和EdsDownload两步,把相机里的图像数据写到本地文件。这里有个血泪经验:EdsRelease一定要在下载完成后立刻调用,我见过有人忘了释放,连拍两百张后程序直接卡死,内存曲线跟爬山一样。文件名用时间戳加毫秒,避免连拍时重名覆盖。3.3 实时取景与界面刷新实时取景(Live View)是另一条链路,它不走拍照命令,而是让相机持续输出预览帧。封装层里通常开一个后台线程拉帧:// LiveViewControl.cs private void StartLiveView() { // 告诉相机进入实时取景模式 _camera.SetProperty(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_PC); _liveViewTimer = new Timer(_ => { IntPtr stream; // 从相机取一帧预览数据 uint err = EDSDK.EdsCreateMemoryStream(0, out stream); if (err != EDSDK.EDS_ERR_OK) return; err = EDSDK.EdsGetLiveViewImage(_camera.Handle, stream); if (err == EDSDK.EDS_ERR_OK) { // 把流转成 Bitmap 显示到 PictureBox var bmp = _camera.StreamToBitmap(stream); this.BeginInvoke(new Action(() => { pictureBoxLiveView.Image?.Dispose(); pictureBoxLiveView.Image = bmp; })); } EDSDK.EdsRelease(stream); }, null, 0, 66); // 约 15fps,够用且不占满 CPU }PropID_Evf_OutputDevice设成PC表示预览流走 USB 到电脑。定时器间隔 66ms 对应 15 帧,再快相机也扛不住,而且界面刷新本身有开销。EdsGetLiveViewImage返回的是 JPEG 流,转 Bitmap 显示即可。注意这个定时器回调在非 UI 线程,更新控件必须BeginInvoke,否则跨线程异常直接崩。4. 避坑与常见问题:连接失败、传输卡顿、内存泄漏4.1 相机连上了但拍不了照现象是EdsOpenSession返回成功,但TakePicture报错。原因通常是相机模式不对,EOS 相机在自动模式或视频模式下不接受 SDK 的拍照命令。解决方法是把相机拨到 M 或 Av/Tv 档,并且关闭机身上的“自动关机”和“Wi-Fi”功能,Wi-Fi 开启时 USB 通道会被占用。我一般会在连接后读一下PropID_AEMode,不是手动档就弹提示让用户改。4.2 实时传输大图时界面卡死连拍 RAW 文件时,DownloadImage是同步阻塞的,如果直接在 UI 线程调用,界面会假死。原因是下载一张 30MB 的 RAW 要几百毫秒,期间消息循环停转。解决办法是把下载放到Task.Run里,下载完再回 UI 线程更新缩略图。封装层可以提供一个DownloadImageAsync方法,内部用await Task.Run(...)包一层,调用方就不用操心线程了。4.3 程序运行一段时间后内存暴涨现象是拍了几十张后任务管理器里内存只增不减。原因有两个:一是图像对象没EdsRelease,二是Bitmap没Dispose。SDK 返回的每个IntPtr对象都占非托管内存,托管 GC 管不到。解决方法是给每个DownloadImage和StreamToBitmap加try/finally,在finally里释放。界面上的PictureBox.Image换图前先Dispose旧图,这个习惯能省掉一半的玄学崩溃。4.4 换台电脑就报 DllNotFoundException现象是开发机跑得好好的,拷到另一台机器就找不到EDSDK.dll。原因是 SDK 的 DLL 没跟着输出,或者位数不匹配。解决方法是把EDSDK.dll和相关的EdsImage.dll一起放到输出目录,并在项目里设置“复制到输出目录”。如果主程序是 AnyCPU,在 64 位系统上会以 64 位运行,但 SDK 只有 32 位版,就会加载失败。固定目标平台为 x64 或 x86,和 DLL 保持一致,这个坑我踩过不止一次。4.5 拍照回调不触发现象是命令发出去了,但OnNewObject一直不执行。原因是对象事件没注册。SDK 需要显式调用EdsSetObjectEventHandler把回调函数挂上去,否则拍照完成也不通知你。解决方法是连接相机后立刻注册事件处理器,并且把委托实例用静态字段存住,防止被 GC 回收导致回调失效。这个细节官方文档写得很隐蔽,但少了它整个传输链路就是断的。5. 二次开发改造:从示例到能用的联机拍摄工具拿到这套源码,最直接的改造是换保存路径和文件名规则。TransferService里的_saveDir和fileName拼装逻辑改掉就行,比如按拍摄日期建子目录、按客户编号前缀命名。我一般会把命名规则抽成一个INameStrategy接口,默认实现用时间戳,需要时换成流水号或条码,不用动传输逻辑。第二个改造点是加参数控制。示例里可能只演示了拍照,实际影棚需要调 ISO、光圈、快门。封装层已经提供了SetProperty,你只需要在界面上加几个下拉框,把PropID_ISOSpeed、PropID_Tv、PropID_Av的值写进去。注意这些属性要在EdsOpenSession之后、TakePicture之前设置,而且相机得在 M 档才生效。改完记得读回来确认,有些相机对非法值会静默忽略。第三个改造方向是传输进度和断点续传。大文件下载时用户想知道进度,SDK 的EdsDownload支持传一个进度回调,封装层可以暴露DownloadProgress事件。如果传输中断,重新连接后可以根据文件名判断哪些已下载,跳过重复的。这个功能在批量采集场景很实用,避免断一次全部重来。验证改造是否成功,我习惯用一个最小闭环:连接相机、设参数、拍一张、确认文件出现在目标目录、界面缩略图刷新、内存没有持续增长。这五步走通,基本就稳了。从那以后我每次改完传输相关的代码,都会强制走一遍这个闭环,哪怕只改了一个文件名拼接。希望帮到你。本文还有配套的精品资源,点击获取
简介:这份资源面向使用 C# 开发相机控制程序的工程师与爱好者,基于 WinForms 编写,通过佳能 EOS SDK 实现相机拍照、实时传输等核心功能,并附带最新版 EOS SDK 组件,可直接用于二次开发或项目集成。压缩包共 1792 个文件,约 134.97MB,其中 1518 个 bin 文件与 104 个 dll 构成 SDK 运行库与依赖组件,18 个 cs 源码文件承载主要业务逻辑,另有 icc、icm 色彩配置文件及 csproj、sln 工程文件,便于在 Visual Studio 中直接打开编译。SDK 支持 EOS-1D Mark III、EOS 40D、EOS 5D Mark II、EOS 7D、EOS 60D、EOS 550D 等多款机型,覆盖从早期到较新的单反产品线。目前已有 1330 人学习下载,读者可借此掌握相机连接、拍照触发、图像回传的完整实现思路,并参考工程结构快速搭建自己的拍摄控制工具。1. 从一台相机到一块屏幕:这套 C# 源码到底解决了什么问题如果你接过那种“相机拍完,人跑到电脑前拷卡,再打开文件夹找图”的活儿,就知道中间那几分钟有多磨人。这套 C# 控制佳能相机拍照并实时传输的源码,干的就是把这段路彻底砍掉:相机快门一响,图片直接落到你指定的目录,界面里同步出现缩略图,连拍、参数调节、实时取景都能在代码里控制。它基于佳能官方 EOS SDK 封装了一套 C# 调用层,核心是 EDSDK.Dll 的互操作,适合两类人:一类是想做影棚联机拍摄工具的开发者,另一类是需要把相机集成进自动化检测、档案采集流程的工程人员。源码里带了最新版 EOS SDK 的引用和示例,拿到手改改相机型号和保存路径就能跑,不用从零啃官方那几百页的 C 接口文档。2. 技术栈与目录结构:EDSDP 互操作是怎么搭起来的2.1 为什么是 C# 调 EOS SDK,而不是纯 C++佳能 EOS SDK 本身是 C 接口的动态库,官方示例清一色 C++。这套源码选择 C# 做上层,原因很实际:业务系统、界面、文件管理大多已经在 .NET 生态里,为了控制相机再养一个 C++ 模块,维护成本翻倍。C# 通过 P/Invoke 调用 EDSDK.Dll,把相机句柄、回调、属性读写都包成托管对象,业务代码里看不到指针和手动内存释放。常见做法是用DllImport声明入口,再用一个静态类集中管理 SDK 的初始化和释放,避免到处写Marshal。2.2 目录结构长什么样源码包解压后大致是这几块,不同版本可能略有出入,但核心目录跑不掉:CanonCameraDemo/ ├── EDSDK/ # 佳能官方 SDK 的 .h 和 .lib/.dll │ ├── EDSDK.h │ ├── EDSDKTypes.h │ └── EDSDK.dll ├── EDSDKWrapper/ # C# 对 SDK 的封装层 │ ├── EdsCamera.cs # 相机对象,连接/拍照/取景 │ ├── EdsObject.cs # 图像对象、目录对象 │ ├── EdsProperty.cs # 属性读写(ISO、光圈、快门) │ └── EdsException.cs # 错误码转异常 ├── CameraApp/ # 主程序,WinForm 或 WPF │ ├── MainForm.cs │ ├── LiveViewControl.cs # 实时取景显示 │ └── TransferService.cs # 拍照后文件传输 └── packages/ # NuGet 依赖EDSDKWrapper是整套代码最值钱的部分,它把 C 接口的EdsSendCommand、EdsSetCapacity、EdsDownload这些函数包成了Camera.Capture()、Camera.DownloadLastImage()这样的方法。你二次开发时基本只动CameraApp,封装层除非要加新功能,否则不用碰。2.3 核心依赖和运行环境跑起来需要三样东西:佳能 EOS SDK 的 DLL(源码包里已带)、.NET Framework 4.6.1 以上或 .NET 6/8 的 Windows 桌面运行时、以及一台支持 EDSDK 的佳能相机。注意 SDK 分 32 位和 64 位,你的主程序目标平台必须和EDSDK.dll一致,否则一连接就报DllNotFoundException。我一般会在项目属性里把目标平台固定成 x64,然后把对应版本的 DLL 放到输出目录,省得运行时找不到。3. 核心模块拆解:连接、拍照、实时传输的代码链路3.1 初始化与相机连接一切从EdsInitializeSDK()开始,没这一步后面全是空指针。封装层里通常这样写:// EdsCamera.cs public class EdsCamera : IDisposable { private IntPtr _camera = IntPtr.Zero; // 初始化 SDK,整个进程只需调用一次 public static void InitializeSdk() { uint err = EDSDK.EdsInitializeSDK(); if (err != EDSDK.EDS_ERR_OK) throw new EdsException(err, "SDK 初始化失败"); } // 连接第一台检测到的相机 public void Connect() { // 获取相机列表,返回一个 IntPtr 数组 IntPtr cameraList; Check(EDSDK.EdsGetCameraList(out cameraList)); int count; Check(EDSDK.EdsGetChildCount(cameraList, out count)); if (count == 0) throw new EdsException(EDSDK.EDS_ERR_DEVICE_NOT_FOUND, "未检测到相机"); // 取第一台相机的句柄 Check(EDSDK.EdsGetChildAtIndex(cameraList, 0, out _camera)); // 打开会话,之后才能读写属性和拍照 Check(EDSDK.EdsOpenSession(_camera)); // 设置保存目标为电脑,而不是相机存储卡 Check(EDSDK.EdsSetCapacity(_camera, new EdsCapacity { NumberOfFreeClusters = 0x7FFFFFFF, BytesPerCluster = 0x1000, Reset = 1 })); } private static void Check(uint err) { if (err != EDSDK.EDS_ERR_OK) throw new EdsException(err); } public void Dispose() { if (_camera != IntPtr.Zero) { EDSDK.EdsCloseSession(_camera); EDSDK.EdsRelease(_camera); _camera = IntPtr.Zero; } } }这段代码的逻辑很直白:先初始化 SDK,再拿相机列表,取第一台打开会话。EdsSetCapacity那一步是关键,它告诉相机“照片别存卡里,直接往电脑送”,后面实时传输才成立。参数NumberOfFreeClusters和BytesPerCluster设成极大值,意思是电脑端容量无限,相机不用等确认。实际项目里如果要多台相机,就把EdsGetChildAtIndex的索引换成循环,每台相机一个EdsCamera实例。3.2 拍照与文件落盘拍照本身是发一条命令,但真正让图片出现在硬盘上的是后续的下载逻辑。SDK 在拍照完成后会触发一个对象事件,回调里拿到新图像的对象句柄,再调用下载:// TransferService.cs public class TransferService { private readonly EdsCamera _camera; private readonly string _saveDir; public TransferService(EdsCamera camera, string saveDir) { _camera = camera; _saveDir = saveDir; Directory.CreateDirectory(saveDir); } // 触发拍照,SDK 会异步回调 public void Capture() { _camera.SendCommand(EDSDK.CameraCommand_TakePicture); } // 在对象事件回调中调用,处理新生成的图像 public void OnNewObject(IntPtr imageObj) { // 读取图像信息,拿到文件名建议 var info = _camera.GetImageInfo(imageObj); string fileName = $"IMG_{DateTime.Now:yyyyMMdd_HHmmss_fff}.jpg"; string fullPath = Path.Combine(_saveDir, fileName); // 下载到指定路径,SDK 内部走的是相机到电脑的传输通道 _camera.DownloadImage(imageObj, fullPath); // 下载完必须释放对象,否则内存会涨 EDSDK.EdsRelease(imageObj); // 通知界面刷新缩略图 ImageSaved?.Invoke(this, fullPath); } public event EventHandler<string> ImageSaved; }Capture()只是发命令,真正的重活在OnNewObject里。DownloadImage封装了EdsCreateFileStream和EdsDownload两步,把相机里的图像数据写到本地文件。这里有个血泪经验:EdsRelease一定要在下载完成后立刻调用,我见过有人忘了释放,连拍两百张后程序直接卡死,内存曲线跟爬山一样。文件名用时间戳加毫秒,避免连拍时重名覆盖。3.3 实时取景与界面刷新实时取景(Live View)是另一条链路,它不走拍照命令,而是让相机持续输出预览帧。封装层里通常开一个后台线程拉帧:// LiveViewControl.cs private void StartLiveView() { // 告诉相机进入实时取景模式 _camera.SetProperty(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_PC); _liveViewTimer = new Timer(_ => { IntPtr stream; // 从相机取一帧预览数据 uint err = EDSDK.EdsCreateMemoryStream(0, out stream); if (err != EDSDK.EDS_ERR_OK) return; err = EDSDK.EdsGetLiveViewImage(_camera.Handle, stream); if (err == EDSDK.EDS_ERR_OK) { // 把流转成 Bitmap 显示到 PictureBox var bmp = _camera.StreamToBitmap(stream); this.BeginInvoke(new Action(() => { pictureBoxLiveView.Image?.Dispose(); pictureBoxLiveView.Image = bmp; })); } EDSDK.EdsRelease(stream); }, null, 0, 66); // 约 15fps,够用且不占满 CPU }PropID_Evf_OutputDevice设成PC表示预览流走 USB 到电脑。定时器间隔 66ms 对应 15 帧,再快相机也扛不住,而且界面刷新本身有开销。EdsGetLiveViewImage返回的是 JPEG 流,转 Bitmap 显示即可。注意这个定时器回调在非 UI 线程,更新控件必须BeginInvoke,否则跨线程异常直接崩。4. 避坑与常见问题:连接失败、传输卡顿、内存泄漏4.1 相机连上了但拍不了照现象是EdsOpenSession返回成功,但TakePicture报错。原因通常是相机模式不对,EOS 相机在自动模式或视频模式下不接受 SDK 的拍照命令。解决方法是把相机拨到 M 或 Av/Tv 档,并且关闭机身上的“自动关机”和“Wi-Fi”功能,Wi-Fi 开启时 USB 通道会被占用。我一般会在连接后读一下PropID_AEMode,不是手动档就弹提示让用户改。4.2 实时传输大图时界面卡死连拍 RAW 文件时,DownloadImage是同步阻塞的,如果直接在 UI 线程调用,界面会假死。原因是下载一张 30MB 的 RAW 要几百毫秒,期间消息循环停转。解决办法是把下载放到Task.Run里,下载完再回 UI 线程更新缩略图。封装层可以提供一个DownloadImageAsync方法,内部用await Task.Run(...)包一层,调用方就不用操心线程了。4.3 程序运行一段时间后内存暴涨现象是拍了几十张后任务管理器里内存只增不减。原因有两个:一是图像对象没EdsRelease,二是Bitmap没Dispose。SDK 返回的每个IntPtr对象都占非托管内存,托管 GC 管不到。解决方法是给每个DownloadImage和StreamToBitmap加try/finally,在finally里释放。界面上的PictureBox.Image换图前先Dispose旧图,这个习惯能省掉一半的玄学崩溃。4.4 换台电脑就报 DllNotFoundException现象是开发机跑得好好的,拷到另一台机器就找不到EDSDK.dll。原因是 SDK 的 DLL 没跟着输出,或者位数不匹配。解决方法是把EDSDK.dll和相关的EdsImage.dll一起放到输出目录,并在项目里设置“复制到输出目录”。如果主程序是 AnyCPU,在 64 位系统上会以 64 位运行,但 SDK 只有 32 位版,就会加载失败。固定目标平台为 x64 或 x86,和 DLL 保持一致,这个坑我踩过不止一次。4.5 拍照回调不触发现象是命令发出去了,但OnNewObject一直不执行。原因是对象事件没注册。SDK 需要显式调用EdsSetObjectEventHandler把回调函数挂上去,否则拍照完成也不通知你。解决方法是连接相机后立刻注册事件处理器,并且把委托实例用静态字段存住,防止被 GC 回收导致回调失效。这个细节官方文档写得很隐蔽,但少了它整个传输链路就是断的。5. 二次开发改造:从示例到能用的联机拍摄工具拿到这套源码,最直接的改造是换保存路径和文件名规则。TransferService里的_saveDir和fileName拼装逻辑改掉就行,比如按拍摄日期建子目录、按客户编号前缀命名。我一般会把命名规则抽成一个INameStrategy接口,默认实现用时间戳,需要时换成流水号或条码,不用动传输逻辑。第二个改造点是加参数控制。示例里可能只演示了拍照,实际影棚需要调 ISO、光圈、快门。封装层已经提供了SetProperty,你只需要在界面上加几个下拉框,把PropID_ISOSpeed、PropID_Tv、PropID_Av的值写进去。注意这些属性要在EdsOpenSession之后、TakePicture之前设置,而且相机得在 M 档才生效。改完记得读回来确认,有些相机对非法值会静默忽略。第三个改造方向是传输进度和断点续传。大文件下载时用户想知道进度,SDK 的EdsDownload支持传一个进度回调,封装层可以暴露DownloadProgress事件。如果传输中断,重新连接后可以根据文件名判断哪些已下载,跳过重复的。这个功能在批量采集场景很实用,避免断一次全部重来。验证改造是否成功,我习惯用一个最小闭环:连接相机、设参数、拍一张、确认文件出现在目标目录、界面缩略图刷新、内存没有持续增长。这五步走通,基本就稳了。从那以后我每次改完传输相关的代码,都会强制走一遍这个闭环,哪怕只改了一个文件名拼接。希望帮到你。本文还有配套的精品资源,点击获取