Canon EDSDK Tutorial in C#

A tutorial about the Canon SDK to remotly control DSLR cameras. From taking photos to using the LiveView.Is your email address OK? You are signed up for our newsletters but your email address is either unconfirmed, or has not been reconfirmed in a long time. Please click here to have a confirmation email sent so we can confirm your email address and start sending you newsletters again. Alternatively, you can update your subscriptions.

  • Download CanonSDKTutorial V11 (source only) - 65.7 KB

  • Download CanonSDKTutorial V11 (source + binary) - 211.7 KB

Canon EDSDK Tutorial in C#

Introduction

The Canon EOS Digital SDK is a quite powerful SDK to remote control Canon DSLRs. Unfortunately it is quite difficult to find some good examples for it on the internet and the provided documentation is not very complete. Since I have found out many things already and want to make it easier for others I thought I could compile some of the most important things together and do a tutorial.

This tutorial includes:

  • Init and terminate the SDK

  • Get connected cameras

  • Open and close a session with a camera

  • Get camera settings

  • Set camera settings

  • Get a list of available settings

  • Take photos (normally and in Bulb mode)

  • Record videos

  • Press the shutter button remotely

  • Download data (images, films) from the camera

  • Start and view the live view

  • Control the focus

  • Lock and unlock the camera UI

  • Get an image thumbnail

  • Get folders and files on the camera

Note: I'm not affiliated with or funded by Canon Inc. in any way

I do not guarantee for this software in any way. Use at your own risk!

Full Library

This article and the attached project is made to learn how to use the SDK and is therefore not always most elegant or useful for production use.

If you are only looking for a production ready library to handle the SDK, head over to my homepage and look at the EDSDK API project

It is much more elegant and includes a lot of practical methods and properties as well as a raw image processing class. It runs on both Windows and Mac. Also, it is not dependent on any graphics library but contains versions for System.Drawing and WPF as well.

Background

You have to have a copy of the Canon EDSDK to get this working. I am not allowed to include the DLLs within the project so you'll have to apply to get them, here:

  • Europe, African and Middle East

  • North, Central and South America

  • Australia and New Zealand

  • India, Indonesia, Malaysia, Pakistan, the Philippines, Singapore and Thailand

Once you have the DLLs, put them beside your executable. Other places will cause problems if the main DLL makes a call to a sub-DLL.

It is also important that you use your camera in fully manual or at least half-automated mode for some methods to work.

Using the code

The solution consists of three projects:

  • EDSDKLib: The main project where all the SDK and camera handling happens

  • WinFormsUI: An example project that consumes the EDSDKLib and uses it in a Windows Forms UI application

  • WPFUI: An example project that consumes the EDSDKLib and uses it in a WPF UI application

  • ConsoleExample: An example project that consumes the EDSDKLib and uses it in a console application

I will only concentrate on the EDSDKLib project here since this is what this whole article is about: Using the EDSDK with C#

First let us look at the classes

  • SDKHandler: The most important class as it handles all the SDK calls and events

  • Camera: This is a simple container for a pointer and some camera related info

  • CameraValues: A static class that contains a list of Av, Tv and ISO IDs and their string representation. It is not necessary but very helpful for user interaction.

  • CameraFileEntry: A helper class that stores informations about a file entry in the camera. E.g.. the file/folder name, the thumbnail of an image etc.

  • STAThread: A helper class to create an STA thread or execute code on an STA thread.

  • EDSDK: This class comes from the SDK and contains all the native calls to the DLLs, some IDs, enums, some helper methods and struct definitions.

Since all the important stuff is in the SDKHandler class, this is what we will look at now.

First, there are some important global variables declared:

  • Camera MainCamera: The used camera

  • bool CameraSessionOpen: States if a session with the MainCamera is opened

  • bool IsLiveViewOn: States if the live view is on or not

  • bool IsFilming: States if camera is recording or not

  • string ImageSaveDirectory: Directory to where photos will be saved (if SaveTo is set to Host or Both)

  • EDSDK.EdsRect Evf_ZoomRect: The focus and zoom border rectangle for live view (set after first use of live view)

  • EDSDK.EdsPoint Evf_ZoomPosition: The focus and zoom border position of the live view (set after first use of live view)

  • EDSDK.EdsPoint Evf_ImagePosition: The cropping position of the enlarged live view image (set after first use of live view)

  • EDSDK.EdsSize Evf_CoordinateSystem: The live view coordinate system (set after first use of live view)

  • bool IsCoordSystemSet: States if the Evf_CoordinateSystem is already set

  • uint Error: Handles errors that happen with the SDK

  • bool DownloadVideo: States if a finished video should be downloaded from the camera

  • uint PrevSaveTo: For video recording, SaveTo has to be set to Camera. This is to store the previous setting until after the filming.

  • Thread LVThread: The thread on which the live view images will get downloaded continuously

  • bool LVoff: If true, the live view will be shut off completely. If false, live view will go back to the camera.

