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
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!
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(); }
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; }
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; } }
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 struct
s:
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);
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"); } }
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 Av
, Tv
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"); } }
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.
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); } }
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); }
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 DownloadEvf
method 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; }
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; }
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); }); }
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); }
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)
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; }
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; }
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
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)
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.
This article, along with any associated source code and files, is licensed under The MIT License