Also, there are some events from the SDK:

  • SDKCameraAddedEvent

  • SDKObjectEvent

  • SDKProgressCallbackEvent

  • SDKPropertyEvent

  • SDKStateEvent

And some events declared by me:

  • CameraAdded: Fires if a camera is added

  • ProgressChanged: Fires if any process reports progress

  • LiveViewUpdated: Fires if the live view image has been updated

  • CameraHasShutdown: If the camera is disconnected or shuts down, this event is fired

  • ImageDownloaded: If an image is downloaded, this event fires with the downloaded image

Now off to the fun part: The methods!

Init and Terminate the SDK

Initializing and terminating are the easiest things to do. When you start your program, create a new instance of the SDKHandler:

Hide   Copy Code

public SDKHandler() {     //initialize SDK     Error = EDSDK.EdsInitializeSDK();     //start the command execution thread     STAThread.Init();     //subscribe to camera added event (the C# event and the SDK event)     SDKCameraAddedEvent += new EDSDK.EdsCameraAddedHandler(SDKHandler_CameraAddedEvent);     EDSDK.EdsSetCameraAddedHandler(SDKCameraAddedEvent, IntPtr.Zero);     //subscribe to the camera events (for the C# events)     SDKStateEvent += new EDSDK.EdsStateEventHandler(Camera_SDKStateEvent);     SDKPropertyEvent += new EDSDK.EdsPropertyEventHandler(Camera_SDKPropertyEvent);     SDKProgressCallbackEvent += new EDSDK.EdsProgressCallback(Camera_SDKProgressCallbackEvent);     SDKObjectEvent += new EDSDK.EdsObjectEventHandler(Camera_SDKObjectEvent); }

And when you close your program, call:

Hide   Copy Code

public void Dispose() {     //close session     CloseSession();     //terminate SDK     Error = EDSDK.EdsTerminateSDK();     //stop command execution thread     STAThread.Shutdown(); }

Getting Connected Cameras

 To open a session, you first have to select a camera. To get a list of all connected cameras, call this:

Hide   Copy Code

public List<Camera> GetCameraList() {     IntPtr camlist;     //get list of cameras     Error = EDSDK.EdsGetCameraList(out camlist);     //get each camera from camlist     int c;     //get amount of connected cameras     Error = EDSDK.EdsGetChildCount(camlist, out c);     List<Camera> OutCamList = new List<Camera>();     for (int i = 0; i < c; i++)     {         IntPtr cptr;         //get pointer to camera at index i         Error = EDSDK.EdsGetChildAtIndex(camlist, i, out cptr);         OutCamList.Add(new Camera(cptr));     }     return OutCamList; }

Open and Close a Session with a Camera

 From the previously received camera list, select one and open a session to work with it:

Hide   Copy Code

public void OpenSession(Camera newCamera) {     if (CameraSessionOpen) CloseSession();     if (newCamera != null)     {         MainCamera = newCamera;         //open a session         SendSDKCommand(delegate { Error = EDSDK.EdsOpenSession(MainCamera.Ref); });         //subscribe to the camera events (for the SDK)         EDSDK.EdsSetCameraStateEventHandler(MainCamera.Ref, EDSDK.StateEvent_All, SDKStateEvent, IntPtr.Zero);         EDSDK.EdsSetObjectEventHandler(MainCamera.Ref, EDSDK.ObjectEvent_All, SDKObjectEvent, IntPtr.Zero);         EDSDK.EdsSetPropertyEventHandler(MainCamera.Ref, EDSDK.PropertyEvent_All, SDKPropertyEvent, IntPtr.Zero);         CameraSessionOpen = true;     } }

 If you are done working with this camera, close the session this way:

Hide   Copy Code

public void CloseSession() {     if (CameraSessionOpen)     {         //if live view is still on, stop it and wait till the thread has stopped         if (IsLiveViewOn)         {             StopLiveView();             LVThread.Join(1000);         }         //close session and release camera         SendSDKCommand(delegate { Error = EDSDK.EdsCloseSession(MainCamera.Ref); });         EDSDK.EdsRelease(MainCamera.Ref);         CameraSessionOpen = false;     } }

Get Camera Settings

Setting and getting camera settings can be very easy for values with an ID, but more difficult for struct values.

Here is the getting method for normal uint values (like Tv, Av or ISO):

Hide   Copy Code

public uint GetSetting(uint PropID) {     if (MainCamera.Ref != IntPtr.Zero)     {         uint property = 0;         Error = EDSDK.EdsGetPropertyData(MainCamera.Ref, PropID, 0, out property);         return property;     }     else { throw new ArgumentNullException("Camera or camera reference is null/zero"); } }

Some properties have string values (like the owners name or the body ID):

Hide   Copy Code

public string GetStringSetting(uint PropID) {     if (MainCamera.Ref != IntPtr.Zero)     {         string data = String.Empty;         EDSDK.EdsGetPropertyData(MainCamera.Ref, PropID, 0, out data);         return data;     }     else { throw new ArgumentNullException("Camera or camera reference is null/zero"); } }

Or some use structs:

Hide   Shrink    Copy Code

public T GetStructSetting<T>(uint PropID) where T : struct {     if (MainCamera.Ref != IntPtr.Zero)     {         //get type and size of struct         Type structureType = typeof(T);         int bufferSize = Marshal.SizeOf(structureType);         //allocate memory         IntPtr ptr = Marshal.AllocHGlobal(bufferSize);         //retrieve value         Error = EDSDK.EdsGetPropertyData(MainCamera.Ref, PropID, 0, bufferSize, ptr);         try         {             //convert pointer to managed structure             T data = (T)Marshal.PtrToStructure(ptr, structureType);             return data;         }         finally         {             if (ptr != IntPtr.Zero)             {                 //free the allocated memory                 Marshal.FreeHGlobal(ptr);                 ptr = IntPtr.Zero;             }         }     }     else { throw new ArgumentNullException("Camera or camera reference is null/zero"); } }

To get struct values, use it like this example:

Hide   Copy Code

EDSDK.EdsTime time = GetStructSetting<EDSDK.EdsTime>(EDSDK.PropID_DateTime);

Set Camera Settings

Same as getting settings, here are the methods to set them:

Normal uint values (the Value variable has to be the ID for the value):

Hide   Copy Code

public void SetSetting(uint PropID, uint Value) {     if (MainCamera.Ref != IntPtr.Zero)     {         SendSDKCommand(delegate         {             int propsize;             EDSDK.EdsDataType proptype;             //get size of property             Error = EDSDK.EdsGetPropertySize(MainCamera.Ref, PropID, 0, out proptype, out propsize);             //set given property             Error = EDSDK.EdsSetPropertyData(MainCamera.Ref, PropID, 0, propsize, Value);         });     }     else { throw new ArgumentNullException("Camera or camera reference is null/zero"); } }

Setting a string value:

Hide   Copy Code

public void SetStringSetting(uint PropID, string Value) {     if (MainCamera.Ref != IntPtr.Zero)     {         if (Value == null) throw new ArgumentNullException("String must not be null");         //convert string to byte array         byte[] propertyValueBytes = System.Text.Encoding.ASCII.GetBytes(Value + '\0');         int propertySize = propertyValueBytes.Length;         //check size of string         if (propertySize > 32) throw new ArgumentOutOfRangeException("Value must be smaller than 32 bytes");         //set value         SendSDKCommand(delegate { Error = EDSDK.EdsSetPropertyData(MainCamera.Ref, PropID, 0, 32, propertyValueBytes); });     }     else { throw new ArgumentNullException("Camera or camera reference is null/zero"); } }

and setting a struct value (like EdsTime):

Hide   Copy Code

public void SetStructSetting<T>(uint PropID, T Value) where T : struct {     if (MainCamera.Ref != IntPtr.Zero)     {         SendSDKCommand(delegate { Error = EDSDK.EdsSetPropertyData(MainCamera.Ref, PropID, 0, Marshal.SizeOf(typeof(T)), Value); });     }     else { throw new ArgumentNullException("Camera or camera reference is null/zero"); } }

Get a List of Available Settings

Certain cameras don't support certain settings and lenses only have a certain range of Av values. That's why you need to get a list of all supported settings. This only works with "AEModeSelect", "ISO", "Av", "Tv", "MeteringMode" and "ExposureCompensation". The list that you get as a return value is actually a list of IDs for the given PropertyID. You can find the matching values from AvTv and ISO in the CameraValues class, look into the SDK documentation PDF for the other values.

Hide   Copy Code

public List<int> GetSettingsList(uint PropID) {     if (MainCamera.Ref != IntPtr.Zero)     {         //a list of settings can only be retrieved for following properties         if (PropID == EDSDK.PropID_AEModeSelect || PropID == EDSDK.PropID_ISOSpeed || PropID == EDSDK.PropID_Av             || PropID == EDSDK.PropID_Tv || PropID == EDSDK.PropID_MeteringMode || PropID == EDSDK.PropID_ExposureCompensation)         {             //get the list of possible values             EDSDK.EdsPropertyDesc des = new EDSDK.EdsPropertyDesc();             Error = EDSDK.EdsGetPropertyDesc(MainCamera.Ref, PropID, out des);             return des.PropDesc.Take(des.NumElements).ToList();         }         else throw new ArgumentException("Method cannot be used with this Property ID");     }     else { throw new ArgumentNullException("Camera or camera reference is null/zero"); } }

Taking a Photo (Normal and Bulb)

An important note to the TakePhoto command: If your camera supports the PressShutterButton command I strongly advise to use that. If the camera is not able to focus it could hang the whole program.

To take a photo with the current settings, call the TakePhoto method:

Hide   Copy Code

public void TakePhoto() {     //start thread to not block everything     SendSDKCommand(delegate     {         //send command to camera         lock (STAThread.ExecLock) { Error = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_TakePicture, 0); };     }, true); }

To take a photo in bulb mode, call the TakePhoto method with the BulbTime parameter:

Hide   Copy Code

public void TakePhoto(uint BulbTime) {     //bulbtime has to be at least a second     if (BulbTime < 1000) { throw new ArgumentException("Bulbtime has to be bigger than 1000ms"); }     //start thread to not block everything     SendSDKCommand(delegate     {         //open the shutter         lock (STAThread.ExecLock) { Error = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_BulbStart, 0); }         //wait for the specified time         Thread.Sleep((int)BulbTime);         //close shutter         lock (STAThread.ExecLock) { Error = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_BulbEnd, 0); }     }, true); }

If you selected the output device to be your PC, look for the next topic to download the image from the camera.

Download a Taken Photo to the Computer

If you want to save taken photos directly onto the computer instead of the camera-memory, set it with calling theSetSetting method:

Hide   Copy Code

SetSetting(EDSDK.PropID_SaveTo, (uint)EDSDK.EdsSaveTo.Host); //EdsSaveTo.Both would save the image to the camera AND the computer

Once you have taken a photo, the SDKObjectEvent will fire with the inEvent variable beingEDSDK.ObjectEvent_DirItemRequestTransfer:

Hide   Copy Code

private uint Camera_SDKObjectEvent(uint inEvent, IntPtr inRef, IntPtr inContext) {     if (inEvent == EDSDK.ObjectEvent_DirItemRequestTransfer) DownloadImage(inRef, ImageSaveDirectory);     return EDSDK.EDS_ERR_OK; }

And the DownloadImage method being (the DownloadData method will be explained at the end):

Hide   Copy Code

public void DownloadImage(IntPtr ObjectPointer, string directory) {     EDSDK.EdsDirectoryItemInfo dirInfo;     IntPtr streamRef;     //get information about object     Error = EDSDK.EdsGetDirectoryItemInfo(ObjectPointer, out dirInfo);     string CurrentPhoto = Path.Combine(directory, dirInfo.szFileName);     SendSDKCommand(delegate     {         //create filestream to data         Error = EDSDK.EdsCreateFileStream(CurrentPhoto, EDSDK.EdsFileCreateDisposition.CreateAlways, EDSDK.EdsAccess.ReadWrite, out streamRef);         //download file         lock (STAThread.ExecLock) { DownloadData(ObjectPointer, streamRef); }         //release stream         Error = EDSDK.EdsRelease(streamRef);     }, true); }

If you don't want to save the image to the hard drive but want to get a Bitmap instead, use the secondDownloadImage method:

Hide   Shrink    Copy Code

public void DownloadImage(IntPtr ObjectPointer) {     //get information about image     EDSDK.EdsDirectoryItemInfo dirInfo = new EDSDK.EdsDirectoryItemInfo();     Error = EDSDK.EdsGetDirectoryItemInfo(ObjectPointer, out dirInfo);     //check the extension. Raw data cannot be read by the bitmap class     string ext = Path.GetExtension(dirInfo.szFileName).ToLower();     if (ext == ".jpg" || ext == ".jpeg")     {         SendSDKCommand(delegate         {             Bitmap bmp = null;             IntPtr streamRef, jpgPointer = IntPtr.Zero;             uint length = 0;             //create memory stream             Error = EDSDK.EdsCreateMemoryStream(dirInfo.Size, out streamRef);                 //download data to the stream                 lock (STAThread.ExecLock) { DownloadData(ObjectPointer, streamRef); }                 Error = EDSDK.EdsGetPointer(streamRef, out jpgPointer);                 Error = EDSDK.EdsGetLength(streamRef, out length);                 unsafe                 {                     //create a System.IO.Stream from the pointer                     using (UnmanagedMemoryStream ums = new UnmanagedMemoryStream((byte*)jpgPointer.ToPointer(), length, length, FileAccess.Read))                     {                         //create bitmap from stream (it's a normal jpeg image)                         bmp = new Bitmap(ums);                     }                 }                 //release data                 Error = EDSDK.EdsRelease(streamRef);             //Fire the event with the image                 if (ImageDownloaded != null) ImageDownloaded(bmp);             }, true);     }     else     {         //if it's a RAW image, cancel the download and release the image         SendSDKCommand(delegate { Error = EDSDK.EdsDownloadCancel(ObjectPointer); });         Error = EDSDK.EdsRelease(ObjectPointer);     } }

Press Shutterbutton

You can remotely press the shutter button in various ways. Half to activate the autofocus or to check exposure values. Or full to take a photo (with or without AF). Note that this feature does not work for older cameras.

Hide   Copy Code

public void PressShutterButton(EDSDK.EdsShutterButton state) {     //start thread to not block everything     SendSDKCommand(delegate     {         //send command to camera         lock (STAThread.ExecLock) { Error = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_PressShutterButton, (uint)state); };     }, true); }

Start and View the Live View

The live view is one of the more difficult things to do, especially if it should be high-performance. First we start the live view like this:

Hide   Copy Code

public void StartLiveView() {     if (!IsLiveViewOn)     {         SetSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_PC);         IsLiveViewOn = true;     } }

Once this is done, the SDKPropertyEvent will fire with the inPropertyID variable beingEDSDK.PropID_Evf_OutputDevice:

Hide   Copy Code

private uint Camera_SDKPropertyEvent(uint inEvent,  uint inPropertyID, uint inParameter, IntPtr inContext) {     if (inPropertyID == EDSDK.PropID_Evf_OutputDevice)     {         if (IsLiveViewOn == true) DownloadEvf();     }     return EDSDK.EDS_ERR_OK; }

And the DownloadEvf method being:

Hide   Shrink    Copy Code

private void DownloadEvf() {     LVThread = STAThread.Create(delegate     {         IntPtr jpgPointer;         IntPtr stream = IntPtr.Zero;         IntPtr EvfImageRef = IntPtr.Zero;         UnmanagedMemoryStream ums;         uint err;         uint length;         //create stream         Error = EDSDK.EdsCreateMemoryStream(0, out stream);         //run live view         while (IsLiveViewOn)         {             lock (STAThread.ExecLock)             {                 //download current live view image                 err = EDSDK.EdsCreateEvfImageRef(stream, out EvfImageRef);                 if (err == EDSDK.EDS_ERR_OK) err = EDSDK.EdsDownloadEvfImage(MainCamera.Ref, EvfImageRef);                 if (err == EDSDK.EDS_ERR_OBJECT_NOTREADY) { Thread.Sleep(4); continue; }                 else Error = err;             }             //get pointer             Error = EDSDK.EdsGetPointer(stream, out jpgPointer);             Error = EDSDK.EdsGetLength(stream, out length);             //get some live view image metadata             if (!IsCoordSystemSet) { Evf_CoordinateSystem = GetEvfCoord(EvfImageRef); IsCoordSystemSet = true; }             Evf_ZoomRect = GetEvfZoomRect(EvfImageRef);             Evf_ZoomPosition = GetEvfPoints(EDSDK.PropID_Evf_ZoomPosition, EvfImageRef);             Evf_ImagePosition = GetEvfPoints(EDSDK.PropID_Evf_ImagePosition, EvfImageRef);             //release current evf image             if (EvfImageRef != IntPtr.Zero) { Error = EDSDK.EdsRelease(EvfImageRef); }             //create stream to image             unsafe { ums = new UnmanagedMemoryStream((byte*)jpgPointer.ToPointer(), length, length, FileAccess.Read); }             //fire the LiveViewUpdated event with the live view image stream             if (LiveViewUpdated != null) LiveViewUpdated(ums);             ums.Close();         }         //release and finish         if (stream != IntPtr.Zero) { Error = EDSDK.EdsRelease(stream); }         //stop the live view         SetSetting(EDSDK.PropID_Evf_OutputDevice, LVoff ? 0 : EDSDK.EvfOutputDevice_TFT);     });     LVThread.Start(); }

To stop the live view simply call the StopLiveView method, which essentially just allows the DownloadEvfmethod to jump out of the while loop. With the LVoff you can set if the live view should be shut down completely or should be shown on the camera. (Some older cameras might always shut down)

Hide   Copy Code

public void StopLiveView(bool LVoff = true) {     this.LVoff = LVoff;     IsLiveViewOn = false; }

Recording Videos

Newer cameras have the possibility to record videos built in. To use this, you have to set your camera to video recording mode first (it will not work otherwise!). Usually there is some button, knob or dial you have to switch. If that is done, you can call the starting method where you either download the finished video:

Hide   Copy Code

public void StartFilming(string FilePath) {     if (!IsFilming)     {         StartFilming();         this.DownloadVideo = true;         ImageSaveDirectory = FilePath;     } }

 Or let the finished video on the memory card of the camera:

Hide   Copy Code

public void StartFilming() {     if (!IsFilming)     {         //Check if the camera is ready to film         if (GetSetting(EDSDK.PropID_Record) != 3) throw new InvalidOperationException("Camera is not in film mode");         IsFilming = true;         //to restore the current setting after recording         PrevSaveTo = GetSetting(EDSDK.PropID_SaveTo);         //when recording videos, it has to be saved on the camera internal memory         SetSetting(EDSDK.PropID_SaveTo, (uint)EDSDK.EdsSaveTo.Camera);         this.DownloadVideo = false;         //start the video recording         SendSDKCommand(delegate { Error = EDSDK.EdsSetPropertyData(MainCamera.Ref, EDSDK.PropID_Record, 0, 4, 4); });     } }

 And if you are finished call the StopFilming method:

Hide   Copy Code

public void StopFilming() {     if (IsFilming)     {         SendSDKCommand(delegate         {             //Shut off live view (it will hang otherwise)             StopLiveView(false);             //stop video recording             Error = EDSDK.EdsSetPropertyData(MainCamera.Ref, EDSDK.PropID_Record, 0, 4, 0);         });         //set back to previous state         SetSetting(EDSDK.PropID_SaveTo, PrevSaveTo);         IsFilming = false;     } }

If the finished video should be downloaded after the recording has stopped, we need to catch theSDKObjectEvent:

Hide   Copy Code

private uint Camera_SDKObjectEvent(uint inEvent, IntPtr inRef, IntPtr inContext) {     if(inEvent == EDSDK.ObjectEvent_DirItemCreated && DownloadVideo) { DownloadImage(inRef, ImageSaveDirectory); DownloadVideo = false; }     return EDSDK.EDS_ERR_OK; }

Lock/Unlock the camera UI

To keep the user or from changing settings on the physical camera, or allowing to do so, you can lock or unlock the camera UI like this:

Hide   Copy Code

public void UILock(bool LockState) {     SendSDKCommand(delegate     {         if (LockState == true) Error = EDSDK.EdsSendStatusCommand(MainCamera.Ref, EDSDK.CameraState_UILock, 0);         else Error = EDSDK.EdsSendStatusCommand(MainCamera.Ref, EDSDK.CameraState_UIUnLock, 0);     }); }

Get the Thumbnail of an Image

Sometimes it is useful to get a thumbnail from an image, regardless if RAW or jpg:

Hide   Copy Code

public Bitmap GetFileThumb(string filepath) {     IntPtr stream;     //create a filestream to given file     Error = EDSDK.EdsCreateFileStream(filepath, EDSDK.EdsFileCreateDisposition.OpenExisting, EDSDK.EdsAccess.Read, out stream);     return GetImage(stream, EDSDK.EdsImageSource.Thumbnail); }

Control the Focus of a Lens

A question often asked all over the internet is how to control the focus of the camera. It's actually quite easy if you know how. Most important, the camera has to be in live view and the lens has to be on AF. Then call this piece of code:

Hide   Copy Code

public void SetFocus(uint Speed) {     if (IsLiveViewOn) SendSDKCommand(delegate { Error = EDSDK.EdsSendCommand(MainCamera.Ref, EDSDK.CameraCommand_DriveLensEvf, Speed); }); }

The variable Speed is simply one of the EDSDK.EvfDriveLens_ values. There are three step sizes in each direction (Far1,2,3 and Near1,2,3)

Get Files and Folders on a Camera

This is a bit a tricky method as it is recursive (or actually the subroutine GetChildren is). Basically it goes through every volume and every folder there is on the camera and extracts names and thumbnails:

Note: This method should not be used while the live view is running. The program is likely to hang.

Hide   Shrink    Copy Code

public CameraFileEntry GetAllEntries() {     //create the main entry which contains all subentries     CameraFileEntry MainEntry = new CameraFileEntry("Camera", true);     //get the number of volumes currently installed in the camera     int VolumeCount;     Error = EDSDK.EdsGetChildCount(MainCamera.Ref, out VolumeCount);     List<CameraFileEntry> VolumeEntries = new List<CameraFileEntry>();     //iterate through all of them     for (int i = 0; i < VolumeCount; i++)     {         //get information about volume         IntPtr ChildPtr;         Error = EDSDK.EdsGetChildAtIndex(MainCamera.Ref, i, out ChildPtr);         EDSDK.EdsVolumeInfo vinfo;         Error = EDSDK.EdsGetVolumeInfo(ChildPtr, out vinfo);         //ignore the HDD         if (vinfo.szVolumeLabel != "HDD")         {             //add volume to the list             VolumeEntries.Add(new CameraFileEntry("Volume" + i + "(" + vinfo.szVolumeLabel + ")", true));             //get all child entries on this volume             VolumeEntries[i].AddSubEntries(GetChildren(ChildPtr));         }         //release the volume         Error = EDSDK.EdsRelease(ChildPtr);     }     //add all volumes to the main entry and return it     MainEntry.AddSubEntries(VolumeEntries.ToArray());     return MainEntry; }

Subroutines Used in Previous Methods

There are a few subroutines to make the above code shorter and more readable.

This sends a command to the camera safely:

Hide   Copy Code

private void SendSDKCommand(Action command, bool longTask = false) {     if (longTask) STAThread.Create(command).Start();     else STAThread.ExecuteSafely(command); }

This is a general method that downloads data from the camera into a stream:

Hide   Copy Code

private void DownloadData(IntPtr ObjectPointer, IntPtr stream) {     //get information about the object     EDSDK.EdsDirectoryItemInfo dirInfo;     Error = EDSDK.EdsGetDirectoryItemInfo(ObjectPointer, out dirInfo);     try     {         //set progress event         Error = EDSDK.EdsSetProgressCallback(stream, SDKProgressCallbackEvent, EDSDK.EdsProgressOption.Periodically, ObjectPointer);         //download the data         Error = EDSDK.EdsDownload(ObjectPointer, dirInfo.Size, stream);     }     finally     {         //set the download as complete         Error = EDSDK.EdsDownloadComplete(ObjectPointer);         //release object         Error = EDSDK.EdsRelease(ObjectPointer);     } }

This method gets a Bitmap from a stream:

Hide   Shrink    Copy Code

private Bitmap GetImage(IntPtr img_stream, EDSDK.EdsImageSource imageSource) {     IntPtr stream = IntPtr.Zero;     IntPtr img_ref = IntPtr.Zero;     IntPtr streamPointer = IntPtr.Zero;     EDSDK.EdsImageInfo imageInfo;     try     {         //create reference and get image info         Error = EDSDK.EdsCreateImageRef(img_stream, out img_ref);         Error = EDSDK.EdsGetImageInfo(img_ref, imageSource, out imageInfo);         EDSDK.EdsSize outputSize = new EDSDK.EdsSize();         outputSize.width = imageInfo.EffectiveRect.width;         outputSize.height = imageInfo.EffectiveRect.height;         //calculate amount of data         int datalength = outputSize.height * outputSize.width * 3;         //create buffer that stores the image         byte[] buffer = new byte[datalength];         //create a stream to the buffer         Error = EDSDK.EdsCreateMemoryStreamFromPointer(buffer, (uint)datalength, out stream);         //load image into the buffer         Error = EDSDK.EdsGetImage(img_ref, imageSource, EDSDK.EdsTargetImageType.RGB, imageInfo.EffectiveRect, outputSize, stream);         //make BGR from RGB (System.Drawing (i.e. GDI+) uses BGR)         unsafe         {             byte tmp;             fixed (byte* pix = buffer)             {                 for (long i = 0; i < datalength; i += 3)                 {                     tmp = pix[i];        //Save B value                     pix[i] = pix[i + 2]; //Set B value with R value                     pix[i + 2] = tmp;    //Set R value with B value                 }             }         }         //Get pointer to stream         Error = EDSDK.EdsGetPointer(stream, out streamPointer);         //Create bitmap with the data in the buffer         return new Bitmap(outputSize.width, outputSize.height, datalength, PixelFormat.Format24bppRgb, streamPointer);     }     finally     {         //Release all data         if (img_stream != IntPtr.Zero) Error = EDSDK.EdsRelease(img_stream);         if (img_ref != IntPtr.Zero) Error = EDSDK.EdsRelease(img_ref);         if (stream != IntPtr.Zero) Error = EDSDK.EdsRelease(stream);     } }

This is a recursive method that goes through all folders and subfolders on the camera:

Hide   Shrink    Copy Code

private CameraFileEntry[] GetChildren(IntPtr ptr) {     int ChildCount;     //get children of first pointer     Error = EDSDK.EdsGetChildCount(ptr, out ChildCount);     if (ChildCount > 0)     {         //if it has children, create an array of entries         CameraFileEntry[] MainEntry = new CameraFileEntry[ChildCount];         for (int i = 0; i < ChildCount; i++)         {             IntPtr ChildPtr;             //get children of children             Error = EDSDK.EdsGetChildAtIndex(ptr, i, out ChildPtr);             //get the information about this children             EDSDK.EdsDirectoryItemInfo ChildInfo;             Error = EDSDK.EdsGetDirectoryItemInfo(ChildPtr, out ChildInfo);             //create entry from information             MainEntry[i] = new CameraFileEntry(ChildInfo.szFileName, GetBool(ChildInfo.isFolder));             if (!MainEntry[i].IsFolder)             {                 //if it's not a folder, create thumbnail and safe it to the entry                 IntPtr stream;                 Error = EDSDK.EdsCreateMemoryStream(0, out stream);                 Error = EDSDK.EdsDownloadThumbnail(ChildPtr, stream);                 MainEntry[i].AddThumb(GetImage(stream, EDSDK.EdsImageSource.Thumbnail));             }             else             {                 //if it's a folder, check for children with recursion                 CameraFileEntry[] retval = GetChildren(ChildPtr);                 if (retval != null) MainEntry[i].AddSubEntries(retval);             }             //release current children             Error = EDSDK.EdsRelease(ChildPtr);         }         return MainEntry;     }     else return null; }

A Note to the Methods

I did not add every single method that is used in the code here. Just download the source if something is not completely clear. And if you still have a question after that, simply ask me Smile | :)

Note to Threading

All critical camera commands have to be executed on a thread in a single threaded apartment (=STA). From my tests critical commands are all commands that actually communicate with the camera, like setting a property or downloading an image. To make this easier, I created the STAThread class that provides methods to do exactly that. It can execute something synchronized so the program flow is not interrupted or it starts a new thread where it's executed asynchronously. To make sure that two commands don't get executed at the same time (which will result in your application hanging) you have to put a lock around those commands. As a lock-object you have use STAThread.ExecLock. You should put the lock around as little code as possible because every other command has to wait until the previous command has finished.

Using the GUI

This project includes a Winforms UI and a WPF UI. They both work the same way and are here to show you how to use the above code in an actual program.

Note: These GUIs are not meant for production use and are merely here as an example to get you started!

Plug in your camera and select in the list. Click on "Open Session" and start to use the camera.

Select the values in the dropdown menus to set them in the camera.

Use the "StartLV" Button to start the live view and the buttons with the arrows on it can be used to control the focus (only works if the lens is in AF mode and live view is running)

Canon EDSDK Tutorial in C#_第1张图片   Canon EDSDK Tutorial in C#_第2张图片

Points of Interest

This code was tested with:

  • EOS 5D Mark III

  • EOS 7D

  • EOS 40D

  • EOS 700D

  • EOS 600D

  • EOS 550D

  • EOS 500D

  • EOS 450D

  • EOS 100D/Rebel SL1

  • EOS 1200D/Rebel T5

  • EOS 1100D/Rebel T3

If you tried it with a different model, please let me know so I can add it to this list.

And if you have found a bug, have an improvement or a new snippet, I'm happy to hear from you!

History

  • November 2013 - Initial version

  • November 2013 - Bugfix for viewing LiveView and taking photos at the same time

  • December 2013 - Added GetStringProperty and GetStructProperty and added video recording

  • January 2014 - Added SetStringProperty and SetStructProperty and minor changes in the UI

  • January 2014 - Added several new methods and made the code much more secure

  • June 2014 - Changed a few methods to be more secure and revised the code and UI a bit

  • October 2014 - Fixed a few bugs, revised the Winforms UI and added a WPF UI

  • November 2014 - Fixed deadlock issue when the live view is running and camera commands are called

  • April 2015 - Fixed a bug where the camera would hang before or after filming. Fixed a broken link

  • May 2015 - Camera commands are now properly executed on an STA thread. Other smaller fixes.

  • August 2015 - UI: fixed CameraAdded+Shutdown bugs; Added basic error handling. Lib: smaller fixes.

License

This article, along with any associated source code and files, is licensed under The MIT License


你可能感兴趣的:(Canon EDSDK Tutorial in C#)