Click here to Skip to main content
15,911,890 members
Articles / Multimedia / Video

Canon EDSDK Tutorial in C#

Rate me:
Please Sign up or sign in to vote.
4.95/5 (147 votes)
25 May 2016MIT14 min read 3.8M   34.7K   239   1.3K
A tutorial about the Canon SDK to remotely control DSLR cameras. From taking photos to using the LiveView.
The documentation provided for Canon EOS Digital SDK is not very complete and it is difficult to find good examples on the internet. This is a tutorial of some of the most important things I have compiled.

New Version Download

Old Version Download

Introduction

The Canon EOS Digital SDK is quite a 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.

The tutorial and the library is written in C# but it is CLS compliant and therefore can be used from any .NET language.

This tutorial includes:

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 it at your own risk!

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:

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 four projects:

  • EDSDKLib: The main project where all the SDK and camera handling happens
  • WinFormsExample: An example project that consumes the EDSDKLib and uses it in a Windows Forms UI application
  • WpfExample: 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 main classes:

  • CanonAPI: The class that mainly handles the SDK lifetime, the connected Cameras and the SDK events
  • Camera: This is used to communicate with a physical camera. Set/Get properties, take photos, download data and more.
  • CanonSDK: This class contains all the native calls to the Canon SDK DLLs and some helper methods to set/get values.
  • STAThread: A helper class to create an STA thread or execute code on an STA thread
  • ErrorHandler: A static class that provides methods to check the SDK return values/error codes

Let's have a closer look into the inner workings of those classes.

Initiate and Terminate the SDK

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

C#
public CanonAPI(bool useCallingThread)
{
    try
    {
        //Ensure that only one caller at a time can increase the counter
        lock (InitLock)
        {
            //If no instance exists yet, initialize everything
            if (RefCount == 0)
            {
                if (useCallingThread)
                {
                    if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
                        throw new ThreadStateException("Calling thread must be in STA");
                    ErrorHandler.CheckError(this, CanonSDK.EdsInitializeSDK());
                }
                else
                {
                    //Trying to trigger DllNotFoundException so it's not thrown
                    //in the event loop on a different thread:
                    CanonSDK.EdsRelease(IntPtr.Zero);

                    //Start the main thread where SDK will run on
                    MainThread = new ApiThread();
                    MainThread.Start();
                    //Initialize the SDK on the main thread
                    MainThread.Invoke(() => ErrorHandler.CheckError
                                     (this, CanonSDK.EdsInitializeSDK()));
                }

                CanonSDK.InitializeVersion();
                //Subscribe to the CameraAdded event
                CameraAddedEvent = new SDKCameraAddedHandler(CanonAPI_CameraAddedEvent);
                ErrorHandler.CheckError(this, 
                    CanonSDK.EdsSetCameraAddedHandler(CameraAddedEvent, IntPtr.Zero));
                _IsSDKInitialized = true;
            }
            RefCount++;
        }
    }
    catch
    {
        IsDisposed = true;
        if (MainThread?.IsRunning == true) MainThread.Shutdown();
        throw;
    }
}

Ensure that you keep around an instance of this class, otherwise the GC might collect it and the SDK gets terminated with it (because of the destructor of the CanonAPI class).

And when you close your program, call the Dispose method. The public Dispose method in turn calls this overload:

C#
protected virtual void Dispose(bool managed)
{
    //Ensure that only one caller at a time can decrease the counter
    lock (InitLock)
    {
        if (!IsDisposed)
        {
            //If it's the last instance, release everything
            if (RefCount == 1)
            {
                _IsSDKInitialized = false;//Set beforehand because if an error happens, 
                                          //the SDK will be in an unstable state anyway

                //Remove event handler for the CameraAdded event
                ErrorCode err = CanonSDK.EdsSetCameraAddedHandler(null, IntPtr.Zero);
                if (managed)
                {
                    ErrorHandler.CheckError(this, err);
                    //Dispose all the connected cameras
                    CurrentCameras.ForEach(t => t.Dispose());
                }
                //Terminate the SDK
                if (MainThread?.IsRunning == true) 
                    err = MainThread.Invoke(() => { return CanonSDK.EdsTerminateSDK(); });
                //Close the main thread
                if (MainThread?.IsRunning == true) MainThread.Shutdown();
                if (managed) ErrorHandler.CheckError(this, err);
            }
            RefCount--;
            IsDisposed = true;
        }
    }
}

Getting Connected Cameras

Now that the SDK is initialized, we can get a list of currently connected cameras:

C#
public List<Camera> GetCameraList()
{
    if (IsDisposed) throw new ObjectDisposedException(nameof(CanonAPI));

    //Ensure that only one caller at a time can access the camera list
    lock (CameraLock)
    {
        //Get a list of camera pointers
        IEnumerable<IntPtr> ptrList = GetCameraPointerList();
        List<Camera> camList = new List<Camera>();

        //Find cameras that were connected before and add new ones
        foreach (var ptr in ptrList)
        {
            var oldCam = CurrentCameras.FirstOrDefault(t => t.Reference == ptr);
            if (oldCam != null && !oldCam.IsDisposed) 
                camList.Add(oldCam);           //Pointer exists already so we reuse it
            else camList.Add(new Camera(ptr)); //Pointer does not exists yet, so we add it
        }

        //Ensure that cameras not connected anymore are disposed properly
        var oldCameras = CurrentCameras.Where(t => !ptrList.Any(u => u == t.Reference));
        foreach (var cam in oldCameras) { if (!cam.IsDisposed) cam.Dispose(); }

        CurrentCameras.Clear();
        CurrentCameras.AddRange(camList);
        return camList;
    }
}

The CanonAPI class keeps a list of connected cameras to ensure that each SDK camera pointer is associated with only one Camera class instance. Having two or more Camera class instances that use the same SDK pointer could potentially cause problems if they access the camera at the same time.

The subroutine GetCameraPointerList that is used above gets a list of pointers that point to the SDKs camera objects:

C#
protected IEnumerable<IntPtr> GetCameraPointerList()
{
    if (IsDisposed) throw new ObjectDisposedException(nameof(CanonAPI));

    IntPtr camlist;
    //Get camera list
    ErrorHandler.CheckError(this, CanonSDK.EdsGetCameraList(out camlist));

    //Get number of connected cameras
    int camCount;
    ErrorHandler.CheckError(this, CanonSDK.EdsGetChildCount(camlist, out camCount));
    List<IntPtr> ptrList = new List<IntPtr>();
    for (int i = 0; i < camCount; i++)
    {
        //Get camera pointer
        IntPtr cptr;
        ErrorHandler.CheckError(this, CanonSDK.EdsGetChildAtIndex(camlist, i, out cptr));
        ptrList.Add(cptr);
    }
    //Release the list
    ErrorHandler.CheckError(this, CanonSDK.EdsRelease(camlist));
    return ptrList;
}

Open and Close a Session with a Camera

For now, we are done with the CanonAPI class and can start using the Camera and open a session with it:

C#
public void OpenSession()
{
    CheckState(false);

    if (!SessionOpen)
    {
        MainThread.Invoke(() =>
        {
            ErrorHandler.CheckError(this, CanonSDK.EdsOpenSession(CamRef));
                    
            //Check if Record is available
            uint property;
            _IsRecordAvailable = CanonSDK.GetPropertyData
                (CamRef, PropertyID.Record, 0, out property) == ErrorCode.OK;

            //Subscribe to events
            SDKStateEvent += new SDKStateEventHandler(Camera_SDKStateEvent);
            SDKPropertyEvent += new SDKPropertyEventHandler(Camera_SDKPropertyEvent);
            SDKProgressCallbackEvent += new SDKProgressCallback
                                        (Camera_SDKProgressCallbackEvent);
            SDKObjectEvent += new SDKObjectEventHandler(Camera_SDKObjectEvent);
            ErrorHandler.CheckError(this, 
            CanonSDK.EdsSetCameraStateEventHandler
                     (CamRef, StateEventID.All, SDKStateEvent, CamRef));
            ErrorHandler.CheckError(this, 
            CanonSDK.EdsSetObjectEventHandler
                     (CamRef, ObjectEventID.All, SDKObjectEvent, CamRef));
            ErrorHandler.CheckError(this, 
            CanonSDK.EdsSetPropertyEventHandler
                     (CamRef, PropertyEventID.All, SDKPropertyEvent, CamRef));

            SessionOpen = true;
        });
    }
}

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

C#
public void CloseSession()
{
    CheckState(false);

    if (SessionOpen)
    {
        //Unsubscribe from all events
        UnsubscribeEvents();

        //If the live view is on, stop it
        if (IsLiveViewOn)
        {
            KeepLVAlive = false;
            LVThread.Join(5000);
        }

        MainThread.Invoke(() =>
        {
            //Close the session with the camera
            ErrorHandler.CheckError(this, CanonSDK.EdsCloseSession(CamRef));
            SessionOpen = false;
        });
    }
}

You can safely open and close the session multiple times while the camera is connected to the computer.

If you are completely done with this camera, you can dispose it by calling the Dispose method (which, as before, internally calls this overload):

C#
protected virtual void Dispose(bool managed)
{
    if (!IsDisposed)
    {
        //Unsubscribe from all events
        UnsubscribeEvents();

        //If the live view is on, stop it
        if (IsLiveViewOn)
        {
            KeepLVAlive = false;
            LVThread.Join();
        }
        IsLiveViewOn = false;

        MainThread.Invoke(() =>
        {
            if (CanonAPI.IsSDKInitialized)
            {
                //If it's open, close the session
                if (SessionOpen) CanonSDK.EdsCloseSession(CamRef);
                //Release the camera
                CanonSDK.EdsRelease(CamRef);
            }
            _IsDisposed = true;
        });
        //Shutdown the main camera thread
        MainThread.Shutdown();
    }
}

UnsubscribeEvents is just a little helper method:

C#
private void UnsubscribeEvents()
{
    SDKStateEvent -= Camera_SDKStateEvent;
    SDKPropertyEvent -= Camera_SDKPropertyEvent;
    SDKProgressCallbackEvent -= Camera_SDKProgressCallbackEvent;
    SDKObjectEvent -= Camera_SDKObjectEvent;

    if (CanonAPI.IsSDKInitialized)
    {
        MainThread.Invoke(() =>
        {
            //Clear callbacks from Canon SDK
            CanonSDK.EdsSetCameraStateEventHandler(CamRef, StateEventID.All, null, CamRef);
            CanonSDK.EdsSetObjectEventHandler(CamRef, ObjectEventID.All, null, CamRef);
            CanonSDK.EdsSetPropertyEventHandler(CamRef, PropertyEventID.All, null, CamRef);
        });
    }
}

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 int values (like Tv, Av or ISO):

C#
public int GetInt32Setting(PropertyID propID, int inParam = 0)
{
    CheckState();

    return MainThread.Invoke(() =>
    {
        int property;
        ErrorHandler.CheckError(this, CanonSDK.GetPropertyData
                               (CamRef, propID, inParam, out property));
        return property;
    });
}

There are multiple methods for getting values of different types but they all look basically the same. The important part is that all of them call the CanonSDK.GetPropertyData method that handles all the details. If you are interested in how to get values from managed to unmanaged, have a look at the mentioned method and its overloads in the CanonSDK class.

Set Camera Settings

Setting a camera setting is similar to getting it but there are only two methods because we can handle it in a more generic way with the object type.

Any value other than a string can be set with this method (e.g., integers for Tv, Av or ISO):

C#
public void SetSetting(PropertyID propID, object value, int inParam = 0)
{
    CheckState();

    MainThread.Invoke(() =>
    {
        int propsize;
        DataType proptype;
        ErrorHandler.CheckError(this, 
        CanonSDK.EdsGetPropertySize(CamRef, propID, inParam, out proptype, out propsize));
        ErrorHandler.CheckError(this, 
        CanonSDK.EdsSetPropertyData(CamRef, propID, inParam, propsize, value));
    });
}

Setting a string value:

C#
public void SetSetting(PropertyID propID, string value, int inParam = 0, int MAX = 32)
{
    CheckState();

    if (value == null) value = string.Empty;
    if (value.Length > MAX - 1) value = value.Substring(0, MAX - 1);

    byte[] propBytes = System.Text.Encoding.ASCII.GetBytes(value + '\0');
    MainThread.Invoke(() =>
    {
        ErrorHandler.CheckError(this, CanonSDK.EdsSetPropertyData(CamRef,
        propID, inParam, propBytes.Length, propBytes));
    });
}

String length can be limited with the MAX parameter because some camera properties can only have a specific length. Most of them have a length of 32 characters though.

Get a List of Available Settings

Different cameras have different settings available 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". This method returns an array of CameraValues. Each CameraValue has an integer value (that's the ID used by the SDK), a string value (that can be used as a label, e.g., "ISO 100" or "1/100") and, if applicable, a double value (that can be used for calculations, e.g., ISO 100 has a value of 100 and a Tv value of 1/100 has the value 0.01):

C#
public CameraValue[] GetSettingsList(PropertyID propId)
{
    CheckState();

    if (propId == PropertyID.AEModeSelect || propId == PropertyID.ISO || 
    propId == PropertyID.Av || propId == PropertyID.Tv
        || propId == PropertyID.MeteringMode || 
        propId == PropertyID.ExposureCompensation)
    {
        CameraValue[] vals = null;
        PropertyDesc des = default(PropertyDesc);
        MainThread.Invoke(() => ErrorHandler.CheckError
        (this, CanonSDK.EdsGetPropertyDesc(CamRef, propId, out des)));
        vals = new CameraValue[des.NumElements];
        for (int i = 0; i < vals.Length; i++) 
            vals[i] = new CameraValue(des.PropDesc[i], propId);
        return vals;
    }
    else throw new ArgumentException($"Method cannot be used with Property ID {propId}");
}

In the library, there are also five corresponding classes for each type that supports this method. These classes contain a list of all possible values and some regularly used fields (like Auto or Bulb). These classes are called AvValues, TvValues, ISOValues, ExpCompValues, AEModeValues and MeteringModeValues. All methods, properties and fields of those classes are static.

Send a Command to the Camera

With camera commands, you can control several things, for example, take a picture, press the shutter button or drive the focus motor of your lens.

C#
public void SendCommand(CameraCommand command, int inParam = 0)
{
    CheckState();
    MainThread.Invoke(() => ErrorHandler.CheckError
    (this, CanonSDK.EdsSendCommand(CamRef, command, inParam)));
}

Some of the commands are used in the following topics.

To change the lock status of your camera or to enter/exit direct transfer mode, you can use the camera status commands:

C#
public void SendCommand(CameraCommand command, int inParam = 0)
{
    CheckState();
    MainThread.Invoke(() => ErrorHandler.CheckError
               (this, CanonSDK.EdsSendCommand(CamRef, command, inParam)));
}

Taking a Photo (Normal and Bulb)

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

C#
public void TakePhoto()
{
    CheckState();
    SendCommand(CameraCommand.TakePicture);
}

Alternatively, you can use the TakePhotoAsync method that executes the command asynchronously (on a Threadpool Thread, not on a .NET 4.5 Task).

Or if your camera supports it, you can use the TakePhotoShutter method that uses the PressShutterButton command instead of the TakePicture command:

C#
public void TakePhotoShutter()
{
    CheckState();
    SendCommand(CameraCommand.PressShutterButton, (int)ShutterButton.Completely);
    SendCommand(CameraCommand.PressShutterButton, (int)ShutterButton.OFF);
}

Same as before, you can also use the TakePhotoShutterAsync method for asynchronous execution.

To take a photo in bulb mode, call the TakePhotoBulb method:

C#
public void TakePhotoBulb(int bulbTime)
{
    CheckState();

    SendCommand(CameraCommand.BulbStart);
    Thread.Sleep(bulbTime);
    SendCommand(CameraCommand.BulbEnd);
}

Note that you have to have the Tv value on Bulb before calling this method, otherwise you'll get an error. The opposite applies to the TakePhoto methods, there, Tv must not be set to Bulb.

If you would like to download the taken image directly to your computer, have a look at the next topic.

Download a Taken Photo to the Computer

To save taken photos directly onto the computer instead of the camera-memory, set it by calling the SetSetting method:

C#
SetSetting(PropertyID.SaveTo, (int)SaveTo.Host); //SaveTo.Both would save the image 
                                                 //to the camera AND the computer

Most cameras require that you set the remaining disk space on your computer before taking a picture. If you do not do this, most cameras assume that there is no disk space left and will return an error.

You can call the SetCapacity method to set the remaining disk space (if you want, you can just set an arbitrarily high value):

C#
public void SetCapacity(int bytesPerSector, int numberOfFreeClusters)
{
    CheckState();
    MainThread.Invoke(() =>
    {
        Capacity capacity = new Capacity(numberOfFreeClusters, bytesPerSector, true);
        ErrorHandler.CheckError(this, CanonSDK.EdsSetCapacity(CamRef, capacity));
    });
}

Once you have taken a photo, the SDKObjectEvent will fire with the inEvent variable being ObjectEventID.DirItemRequestTransfer. For convenience, the Camera class has a DownloadReady event that fires with all the info you need.

The DownloadReady event has two parameters, one is the Camera object from which the event was fired and the second being a DownloadInfo object. The DownloadInfo object provides some information about the file and can be passed to three methods:

The DownloadFile method downloads the image into the provided directory. To get or set the file name, you can use the FileName property of the DownloadInfo object.

C#
public void DownloadFile(DownloadInfo Info, string directory)
{
    CheckState();
    if (Info == null) throw new ArgumentNullException(nameof(Info));
    if (directory == null || string.IsNullOrEmpty(directory.Trim())) directory = ".";

    string currentFile = Path.Combine(directory, Info.FileName);
    if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
    DownloadToFile(Info, currentFile);
}

The DownloadFile method without the directory parameter downloads the image into a System.IO.Stream instead of saving it to the file system.

C#
public Stream DownloadFile(DownloadInfo Info)
{
    CheckState();
    if (Info == null) throw new ArgumentNullException(nameof(Info));
    return DownloadToStream(Info);
}

With the CancelDownload method, you can cancel and discard the taken image:

C#
public void CancelDownload(DownloadInfo Info)
{
    CheckState();
    if (Info == null) throw new ArgumentNullException(nameof(Info));

    MainThread.Invoke(() =>
    {
        ErrorHandler.CheckError(this, CanonSDK.EdsDownloadCancel(Info.Reference));
        ErrorHandler.CheckError(this, CanonSDK.EdsRelease(Info.Reference));
    });
}

The two DownloadFile methods used these subroutines to do the actual work:

C#
protected void DownloadToFile(DownloadInfo Info, string filepath)
{
    using (var stream = new SDKStream
          (filepath, FileCreateDisposition.CreateAlways, FileAccess.ReadWrite))
    {
        DownloadData(Info, stream.Reference);
    }
}

and:

C#
protected Stream DownloadToStream(DownloadInfo Info)
{
    SDKStream stream = new SDKStream(Info.Size64);
    DownloadData(Info, stream.Reference);
    stream.Position = 0;
    return stream;
}

The SDKStream class is a wrapper around SDK streams so it can be used like a normal System.IO.Stream.

And this is the real download:

C#
protected void DownloadData(DownloadInfo Info, IntPtr stream)
{
    MainThread.Invoke(() =>
    {
        try
        {
            //Set the progress callback
            ErrorHandler.CheckError(this, CanonSDK.EdsSetProgressCallback
            (stream, SDKProgressCallbackEvent, ProgressOption.Periodically, Info.Reference));
            //Check which SDK version is used and download the data with the correct method
            if (CanonSDK.IsVerGE34) ErrorHandler.CheckError
            (this, CanonSDK.EdsDownload(Info.Reference, Info.Size64, stream));
            else ErrorHandler.CheckError
            (this, CanonSDK.EdsDownload(Info.Reference, Info.Size, stream));
        }
        finally
        {
            //Release all data
            ErrorHandler.CheckError(this, CanonSDK.EdsDownloadComplete(Info.Reference));
            ErrorHandler.CheckError(this, CanonSDK.EdsRelease(Info.Reference));
        }
    });
}

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:

C#
public void StartLiveView()
{
    CheckState();
    if (!IsLiveViewOn) SetSetting(PropertyID.Evf_OutputDevice, (int)EvfOutputDevice.PC);
}

Once this is done, the SDKPropertyEvent will fire with the inPropertyID variable being PropertyID.Evf_OutputDevice:

C#
private ErrorCode Camera_SDKPropertyEvent
(PropertyEventID inEvent, PropertyID inPropertyID, int inParameter, IntPtr inContext)
{
    ThreadPool.QueueUserWorkItem((state) =>
    {
        try
        {
            if (inPropertyID == PropertyID.Evf_OutputDevice || 
                inPropertyID == PropertyID.Record)
            {
                lock (lvThreadLockObj)
                {
                    EvfOutputDevice outDevice = GetEvf_OutputDevice();
                    Recording recordState = IsRecordAvailable ? 
                    ((Recording)GetInt32Setting(PropertyID.Record)) : Recording.Off;

                    if (outDevice == EvfOutputDevice.PC || 
                    (recordState == Recording.Ready && 
                                    outDevice == EvfOutputDevice.Filming) ||
                        (useFilmingPcLv && recordState == Recording.On && 
                        (outDevice == EvfOutputDevice.Filming || 
                         outDevice == EvfOutputDevice.Camera)))
                    {
                        if (!KeepLVAlive)
                        {
                            KeepLVAlive = true;
                            LVThread = STAThread.CreateThread(DownloadEvf);
                            LVThread.Start();
                        }
                    }
                    else if (KeepLVAlive) { KeepLVAlive = false; }
                }
            }
        }
        catch (Exception ex) 
        { if (!IsDisposed && !ErrorHandler.ReportError(this, ex)) throw; }

        PropertyChanged?.Invoke(this, inEvent, inPropertyID, inParameter);
    });
    return ErrorCode.OK;
}

There are a few more checks in there to ensure that the live view also works while filming.

And the DownloadEvf method being:

C#
private void DownloadEvf()
{
    if (IsLiveViewOn) return;

    try
    {
        //Create variables
        IntPtr evfImageRef = IntPtr.Zero;
        ErrorCode err;

        //Create stream
        using (var stream = new SDKStream(0))
        {
            IsLiveViewOn = true;
                    
            //Run live view
            while (KeepLVAlive)
            {
                //Get live view image
                lock (STAThread.ExecLock)
                {
                    err = CanonSDK.EdsCreateEvfImageRef(stream.Reference, out evfImageRef);
                    if (err == ErrorCode.OK) err = 
                        CanonSDK.EdsDownloadEvfImage(CamRef, evfImageRef);
                }

                //Check for errors
                if (err == ErrorCode.OBJECT_NOTREADY) { continue; }
                else if (err != ErrorCode.OK) { ErrorHandler.CheckError(err); continue; }

                //Release current evf image
                CanonSDK.EdsRelease(evfImageRef);
                        
                //Set stream position back to zero
                stream.Position = 0;

                //Update live view
                LiveViewUpdated?.Invoke(this, stream);
            }
        }
    }
    catch (Exception ex) { if (ex is ThreadAbortException || 
                               !ErrorHandler.ReportError(this, ex)) throw; }
    finally
    {
        IsLiveViewOn = false;
        ThreadPool.QueueUserWorkItem((state) => LiveViewStopped?.Invoke(this));
    }
}

To stop the live view, simply call the StopLiveView method. This will trigger the SDKPropertyEvent again where the KeepLVAlive variable is set to false and the live view loop will stop. With the LVOff parameter, 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.)

C#
public void StopLiveView(bool LVOff = true)
{
    CheckState();
    if (LVOff) SetSetting(PropertyID.Evf_OutputDevice, (int)EvfOutputDevice.Off);
    else SetSetting(PropertyID.Evf_OutputDevice, (int)EvfOutputDevice.Camera);
}

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:

C#
public void StartFilming(bool PCLiveview)
{
    CheckState();

    Recording state = (Recording)GetInt32Setting(PropertyID.Record);
    if (state != Recording.On)
    {
        if (state != Recording.Ready) throw new InvalidOperationException
        ("The camera is not ready to film. The Record property has to be Recording.Ready");
        useFilmingPcLv = PCLiveview;
        //When recording videos, it has to be saved on the camera internal memory
        SetSetting(PropertyID.SaveTo, (int)SaveTo.Camera);
        //Start the video recording
        SetSetting(PropertyID.Record, (int)Recording.On);
    }
}

Note that the SaveTo property is set to Camera. This is a requirement of the SDK, presumably because the data transfer to the computer would be too slow.

Still, you can download the finished film after you stopped filming just by setting the saveFilm parameter of the StopFilming method to true:

C#
public void StopFilming(bool saveFilm, bool stopLiveView)
{
    CheckState();

    Recording state = (Recording)GetInt32Setting(PropertyID.Record);
    if (state == Recording.On)
    {
        this.saveFilm = saveFilm;
        //Stop video recording
        SetSetting(PropertyID.Record, (int)Recording.Off);
        useFilmingPcLv = false;
        if (IsLiveViewOn && stopLiveView) StopLiveView(false);
    }
}

Internally, it checks the SDKObjectEvent that will fire with inEvent being ObjectEventID.DirItemCreated. We can safely assume that this created item is the film. Same as with downloading a photo, the DownloadReady event will fire and you can download the film with one of the DownloadFile methods.

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:

C#
public void UILock(bool lockState)
{
    if (lockState) SendStatusCommand(CameraStatusCommand.UILock);
    else SendStatusCommand(CameraStatusCommand.UIUnLock);
}

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 importantly, the camera has to be in live view and the lens has to be on AF. Then, you can simply call SendCommand with DriveLensEvf as the command type:

C#
SendCommand(CameraCommand.DriveLensEvf, (int)DriveLens.Near2);

To control distance and direction, you have to set the DriveLens enum appropriately. Near is to focus closer, Far is to focus further away and 1 is a small, 2 is a medium and 3 is a big step.

Files and Folders on a Camera

There are several methods for handling the file system of the camera:

  • Use GetAllVolumes to get all camera volumes (e.g., CF or SD cards)
  • Use GetAllEntries to get all volumes, files and folders on the camera
  • Use GetAllImages to get all images on the camera
  • Use FormatVolume to format a volume
  • Use DownloadFiles to download one or more files into a directory
  • Use DeleteFiles to delete one or more files on the camera

I won't go into detail here but feel free to look at the source code of these methods to find out how exactly they work.

Get the Thumbnail of an Image

Sometimes, it is useful to get a thumbnail from an image, regardless if RAW or jpg. To do that, you can call the GetFileThumb method of the CanonAPI class.

C#
public Bitmap GetFileThumb(string filepath)
{
    //create a file stream to given file
    using (var stream = new SDKStream
          (filepath, FileCreateDisposition.OpenExisting, FileAccess.Read))
    {
        //Create a thumbnail Bitmap from the stream
        return GetImage(stream.Reference, ImageSource.Thumbnail);
    }
}

whereas the more generic method GetImage is defined like this:

C#
protected Bitmap GetImage(IntPtr imgStream, ImageSource imageSource)
{
    IntPtr imgRef = IntPtr.Zero;
    IntPtr streamPointer = IntPtr.Zero;
    ImageInfo imageInfo;

    try
    {
        //create reference and get image info
        ErrorHandler.CheckError(this, 
            CanonSDK.EdsCreateImageRef(imgStream, out imgRef));
        ErrorHandler.CheckError(this, 
            CanonSDK.EdsGetImageInfo(imgRef, imageSource, out imageInfo));

        Size outputSize = new Size();
        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
        using (var stream = new SDKStream(buffer))
        {
            //load image into the buffer
            ErrorHandler.CheckError(this, CanonSDK.EdsGetImage
            (imgRef, imageSource, TargetImageType.RGB, 
             imageInfo.EffectiveRect, outputSize, stream.Reference));

            //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 data
            ErrorHandler.CheckError(this, 
                CanonSDK.EdsGetPointer(stream.Reference, 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 (imgStream != IntPtr.Zero) ErrorHandler.CheckError
           (this, CanonSDK.EdsRelease(imgStream));
        if (imgRef != IntPtr.Zero) ErrorHandler.CheckError
           (this, CanonSDK.EdsRelease(imgRef));
    }
}

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. :)

Note to Threading

The Canon SDK requires the use of threads in a single threaded apartment (=STA) which makes things a bit difficult. Lucky for you, I added the STAThread class that handles the setup and issues that arise with this threading model. The CanonAPI class has one thread that is used for initializing and terminating the SDK and to get all SDK events. Each Camera instance has one thread that is used to execute all commands on. To ensure that nothing executes at the same time, all commands are surrounded by a lock (with the ExecLock field as lock object). Should two commands execute at the same time, the SDK will hang completely in most cases. All methods provided by the CanonAPI and the Camera are safe to execute but if you want to call methods from the CanonSDK class, you have to make sure to execute it on the right thread (by calling the Invoke method of the STAThread class). If you have any issues with the SDK hanging, please let me know.

Using the GUI

This project includes a Window Forms 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).

Image 1

Image 2

Points of Interest

This code was tested with:

  • EOS 5D Mark III
  • EOS 7D
  • EOS 40D
  • EOS 60D
  • EOS 700D
  • EOS 600D
  • EOS 550D
  • EOS 500D
  • EOS 450D
  • EOS 100D/Rebel SL1
  • EOS 1200D/Rebel T5
  • EOS 1100D/Rebel T3
  • and several others

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

  • March 2016 (1.0.0) - Complete revision of the project. It now uses a similar architecture to the commercial library.
  • March 2016 (1.0.1) - Fixed wrong check in StopFilming method
  • April 2016 (1.1.0) - Several bugfixes, improvements and support for Canon SDK 3.4
  • April 2016 (1.1.1) - Fixed incorrect lock in STAThread.Invoke method (thanks Dave)
  • May 2016 (1.1.2) - Various smaller fixes (thanks elgarf) and added LiveViewStopped event

License

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


Written By
Software Developer
Austria Austria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
AnswerRe: Multiple camera invoke side by side in single Instance. Pin
Johannes Bildstein15-Jul-15 2:10
professionalJohannes Bildstein15-Jul-15 2:10 
GeneralRe: Multiple camera invoke side by side in single Instance. Pin
futurevj16-Jul-15 20:22
futurevj16-Jul-15 20:22 
GeneralRe: Multiple camera invoke side by side in single Instance. Pin
Johannes Bildstein18-Jul-15 10:41
professionalJohannes Bildstein18-Jul-15 10:41 
GeneralRe: Multiple camera invoke side by side in single Instance. Pin
Amar Chaudhary28-Aug-15 12:19
Amar Chaudhary28-Aug-15 12:19 
GeneralRe: Multiple camera invoke side by side in single Instance. Pin
Amar Chaudhary31-Aug-15 1:26
Amar Chaudhary31-Aug-15 1:26 
GeneralRe: Multiple camera invoke side by side in single Instance. Pin
Johannes Bildstein1-Sep-15 0:23
professionalJohannes Bildstein1-Sep-15 0:23 
GeneralRe: Multiple camera invoke side by side in single Instance. Pin
Amar Chaudhary1-Sep-15 1:28
Amar Chaudhary1-Sep-15 1:28 
QuestionRe: Multiple camera invoke side by side in single Instance. Pin
Amar Chaudhary15-Sep-15 15:47
Amar Chaudhary15-Sep-15 15:47 
Hi
I started working on the same..
Encountering issue
- camera gets hang [keeps returning error code 81 - busy] when I try to run the code..

Please also see if I am going in right direction.

Regards
Amar
Code for reference :

This is how I am calling the class:
C#
private void cmdPreview_Click(object sender, RoutedEventArgs e)
        {
            var camL = (pnlLeftCam as StackPanel).DataContext as CameraInfo;
            var camR = (pnlRightCam as StackPanel).DataContext as CameraInfo;
            if (camL == null || camR == null)
            {
                MessageBox.Show("Please select Camera", "Select Both camera");
                return;
            }
            if ((!CameraHandler.IsLeftLiveViewOn))
            {
                CameraHandler.StopLeftLiveView();
                LVCanvas.Background = Brushes.LightGray;
            }
            if ((!CameraHandler.IsRightLiveViewOn))
            {
                CameraHandler.StopRightLiveView();
                RVCanvas.Background = Brushes.LightGray;
            }
            CameraHandler.CloseLeftSession();
            CameraHandler.CloseRightSession();
            CameraHandler.OpenSession(camL.Camera, camR.Camera);
            if (!CameraHandler.IsLeftLiveViewOn)
            {
                CameraHandler.PressLeftShutterButton(EDSDK.EdsShutterButton.CameraCommand_ShutterButton_Halfway);
                bgbrush.Stretch = Stretch.Uniform;

                LVCanvas.LayoutTransform = new RotateTransform(270, 0, 0);
                LVCanvas.Background = bgbrush;
                CameraHandler.StartLeftLiveView();
            }
            else
            {

                CameraHandler.StopLeftLiveView();
                LVCanvas.Background = Brushes.LightGray;
            }
            if (!CameraHandler.IsRightLiveViewOn)
            {
                CameraHandler.PressRightShutterButton(EDSDK.EdsShutterButton.CameraCommand_ShutterButton_Halfway);
                bgbrush.Stretch = Stretch.Uniform;

                RVCanvas.LayoutTransform = new RotateTransform(90, 0, 0);
                RVCanvas.Background = bgbrush;
                CameraHandler.StartRightLiveView();
            }
            else
            {

                CameraHandler.StopRightLiveView();
                RVCanvas.Background = Brushes.LightGray;
            }
 }


The modified code :
C#
using System;
using System.IO;
using System.Linq;
using System.Drawing;
using System.Threading;
using System.Globalization;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace EDSDKLib
{
    /// <summary>
    /// Handles the Canon SDK
    /// </summary>
    public class SDKHandler : IDisposable
    {
        #region Left Variables

        /// <summary>
        /// The used camera
        /// </summary>
        public Camera LeftCamera { get; private set; }
        /// <summary>
        /// States if a session with the MainCamera is opened
        /// </summary>
        public bool LeftCameraSessionOpen { get; private set; }
        /// <summary>
        /// States if the live view is on or not
        /// </summary>
        public bool IsLeftLiveViewOn { get; private set; }
        /// <summary>
        /// States if camera is recording or not
        /// </summary>
        public bool IsLeftFilming { get; private set; }
        /// <summary>
        /// Directory to where photos will be saved
        /// </summary>
        public string LeftImageSaveDirectory { get; set; }
        /// <summary>
        /// The focus and zoom border rectangle for live view (set after first use of live view)
        /// </summary>
        public EDSDK.EdsRect Left_Evf_ZoomRect { get; private set; }
        /// <summary>
        /// The focus and zoom border position of the live view (set after first use of live view)
        /// </summary>
        public EDSDK.EdsPoint Left_Evf_ZoomPosition { get; private set; }
        /// <summary>
        /// The cropping position of the enlarged live view image (set after first use of live view)
        /// </summary>
        public EDSDK.EdsPoint Left_Evf_ImagePosition { get; private set; }
        /// <summary>
        /// The live view coordinate system (set after first use of live view)
        /// </summary>
        public EDSDK.EdsSize Left_Evf_CoordinateSystem { get; private set; }
        /// <summary>
        /// States if the Evf_CoordinateSystem is already set
        /// </summary>
        public bool IsLeftCoordSystemSet = false;
        /// <summary>
        /// Handles errors that happen with the SDK
        /// </summary>
        public uint LeftError
        {
            get { return EDSDK.EDS_ERR_OK; }
            set { if (value != EDSDK.EDS_ERR_OK) throw new Exception("SDK Error: 0x" + value.ToString("X")); }
        }


        /// <summary>
        /// States if a finished video should be downloaded from the camera
        /// </summary>
        private bool Left_DownloadVideo;
        /// <summary>
        /// For video recording, SaveTo has to be set to Camera. This is to store the previous setting until after the filming.
        /// </summary>
        private uint Left_PrevSaveTo;
        /// <summary>
        /// The thread on which the live view images will get downloaded continuously
        /// </summary>
        private Thread Left_LVThread;
        /// <summary>
        /// If true, the live view will be shut off completely. If false, live view will go back to the camera.
        /// </summary>
        private bool Left_LVoff;

        #endregion

        #region Right Variables

        /// <summary>
        /// The used camera
        /// </summary>
        public Camera RightCamera { get; private set; }
        /// <summary>
        /// States if a session with the MainCamera is opened
        /// </summary>
        public bool RightCameraSessionOpen { get; private set; }
        /// <summary>
        /// States if the live view is on or not
        /// </summary>
        public bool IsRightLiveViewOn { get; private set; }
        /// <summary>
        /// States if camera is recording or not
        /// </summary>
        public bool IsRightFilming { get; private set; }
        /// <summary>
        /// Directory to where photos will be saved
        /// </summary>
        public string RightImageSaveDirectory { get; set; }
        /// <summary>
        /// The focus and zoom border rectangle for live view (set after first use of live view)
        /// </summary>
        public EDSDK.EdsRect Right_Evf_ZoomRect { get; private set; }
        /// <summary>
        /// The focus and zoom border position of the live view (set after first use of live view)
        /// </summary>
        public EDSDK.EdsPoint Right_Evf_ZoomPosition { get; private set; }
        /// <summary>
        /// The cropping position of the enlarged live view image (set after first use of live view)
        /// </summary>
        public EDSDK.EdsPoint Right_Evf_ImagePosition { get; private set; }
        /// <summary>
        /// The live view coordinate system (set after first use of live view)
        /// </summary>
        public EDSDK.EdsSize Right_Evf_CoordinateSystem { get; private set; }
        /// <summary>
        /// States if the Evf_CoordinateSystem is already set
        /// </summary>
        public bool IsRightCoordSystemSet = false;
        /// <summary>
        /// Handles errors that happen with the SDK
        /// </summary>
        public uint RightError
        {
            get { return EDSDK.EDS_ERR_OK; }
            set { if (value != EDSDK.EDS_ERR_OK) throw new Exception("SDK Error: 0x" + value.ToString("X")); }
        }


        /// <summary>
        /// States if a finished video should be downloaded from the camera
        /// </summary>
        private bool Right_DownloadVideo;
        /// <summary>
        /// For video recording, SaveTo has to be set to Camera. This is to store the previous setting until after the filming.
        /// </summary>
        private uint Right_PrevSaveTo;
        /// <summary>
        /// The thread on which the live view images will get downloaded continuously
        /// </summary>
        private Thread Right_LVThread;
        /// <summary>
        /// If true, the live view will be shut off completely. If false, live view will go back to the camera.
        /// </summary>
        private bool Right_LVoff;

        #endregion
        #region Events

        #region SDK Events

        public event EDSDK.EdsCameraAddedHandler SDKCameraAddedEvent;
        public event EDSDK.EdsObjectEventHandler SDKObjectEvent;
        public event EDSDK.EdsProgressCallback SDKProgressCallbackEvent;
        public event EDSDK.EdsPropertyEventHandler SDKPropertyEvent;
        public event EDSDK.EdsStateEventHandler SDKStateEvent;

        #endregion

        #region Custom Events

        public delegate void CameraAddedHandler();
        public delegate void ProgressHandler(int Progress);
        public delegate void StreamUpdate(Stream img);
        public delegate void BitmapUpdate(Bitmap bmp);

        /// <summary>
        /// Fires if a camera is added
        /// </summary>
        public event CameraAddedHandler CameraAdded;
        /// <summary>
        /// Fires if any process reports progress
        /// </summary>
        public event ProgressHandler ProgressChanged;
        /// <summary>
        /// Fires if the live view image has been updated
        /// </summary>
        public event StreamUpdate LiveViewUpdated;
        /// <summary>
        /// If the camera is disconnected or shuts down, this event is fired
        /// </summary>
        public event EventHandler CameraHasShutdown;
        /// <summary>
        /// If an image is downloaded, this event fires with the downloaded image.
        /// </summary>
        public event BitmapUpdate ImageDownloaded;

        #endregion

        #endregion

        #region Basic SDK and Session handling

        /// <summary>
        /// Initializes the SDK and adds events
        /// </summary>
        public SDKHandler()
        {
            //initialize SDK
            LeftError = EDSDK.EdsInitializeSDK();
            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);
        }
        
        /// <summary>
        /// Get a list of all connected cameras
        /// </summary>
        /// <returns>The camera list</returns>
        public List<Camera> GetCameraList()
        {
            IntPtr camlist;
            //get list of cameras
            LeftError = EDSDK.EdsGetCameraList(out camlist);

            //get each camera from camlist
            int c;
            //get amount of connected cameras
            LeftError = 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
                LeftError = EDSDK.EdsGetChildAtIndex(camlist, i, out cptr);
                OutCamList.Add(new Camera(cptr));
            }
            return OutCamList;
        }

        /// <summary>
        /// Opens a session with given camera
        /// </summary>
        /// <param name="newCamera">The camera which will be used</param>
        /// disabled for the moment
        private void OpenSession(Camera newCamera)
        {
            if (LeftCamera == newCamera)
            {
                if (LeftCameraSessionOpen) CloseLeftSession();
                if (newCamera != null)
                {
                    //open a session
                    SendSDKCommand(delegate { LeftError = EDSDK.EdsOpenSession(LeftCamera.Ref); });
                    //subscribe to the camera events (for the SDK)
                    EDSDK.EdsSetCameraStateEventHandler(LeftCamera.Ref, EDSDK.StateEvent_All, SDKStateEvent, LeftCamera.Ref);
                    EDSDK.EdsSetObjectEventHandler(LeftCamera.Ref, EDSDK.ObjectEvent_All, SDKObjectEvent, LeftCamera.Ref);
                    EDSDK.EdsSetPropertyEventHandler(LeftCamera.Ref, EDSDK.PropertyEvent_All, SDKPropertyEvent, LeftCamera.Ref);
                    LeftCameraSessionOpen = true;
                }
            }
            else if (RightCamera == newCamera)
            {
                if (RightCameraSessionOpen) CloseRightSession();
                if (newCamera != null)
                {
                    //open a session
                    SendSDKCommand(delegate { RightError = EDSDK.EdsOpenSession(RightCamera.Ref); });
                    //subscribe to the camera events (for the SDK)
                    EDSDK.EdsSetCameraStateEventHandler(RightCamera.Ref, EDSDK.StateEvent_All, SDKStateEvent, RightCamera.Ref);
                    EDSDK.EdsSetObjectEventHandler(RightCamera.Ref, EDSDK.ObjectEvent_All, SDKObjectEvent, RightCamera.Ref);
                    EDSDK.EdsSetPropertyEventHandler(RightCamera.Ref, EDSDK.PropertyEvent_All, SDKPropertyEvent, RightCamera.Ref);
                    RightCameraSessionOpen = true;
                }
            }
        }
        public void OpenSession(Camera leftCamera, Camera rightCamera)
        {
            if (LeftCameraSessionOpen) CloseLeftSession();
            if (leftCamera != null)
            {
                LeftCamera = leftCamera;
                //open a session
                SendSDKCommand(delegate { LeftError = EDSDK.EdsOpenSession(LeftCamera.Ref); });
                //subscribe to the camera events (for the SDK)
                EDSDK.EdsSetCameraStateEventHandler(LeftCamera.Ref, EDSDK.StateEvent_All, SDKStateEvent, LeftCamera.Ref);
                EDSDK.EdsSetObjectEventHandler(LeftCamera.Ref, EDSDK.ObjectEvent_All, SDKObjectEvent, LeftCamera.Ref);
                EDSDK.EdsSetPropertyEventHandler(LeftCamera.Ref, EDSDK.PropertyEvent_All, SDKPropertyEvent, LeftCamera.Ref);
                LeftCameraSessionOpen = true;
            }
            if (RightCameraSessionOpen) CloseRightSession();
            if (rightCamera != null)
            {
                RightCamera = rightCamera;
                //open a session
                SendSDKCommand(delegate { RightError = EDSDK.EdsOpenSession(RightCamera.Ref); });
                //subscribe to the camera events (for the SDK)
                EDSDK.EdsSetCameraStateEventHandler(RightCamera.Ref, EDSDK.StateEvent_All, SDKStateEvent, RightCamera.Ref);
                EDSDK.EdsSetObjectEventHandler(RightCamera.Ref, EDSDK.ObjectEvent_All, SDKObjectEvent, RightCamera.Ref);
                EDSDK.EdsSetPropertyEventHandler(RightCamera.Ref, EDSDK.PropertyEvent_All, SDKPropertyEvent, RightCamera.Ref);
                RightCameraSessionOpen = true;
            }
        }

        /// <summary>
        /// Closes the session with the current camera
        /// </summary>
        public void CloseLeftSession()
        {
            if (LeftCameraSessionOpen)
            {
                //if live view is still on, stop it and wait till the thread has stopped
                if (IsLeftLiveViewOn)
                {
                    StopLeftLiveView();
                    
                    Left_LVThread.Join(1000);
                }

                //Remove the event handler
                EDSDK.EdsSetCameraStateEventHandler(LeftCamera.Ref, EDSDK.StateEvent_All, null, LeftCamera.Ref);
                EDSDK.EdsSetObjectEventHandler(LeftCamera.Ref, EDSDK.ObjectEvent_All, null, LeftCamera.Ref);
                EDSDK.EdsSetPropertyEventHandler(LeftCamera.Ref, EDSDK.PropertyEvent_All, null, LeftCamera.Ref);

                //close session and release camera
                SendSDKCommand(delegate { LeftError = EDSDK.EdsCloseSession(LeftCamera.Ref); });
                uint c = EDSDK.EdsRelease(LeftCamera.Ref);
                LeftCameraSessionOpen = false;
            }
           
        }
        public void CloseRightSession()
        {
            
            if (RightCameraSessionOpen)
            {
                //if live view is still on, stop it and wait till the thread has stopped
                if (IsRightLiveViewOn)
                {
                    StopRightLiveView();
                    Right_LVThread.Join(1000);
                }

                //Remove the event handler
                EDSDK.EdsSetCameraStateEventHandler(RightCamera.Ref, EDSDK.StateEvent_All, null, RightCamera.Ref);
                EDSDK.EdsSetObjectEventHandler(RightCamera.Ref, EDSDK.ObjectEvent_All, null, RightCamera.Ref);
                EDSDK.EdsSetPropertyEventHandler(RightCamera.Ref, EDSDK.PropertyEvent_All, null, RightCamera.Ref);

                //close session and release camera
                SendSDKCommand(delegate { RightError = EDSDK.EdsCloseSession(RightCamera.Ref); });
                uint c = EDSDK.EdsRelease(RightCamera.Ref);
                RightCameraSessionOpen = false;
            }
        }

        /// <summary>
        /// Closes open session and terminates the SDK
        /// </summary>
        public void Dispose()
        {
            //close session
            CloseLeftSession();
            CloseRightSession();
            //terminate SDK
            LeftError = EDSDK.EdsTerminateSDK();
            RightError = LeftError;
            //stop command execution thread
            STAThread.Shutdown();
        }

        #endregion

        #region Eventhandling

        /// <summary>
        /// A new camera was plugged into the computer
        /// </summary>
        /// <param name="inContext">The pointer to the added camera</param>
        /// <returns>An EDSDK errorcode</returns>
        private uint SDKHandler_CameraAddedEvent(IntPtr inContext)
        {
            //Handle new camera here
            if (CameraAdded != null) CameraAdded();
            return EDSDK.EDS_ERR_OK;
        }

        /// <summary>
        /// An Objectevent fired
        /// </summary>
        /// <param name="inEvent">The ObjectEvent id</param>
        /// <param name="inRef">Pointer to the object</param>
        /// <param name="inContext"></param>
        /// <returns>An EDSDK errorcode</returns>
        private uint Camera_SDKObjectEvent(uint inEvent, IntPtr inRef, IntPtr inContext)
        {
            //handle object event here
            switch (inEvent)
            {
                case EDSDK.ObjectEvent_All:
                    break;
                case EDSDK.ObjectEvent_DirItemCancelTransferDT:
                    break;
                case EDSDK.ObjectEvent_DirItemContentChanged:
                    break;
                case EDSDK.ObjectEvent_DirItemCreated:
                    if (Left_DownloadVideo) { DownloadImage(inRef, LeftImageSaveDirectory); Left_DownloadVideo = false; }
                    if (Right_DownloadVideo) { DownloadImage(inRef, RightImageSaveDirectory); Right_DownloadVideo = false; }
                    break;
                case EDSDK.ObjectEvent_DirItemInfoChanged:
                    break;
                case EDSDK.ObjectEvent_DirItemRemoved:
                    break;
                case EDSDK.ObjectEvent_DirItemRequestTransfer:
                    DownloadImage(inRef, LeftImageSaveDirectory);
                    DownloadImage(inRef, RightImageSaveDirectory);
                    break;
                case EDSDK.ObjectEvent_DirItemRequestTransferDT:
                    break;
                case EDSDK.ObjectEvent_FolderUpdateItems:
                    break;
                case EDSDK.ObjectEvent_VolumeAdded:
                    break;
                case EDSDK.ObjectEvent_VolumeInfoChanged:
                    break;
                case EDSDK.ObjectEvent_VolumeRemoved:
                    break;
                case EDSDK.ObjectEvent_VolumeUpdateItems:
                    break;
            }

            return EDSDK.EDS_ERR_OK;
        }

        /// <summary>
        /// A progress was made
        /// </summary>
        /// <param name="inPercent">Percent of progress</param>
        /// <param name="inContext">...</param>
        /// <param name="outCancel">Set true to cancel event</param>
        /// <returns>An EDSDK errorcode</returns>
        private uint Camera_SDKProgressCallbackEvent(uint inPercent, IntPtr inContext, ref bool outCancel)
        {
            //Handle progress here
            if (ProgressChanged != null) ProgressChanged((int)inPercent);
            return EDSDK.EDS_ERR_OK;
        }

        /// <summary>
        /// A property changed
        /// </summary>
        /// <param name="inEvent">The PropetyEvent ID</param>
        /// <param name="inPropertyID">The Property ID</param>
        /// <param name="inParameter">Event Parameter</param>
        /// <param name="inContext">...</param>
        /// <returns>An EDSDK errorcode</returns>
        private uint Camera_SDKPropertyEvent(uint inEvent, uint inPropertyID, uint inParameter, IntPtr inContext)
        {
            //Handle property event here
            switch (inEvent)
            {
                case EDSDK.PropertyEvent_All:
                    break;
                case EDSDK.PropertyEvent_PropertyChanged:
                    break;
                case EDSDK.PropertyEvent_PropertyDescChanged:
                    break;
            }

            switch (inPropertyID)
            {
                case EDSDK.PropID_AEBracket:
                    break;
                case EDSDK.PropID_AEMode:
                    break;
                case EDSDK.PropID_AEModeSelect:
                    break;
                case EDSDK.PropID_AFMode:
                    break;
                case EDSDK.PropID_Artist:
                    break;
                case EDSDK.PropID_AtCapture_Flag:
                    break;
                case EDSDK.PropID_Av:
                    break;
                case EDSDK.PropID_AvailableShots:
                    break;
                case EDSDK.PropID_BatteryLevel:
                    break;
                case EDSDK.PropID_BatteryQuality:
                    break;
                case EDSDK.PropID_BodyIDEx:
                    break;
                case EDSDK.PropID_Bracket:
                    break;
                case EDSDK.PropID_CFn:
                    break;
                case EDSDK.PropID_ClickWBPoint:
                    break;
                case EDSDK.PropID_ColorMatrix:
                    break;
                case EDSDK.PropID_ColorSaturation:
                    break;
                case EDSDK.PropID_ColorSpace:
                    break;
                case EDSDK.PropID_ColorTemperature:
                    break;
                case EDSDK.PropID_ColorTone:
                    break;
                case EDSDK.PropID_Contrast:
                    break;
                case EDSDK.PropID_Copyright:
                    break;
                case EDSDK.PropID_DateTime:
                    break;
                case EDSDK.PropID_DepthOfField:
                    break;
                case EDSDK.PropID_DigitalExposure:
                    break;
                case EDSDK.PropID_DriveMode:
                    break;
                case EDSDK.PropID_EFCompensation:
                    break;
                case EDSDK.PropID_Evf_AFMode:
                    break;
                case EDSDK.PropID_Evf_ColorTemperature:
                    break;
                case EDSDK.PropID_Evf_DepthOfFieldPreview:
                    break;
                case EDSDK.PropID_Evf_FocusAid:
                    break;
                case EDSDK.PropID_Evf_Histogram:
                    break;
                case EDSDK.PropID_Evf_HistogramStatus:
                    break;
                case EDSDK.PropID_Evf_ImagePosition:
                    break;
                case EDSDK.PropID_Evf_Mode:
                    break;
                case EDSDK.PropID_Evf_OutputDevice:
                    if (IsLeftLiveViewOn || IsRightLiveViewOn) DownloadEvf();
                    break;
                case EDSDK.PropID_Evf_WhiteBalance:
                    break;
                case EDSDK.PropID_Evf_Zoom:
                    break;
                case EDSDK.PropID_Evf_ZoomPosition:
                    break;
                case EDSDK.PropID_ExposureCompensation:
                    break;
                case EDSDK.PropID_FEBracket:
                    break;
                case EDSDK.PropID_FilterEffect:
                    break;
                case EDSDK.PropID_FirmwareVersion:
                    break;
                case EDSDK.PropID_FlashCompensation:
                    break;
                case EDSDK.PropID_FlashMode:
                    break;
                case EDSDK.PropID_FlashOn:
                    break;
                case EDSDK.PropID_FocalLength:
                    break;
                case EDSDK.PropID_FocusInfo:
                    break;
                case EDSDK.PropID_GPSAltitude:
                    break;
                case EDSDK.PropID_GPSAltitudeRef:
                    break;
                case EDSDK.PropID_GPSDateStamp:
                    break;
                case EDSDK.PropID_GPSLatitude:
                    break;
                case EDSDK.PropID_GPSLatitudeRef:
                    break;
                case EDSDK.PropID_GPSLongitude:
                    break;
                case EDSDK.PropID_GPSLongitudeRef:
                    break;
                case EDSDK.PropID_GPSMapDatum:
                    break;
                case EDSDK.PropID_GPSSatellites:
                    break;
                case EDSDK.PropID_GPSStatus:
                    break;
                case EDSDK.PropID_GPSTimeStamp:
                    break;
                case EDSDK.PropID_GPSVersionID:
                    break;
                case EDSDK.PropID_HDDirectoryStructure:
                    break;
                case EDSDK.PropID_ICCProfile:
                    break;
                case EDSDK.PropID_ImageQuality:
                    break;
                case EDSDK.PropID_ISOBracket:
                    break;
                case EDSDK.PropID_ISOSpeed:
                    break;
                case EDSDK.PropID_JpegQuality:
                    break;
                case EDSDK.PropID_LensName:
                    break;
                case EDSDK.PropID_LensStatus:
                    break;
                case EDSDK.PropID_Linear:
                    break;
                case EDSDK.PropID_MakerName:
                    break;
                case EDSDK.PropID_MeteringMode:
                    break;
                case EDSDK.PropID_NoiseReduction:
                    break;
                case EDSDK.PropID_Orientation:
                    break;
                case EDSDK.PropID_OwnerName:
                    break;
                case EDSDK.PropID_ParameterSet:
                    break;
                case EDSDK.PropID_PhotoEffect:
                    break;
                case EDSDK.PropID_PictureStyle:
                    break;
                case EDSDK.PropID_PictureStyleCaption:
                    break;
                case EDSDK.PropID_PictureStyleDesc:
                    break;
                case EDSDK.PropID_ProductName:
                    break;
                case EDSDK.PropID_Record:
                    break;
                case EDSDK.PropID_RedEye:
                    break;
                case EDSDK.PropID_SaveTo:
                    break;
                case EDSDK.PropID_Sharpness:
                    break;
                case EDSDK.PropID_ToneCurve:
                    break;
                case EDSDK.PropID_ToningEffect:
                    break;
                case EDSDK.PropID_Tv:
                    break;
                case EDSDK.PropID_Unknown:
                    break;
                case EDSDK.PropID_WBCoeffs:
                    break;
                case EDSDK.PropID_WhiteBalance:
                    break;
                case EDSDK.PropID_WhiteBalanceBracket:
                    break;
                case EDSDK.PropID_WhiteBalanceShift:
                    break;
            }
            return EDSDK.EDS_ERR_OK;
        }

        /// <summary>
        /// The camera state changed
        /// </summary>
        /// <param name="inEvent">The StateEvent ID</param>
        /// <param name="inParameter">Parameter from this event</param>
        /// <param name="inContext">...</param>
        /// <returns>An EDSDK errorcode</returns>
        private uint Camera_SDKStateEvent(uint inEvent, uint inParameter, IntPtr inContext)
        {
            //Handle state event here
            switch (inEvent)
            {
                case EDSDK.StateEvent_All:
                    break;
                case EDSDK.StateEvent_AfResult:
                    break;
                case EDSDK.StateEvent_BulbExposureTime:
                    break;
                case EDSDK.StateEvent_CaptureError:
                    break;
                case EDSDK.StateEvent_InternalError:
                    break;
                case EDSDK.StateEvent_JobStatusChanged:
                    break;
                case EDSDK.StateEvent_Shutdown:
                    LeftCameraSessionOpen = false;
                    if (Left_LVThread.IsAlive) Left_LVThread.Abort();
                    if (Right_LVThread.IsAlive) Right_LVThread.Abort();
                    if (CameraHasShutdown != null) CameraHasShutdown(this, new EventArgs());
                    break;
                case EDSDK.StateEvent_ShutDownTimerUpdate:
                    break;
                case EDSDK.StateEvent_WillSoonShutDown:
                    break;
            }
            return EDSDK.EDS_ERR_OK;
        }

        #endregion

        #region Camera commands

        #region Download data

        /// <summary>
        /// Downloads an image to given directory
        /// </summary>
        /// <param name="ObjectPointer">Pointer to the object. Get it from the SDKObjectEvent.</param>
        /// <param name="directory">Path to where the image will be saved to</param>
        public void DownloadImage(IntPtr ObjectPointer, string directory)
        {
            EDSDK.EdsDirectoryItemInfo dirInfo;
            IntPtr streamRef;
            //get information about object
            LeftError = EDSDK.EdsGetDirectoryItemInfo(ObjectPointer, out dirInfo);
            string CurrentPhoto = Path.Combine(directory, dirInfo.szFileName);

            SendSDKCommand(delegate
            {
                //create filestream to data
                LeftError = EDSDK.EdsCreateFileStream(CurrentPhoto, EDSDK.EdsFileCreateDisposition.CreateAlways, EDSDK.EdsAccess.ReadWrite, out streamRef);
                //download file
                lock (STAThread.ExecLock) { DownloadData(ObjectPointer, streamRef); }
                //release stream
                LeftError = EDSDK.EdsRelease(streamRef);
            }, true);
           
        }

        /// <summary>
        /// Downloads a jpg image from the camera into a Bitmap. Fires the ImageDownloaded event when done.
        /// </summary>
        /// <param name="ObjectPointer">Pointer to the object. Get it from the SDKObjectEvent.</param>
        public void DownloadImage(IntPtr ObjectPointer)
        {
            //get information about image
            EDSDK.EdsDirectoryItemInfo dirInfo = new EDSDK.EdsDirectoryItemInfo();
            LeftError = 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
                    LeftError = EDSDK.EdsCreateMemoryStream(dirInfo.Size, out streamRef);

                     //download data to the stream
                     lock (STAThread.ExecLock) { DownloadData(ObjectPointer, streamRef); }
                     LeftError = EDSDK.EdsGetPointer(streamRef, out jpgPointer);
                     LeftError = 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
                     LeftError = 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 { LeftError = EDSDK.EdsDownloadCancel(ObjectPointer); });
                LeftError = EDSDK.EdsRelease(ObjectPointer);
            }
        }

        /// <summary>
        /// Gets the thumbnail of an image (can be raw or jpg)
        /// </summary>
        /// <param name="filepath">The filename of the image</param>
        /// <returns>The thumbnail of the image</returns>
        public Bitmap GetFileThumb(string filepath)
        {
            IntPtr stream;
            //create a filestream to given file
            LeftError = EDSDK.EdsCreateFileStream(filepath, EDSDK.EdsFileCreateDisposition.OpenExisting, EDSDK.EdsAccess.Read, out stream);
            return GetImage(stream, EDSDK.EdsImageSource.Thumbnail);
        }

        /// <summary>
        /// Downloads data from the camera
        /// </summary>
        /// <param name="ObjectPointer">Pointer to the object</param>
        /// <param name="stream">Pointer to the stream created in advance</param>
        private void DownloadData(IntPtr ObjectPointer, IntPtr stream)
        {
            //get information about the object
            EDSDK.EdsDirectoryItemInfo dirInfo;
            LeftError = EDSDK.EdsGetDirectoryItemInfo(ObjectPointer, out dirInfo);

            try
            {
                //set progress event
                LeftError = EDSDK.EdsSetProgressCallback(stream, SDKProgressCallbackEvent, EDSDK.EdsProgressOption.Periodically, ObjectPointer);
                //download the data
                LeftError = EDSDK.EdsDownload(ObjectPointer, dirInfo.Size, stream);
            }
            finally
            {
                //set the download as complete
                LeftError = EDSDK.EdsDownloadComplete(ObjectPointer);
                //release object
                LeftError = EDSDK.EdsRelease(ObjectPointer);
            }
        }

        /// <summary>
        /// Creates a Bitmap out of a stream
        /// </summary>
        /// <param name="img_stream">Image stream</param>
        /// <param name="imageSource">Type of image</param>
        /// <returns>The bitmap from the stream</returns>
        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
                LeftError = EDSDK.EdsCreateImageRef(img_stream, out img_ref);
                LeftError = 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
                LeftError = EDSDK.EdsCreateMemoryStreamFromPointer(buffer, (uint)datalength, out stream);
                //load image into the buffer
                LeftError = EDSDK.EdsGetImage(img_ref, imageSource, EDSDK.EdsTargetImageType.RGB, imageInfo.EffectiveRect, outputSize, stream);

                //create output bitmap
                Bitmap bmp = new Bitmap(outputSize.width, outputSize.height, PixelFormat.Format24bppRgb);

                //assign values to bitmap and make BGR from RGB (System.Drawing (i.e. GDI+) uses BGR)
                unsafe
                {
                    BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);

                    byte* outPix = (byte*)data.Scan0;
                    fixed (byte* inPix = buffer)
                    {
                        for (int i = 0; i < datalength; i += 3)
                        {
                            outPix[i] = inPix[i + 2];//Set B value with R value
                            outPix[i + 1] = inPix[i + 1];//Set G value
                            outPix[i + 2] = inPix[i];//Set R value with B value
                        }
                    }
                    bmp.UnlockBits(data);
                }

                return bmp;
            }
            finally
            {
                //Release all data
                if (img_stream != IntPtr.Zero) EDSDK.EdsRelease(img_stream);
                if (img_ref != IntPtr.Zero) EDSDK.EdsRelease(img_ref);
                if (stream != IntPtr.Zero) EDSDK.EdsRelease(stream);
            }
        }

        #endregion

        #region Get Left Settings

        /// <summary>
        /// Gets the list of possible values for the current camera to set.
        /// Only the PropertyIDs "AEModeSelect", "ISO", "Av", "Tv", "MeteringMode" and "ExposureCompensation" are allowed.
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <returns>A list of available values for the given property ID</returns>
        public List<int> GetLeftSettingsList(uint PropID)
        {
            if (LeftCamera.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();
                    LeftError = EDSDK.EdsGetPropertyDesc(LeftCamera.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"); }
        }

        /// <summary>
        /// Gets the current setting of given property ID as an uint
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <returns>The current setting of the camera</returns>
        public uint GetLeftSetting(uint PropID)
        {
            if (LeftCamera.Ref != IntPtr.Zero)
            {
                uint property = 0;
                LeftError = EDSDK.EdsGetPropertyData(LeftCamera.Ref, PropID, 0, out property);
                return property;
            }
            else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
        }

        /// <summary>
        /// Gets the current setting of given property ID as a string
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <returns>The current setting of the camera</returns>
        public string GetLeftStringSetting(uint PropID)
        {
            if (LeftCamera.Ref != IntPtr.Zero)
            {
                string data = String.Empty;
                EDSDK.EdsGetPropertyData(LeftCamera.Ref, PropID, 0, out data);
                return data;
            }
            else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
        }

        /// <summary>
        /// Gets the current setting of given property ID as a struct
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <typeparam name="T">One of the EDSDK structs</typeparam>
        /// <returns>The current setting of the camera</returns>
        public T GetLeftStructSetting<T>(uint PropID) where T : struct
        {
            if (LeftCamera.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
                LeftError = EDSDK.EdsGetPropertyData(LeftCamera.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"); }
        }

        #endregion

        #region Get Right Settings

        /// <summary>
        /// Gets the list of possible values for the current camera to set.
        /// Only the PropertyIDs "AEModeSelect", "ISO", "Av", "Tv", "MeteringMode" and "ExposureCompensation" are allowed.
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <returns>A list of available values for the given property ID</returns>
        public List<int> GetRightSettingsList(uint PropID)
        {
            if (RightCamera.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();
                    RightError = EDSDK.EdsGetPropertyDesc(RightCamera.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"); }
        }

        /// <summary>
        /// Gets the current setting of given property ID as an uint
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <returns>The current setting of the camera</returns>
        public uint GetRightSetting(uint PropID)
        {
            if (RightCamera.Ref != IntPtr.Zero)
            {
                uint property = 0;
                RightError = EDSDK.EdsGetPropertyData(RightCamera.Ref, PropID, 0, out property);
                return property;
            }
            else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
        }

        /// <summary>
        /// Gets the current setting of given property ID as a string
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <returns>The current setting of the camera</returns>
        public string GetRightStringSetting(uint PropID)
        {
            if (RightCamera.Ref != IntPtr.Zero)
            {
                string data = String.Empty;
                EDSDK.EdsGetPropertyData(RightCamera.Ref, PropID, 0, out data);
                return data;
            }
            else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
        }

        /// <summary>
        /// Gets the current setting of given property ID as a struct
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <typeparam name="T">One of the EDSDK structs</typeparam>
        /// <returns>The current setting of the camera</returns>
        public T GetRightStructSetting<T>(uint PropID) where T : struct
        {
            if (RightCamera.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
                RightError = EDSDK.EdsGetPropertyData(RightCamera.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"); }
        }

        #endregion

        #region Set Left Settings

        /// <summary>
        /// Sets an uint value for the given property ID
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <param name="Value">The value which will be set</param>
        public void SetLeftSetting(uint PropID, uint Value)
        {
            if (LeftCamera.Ref != IntPtr.Zero)
            {
                SendSDKCommand(delegate
                {
                    int propsize;
                    EDSDK.EdsDataType proptype;
                    //get size of property
                    LeftError = EDSDK.EdsGetPropertySize(LeftCamera.Ref, PropID, 0, out proptype, out propsize);
                    //set given property
                    LeftError = EDSDK.EdsSetPropertyData(LeftCamera.Ref, PropID, 0, propsize, Value);
                });
            }
            else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
        }

        /// <summary>
        /// Sets a string value for the given property ID
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <param name="Value">The value which will be set</param>
        public void SetLeftStringSetting(uint PropID, string Value)
        {
            if (LeftCamera.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 { LeftError = EDSDK.EdsSetPropertyData(LeftCamera.Ref, PropID, 0, 32, propertyValueBytes); });
            }
            else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
        }

        /// <summary>
        /// Sets a struct value for the given property ID
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <param name="Value">The value which will be set</param>
        public void SetLeftStructSetting<T>(uint PropID, T Value) where T : struct
        {
            if (LeftCamera.Ref != IntPtr.Zero)
            {
                SendSDKCommand(delegate { LeftError = EDSDK.EdsSetPropertyData(LeftCamera.Ref, PropID, 0, Marshal.SizeOf(typeof(T)), Value); });
            }
            else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
        }

        #endregion

        #region Set Right Settings

        /// <summary>
        /// Sets an uint value for the given property ID
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <param name="Value">The value which will be set</param>
        public void SetRightSetting(uint PropID, uint Value)
        {
            if (RightCamera.Ref != IntPtr.Zero)
            {
                SendSDKCommand(delegate
                {
                    int propsize;
                    EDSDK.EdsDataType proptype;
                    //get size of property
                    RightError = EDSDK.EdsGetPropertySize(RightCamera.Ref, PropID, 0, out proptype, out propsize);
                    //set given property
                    RightError = EDSDK.EdsSetPropertyData(RightCamera.Ref, PropID, 0, propsize, Value);
                });
            }
            else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
        }

        /// <summary>
        /// Sets a string value for the given property ID
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <param name="Value">The value which will be set</param>
        public void SetRightStringSetting(uint PropID, string Value)
        {
            if (RightCamera.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 { RightError = EDSDK.EdsSetPropertyData(RightCamera.Ref, PropID, 0, 32, propertyValueBytes); });
            }
            else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
        }

        /// <summary>
        /// Sets a struct value for the given property ID
        /// </summary>
        /// <param name="PropID">The property ID</param>
        /// <param name="Value">The value which will be set</param>
        public void SetRightStructSetting<T>(uint PropID, T Value) where T : struct
        {
            if (RightCamera.Ref != IntPtr.Zero)
            {
                SendSDKCommand(delegate { RightError = EDSDK.EdsSetPropertyData(RightCamera.Ref, PropID, 0, Marshal.SizeOf(typeof(T)), Value); });
            }
            else { throw new ArgumentNullException("Camera or camera reference is null/zero"); }
        }

        #endregion

        #region Live view

        /// <summary>
        /// Starts the live view
        /// </summary>
        public void StartLeftLiveView()
        {
            if (!IsLeftLiveViewOn)
            {
                SetLeftSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_PC);
                IsLeftLiveViewOn = true;
            }
           
        }
        public void StartRightLiveView()
        {
           
            if (!IsRightLiveViewOn)
            {
                SetRightSetting(EDSDK.PropID_Evf_OutputDevice, EDSDK.EvfOutputDevice_PC);
                IsRightLiveViewOn = true;
            }
        }

        /// <summary>
        /// Stops the live view
        /// </summary>
        public void StopLeftLiveView(bool LVoff = true)
        {
            this.Left_LVoff = LVoff;
            IsLeftLiveViewOn = false;
          
        }
        public void StopRightLiveView(bool LVoff = true)
        {
            this.Right_LVoff = LVoff;
            IsRightLiveViewOn = false;

        }

        /// <summary>
        /// Downloads the live view image
        /// </summary>
        private void DownloadEvf()
        {
            Left_LVThread = STAThread.Create(delegate
            {
                try
                {
                    IntPtr jpgPointer;
                    IntPtr stream = IntPtr.Zero;
                    IntPtr EvfImageRef = IntPtr.Zero;
                    UnmanagedMemoryStream ums;

                    uint err;
                    uint length;
                    //create stream
                    LeftError = EDSDK.EdsCreateMemoryStream(0, out stream);

                    //run live view
                    while (IsLeftLiveViewOn)
                    {
                        lock (STAThread.ExecLock)
                        {
                            //download current live view image
                            err = EDSDK.EdsCreateEvfImageRef(stream, out EvfImageRef);
                            if (err == EDSDK.EDS_ERR_OK) err = EDSDK.EdsDownloadEvfImage(LeftCamera.Ref, EvfImageRef);
                            if (err == EDSDK.EDS_ERR_OBJECT_NOTREADY) { Thread.Sleep(4); continue; }
                            else LeftError = err;
                        }

                        //get pointer
                        LeftError = EDSDK.EdsGetPointer(stream, out jpgPointer);
                        LeftError = EDSDK.EdsGetLength(stream, out length);

                        //get some live view image metadata
                        if (!IsLeftCoordSystemSet) { Left_Evf_CoordinateSystem = GetEvfCoord(EvfImageRef); IsLeftCoordSystemSet = true; }
                        Left_Evf_ZoomRect = GetEvfZoomRect(EvfImageRef);
                        Left_Evf_ZoomPosition = GetEvfPoints(EDSDK.PropID_Evf_ZoomPosition, EvfImageRef);
                        Left_Evf_ImagePosition = GetEvfPoints(EDSDK.PropID_Evf_ImagePosition, EvfImageRef);

                        //release current evf image
                        if (EvfImageRef != IntPtr.Zero) { LeftError = 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) { LeftError = EDSDK.EdsRelease(stream); }
                    //stop the live view
                    SetLeftSetting(EDSDK.PropID_Evf_OutputDevice, Left_LVoff ? 0 : EDSDK.EvfOutputDevice_TFT);
                }
                catch { IsLeftLiveViewOn = false; }
            });
            Left_LVThread.Start();
            Right_LVThread = STAThread.Create(delegate
            {
                try
                {
                    IntPtr jpgPointer;
                    IntPtr stream = IntPtr.Zero;
                    IntPtr EvfImageRef = IntPtr.Zero;
                    UnmanagedMemoryStream ums;

                    uint err;
                    uint length;
                    //create stream
                    RightError = EDSDK.EdsCreateMemoryStream(0, out stream);

                    //run live view
                    while (IsRightLiveViewOn)
                    {
                        lock (STAThread.ExecLock)
                        {
                            //download current live view image
                            err = EDSDK.EdsCreateEvfImageRef(stream, out EvfImageRef);
                            if (err == EDSDK.EDS_ERR_OK) err = EDSDK.EdsDownloadEvfImage(RightCamera.Ref, EvfImageRef);
                            if (err == EDSDK.EDS_ERR_OBJECT_NOTREADY) { Thread.Sleep(4); continue; }
                            else RightError = err;
                        }

                        //get pointer
                        RightError = EDSDK.EdsGetPointer(stream, out jpgPointer);
                        RightError = EDSDK.EdsGetLength(stream, out length);

                        //get some live view image metadata
                        if (!IsRightCoordSystemSet) { Right_Evf_CoordinateSystem = GetEvfCoord(EvfImageRef); IsRightCoordSystemSet = true; }
                        Right_Evf_ZoomRect = GetEvfZoomRect(EvfImageRef);
                        Right_Evf_ZoomPosition = GetEvfPoints(EDSDK.PropID_Evf_ZoomPosition, EvfImageRef);
                        Right_Evf_ImagePosition = GetEvfPoints(EDSDK.PropID_Evf_ImagePosition, EvfImageRef);

                        //release current evf image
                        if (EvfImageRef != IntPtr.Zero) { RightError = 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) { RightError = EDSDK.EdsRelease(stream); }
                    //stop the live view
                    SetRightSetting(EDSDK.PropID_Evf_OutputDevice, Right_LVoff ? 0 : EDSDK.EvfOutputDevice_TFT);
                }
                catch { IsRightLiveViewOn = false; }
            });
            Right_LVThread.Start();
        }

        /// <summary>
        /// Get the live view ZoomRect value
        /// </summary>
        /// <param name="imgRef">The live view reference</param>
        /// <returns>ZoomRect value</returns>
        private EDSDK.EdsRect GetEvfZoomRect(IntPtr imgRef)
        {
            int size = Marshal.SizeOf(typeof(EDSDK.EdsRect));
            IntPtr ptr = Marshal.AllocHGlobal(size);
            uint err = EDSDK.EdsGetPropertyData(imgRef, EDSDK.PropID_Evf_ZoomRect, 0, size, ptr);
            EDSDK.EdsRect rect = (EDSDK.EdsRect)Marshal.PtrToStructure(ptr, typeof(EDSDK.EdsRect));
            Marshal.FreeHGlobal(ptr);
            if (err == EDSDK.EDS_ERR_OK) return rect;
            else return new EDSDK.EdsRect();
        }

        /// <summary>
        /// Get the live view coordinate system
        /// </summary>
        /// <param name="imgRef">The live view reference</param>
        /// <returns>the live view coordinate system</returns>
        private EDSDK.EdsSize GetEvfCoord(IntPtr imgRef)
        {
            int size = Marshal.SizeOf(typeof(EDSDK.EdsSize));
            IntPtr ptr = Marshal.AllocHGlobal(size);
            uint err = EDSDK.EdsGetPropertyData(imgRef, EDSDK.PropID_Evf_CoordinateSystem, 0, size, ptr);
            EDSDK.EdsSize coord = (EDSDK.EdsSize)Marshal.PtrToStructure(ptr, typeof(EDSDK.EdsSize));
            Marshal.FreeHGlobal(ptr);
            if (err == EDSDK.EDS_ERR_OK) return coord;
            else return new EDSDK.EdsSize();
        }

        /// <summary>
        /// Get a live view EdsPoint value
        /// </summary>
        /// <param name="imgRef">The live view reference</param>
        /// <returns>a live view EdsPoint value</returns>
        private EDSDK.EdsPoint GetEvfPoints(uint PropID, IntPtr imgRef)
        {
            int size = Marshal.SizeOf(typeof(EDSDK.EdsPoint));
            IntPtr ptr = Marshal.AllocHGlobal(size);
            uint err = EDSDK.EdsGetPropertyData(imgRef, PropID, 0, size, ptr);
            EDSDK.EdsPoint data = (EDSDK.EdsPoint)Marshal.PtrToStructure(ptr, typeof(EDSDK.EdsPoint));
            Marshal.FreeHGlobal(ptr);
            if (err == EDSDK.EDS_ERR_OK) return data;
            else return new EDSDK.EdsPoint();
        }

        #endregion

        #region Filming

        /// <summary>
        /// Starts recording a video and downloads it when finished
        /// </summary>
        /// <param name="FilePath">Directory to where the final video will be saved to</param>
        public void StartFilming(string FilePath)
        {
            if (!IsLeftFilming)
            {
                StartFilming();
                this.Left_DownloadVideo = true;
                LeftImageSaveDirectory = FilePath;
            }
            if (!IsRightFilming)
            {
                StartFilming();
                this.Right_DownloadVideo = true;
                RightImageSaveDirectory = FilePath;
            }
        }

        /// <summary>
        /// Starts recording a video
        /// </summary>
        public void StartFilming()
        {
            if (!IsLeftFilming)
            {
                //Check if the camera is ready to film
                if (GetLeftSetting(EDSDK.PropID_Record) != 3) throw new InvalidOperationException("Camera is not in film mode");

                IsLeftFilming = true;

                //to restore the current setting after recording
                Left_PrevSaveTo = GetLeftSetting(EDSDK.PropID_SaveTo);
                //when recording videos, it has to be saved on the camera internal memory
                SetLeftSetting(EDSDK.PropID_SaveTo, (uint)EDSDK.EdsSaveTo.Camera);
                this.Left_DownloadVideo = false;
                //start the video recording
                SendSDKCommand(delegate { LeftError = EDSDK.EdsSetPropertyData(LeftCamera.Ref, EDSDK.PropID_Record, 0, 4, 4); });
            }
            if (!IsRightFilming)
            {
                //Check if the camera is ready to film
                if (GetRightSetting(EDSDK.PropID_Record) != 3) throw new InvalidOperationException("Camera is not in film mode");

                IsRightFilming = true;

                //to restore the current setting after recording
                Right_PrevSaveTo = GetRightSetting(EDSDK.PropID_SaveTo);
                //when recording videos, it has to be saved on the camera internal memory
                SetRightSetting(EDSDK.PropID_SaveTo, (uint)EDSDK.EdsSaveTo.Camera);
                this.Right_DownloadVideo = false;
                //start the video recording
                SendSDKCommand(delegate { RightError = EDSDK.EdsSetPropertyData(RightCamera.Ref, EDSDK.PropID_Record, 0, 4, 4); });
            }
        }

        /// <summary>
        /// Stops recording a video
        /// </summary>
        public void StopFilming()
        {
            if (IsLeftFilming)
            {
                SendSDKCommand(delegate
                {
                    //Shut off live view (it will hang otherwise)
                    StopLeftLiveView(false);
                    //stop video recording
                    LeftError = EDSDK.EdsSetPropertyData(LeftCamera.Ref, EDSDK.PropID_Record, 0, 4, 0);
                });
                //set back to previous state
                SetLeftSetting(EDSDK.PropID_SaveTo, Left_PrevSaveTo);
                IsLeftFilming = false;
            }
            if (IsRightFilming)
            {
                SendSDKCommand(delegate
                {
                    //Shut off live view (it will hang otherwise)
                    StopRightLiveView(false);
                    //stop video recording
                    RightError = EDSDK.EdsSetPropertyData(RightCamera.Ref, EDSDK.PropID_Record, 0, 4, 0);
                });
                //set back to previous state
                SetRightSetting(EDSDK.PropID_SaveTo, Right_PrevSaveTo);
                IsRightFilming = false;
            }
        }

        #endregion

        #region Taking photos

        /// <summary>
        /// Press the shutter button
        /// </summary>
        /// <param name="state">State of the shutter button</param>
        public void PressLeftShutterButton(EDSDK.EdsShutterButton state)
        {
            //start thread to not block everything
            SendSDKCommand(delegate
            {
                //send command to camera
                lock (STAThread.ExecLock) { LeftError = EDSDK.EdsSendCommand(LeftCamera.Ref, EDSDK.CameraCommand_PressShutterButton, (uint)state); };
            }, true);
        }
        public void PressRightShutterButton(EDSDK.EdsShutterButton state)
        {
            //start thread to not block everything
            SendSDKCommand(delegate
            {
                //send command to camera
                lock (STAThread.ExecLock) { RightError = EDSDK.EdsSendCommand(RightCamera.Ref, EDSDK.CameraCommand_PressShutterButton, (uint)state); };
            }, true);
        }

        /// <summary>
        /// Takes a photo with the current camera settings
        /// </summary>
        public void TakeLeftPhoto()
        {
            //start thread to not block everything
            SendSDKCommand(delegate
            {
                //send command to camera
                lock (STAThread.ExecLock) { LeftError = EDSDK.EdsSendCommand(LeftCamera.Ref, EDSDK.CameraCommand_TakePicture, 0); };
            }, true);
        }
        public void TakeRightPhoto()
        {
            //start thread to not block everything
            SendSDKCommand(delegate
            {
                //send command to camera
                lock (STAThread.ExecLock) { RightError = EDSDK.EdsSendCommand(RightCamera.Ref, EDSDK.CameraCommand_TakePicture, 0); };
            }, true);
        }
        /// <summary>
        /// Takes a photo in bulb mode with the current camera settings
        /// </summary>
        /// <param name="BulbTime">The time in milliseconds for how long the shutter will be open</param>
        public void TakeLeftPhoto(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) { LeftError = EDSDK.EdsSendCommand(LeftCamera.Ref, EDSDK.CameraCommand_BulbStart, 0); }
                //wait for the specified time
                Thread.Sleep((int)BulbTime);
                //close shutter
                lock (STAThread.ExecLock) { LeftError = EDSDK.EdsSendCommand(LeftCamera.Ref, EDSDK.CameraCommand_BulbEnd, 0); }
            }, true);
        }
        public void TakeRightPhoto(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) { RightError = EDSDK.EdsSendCommand(RightCamera.Ref, EDSDK.CameraCommand_BulbStart, 0); }
                //wait for the specified time
                Thread.Sleep((int)BulbTime);
                //close shutter
                lock (STAThread.ExecLock) { RightError = EDSDK.EdsSendCommand(RightCamera.Ref, EDSDK.CameraCommand_BulbEnd, 0); }
            }, true);
        }

        #endregion

        #region Other

        /// <summary>
        /// Sends a command to the camera safely
        /// </summary>
        private void SendSDKCommand(Action command, bool longTask = false)
        {
            if (longTask) STAThread.Create(command).Start();
            else STAThread.ExecuteSafely(command);
        }

        /// <summary>
        /// Tells the camera that there is enough space on the HDD if SaveTo is set to Host
        /// This method does not use the actual free space!
        /// </summary>
        public void SetLeftCapacity()
        {            
            //create new capacity struct
            EDSDK.EdsCapacity capacity = new EDSDK.EdsCapacity();

            //set big enough values
            capacity.Reset = 1;
            capacity.BytesPerSector = 0x1000;
            capacity.NumberOfFreeClusters = 0x7FFFFFFF;

            //set the values to camera
            SendSDKCommand(delegate { LeftError = EDSDK.EdsSetCapacity(LeftCamera.Ref, capacity); });
        }
        public void SetRightCapacity()
        {
            //create new capacity struct
            EDSDK.EdsCapacity capacity = new EDSDK.EdsCapacity();

            //set big enough values
            capacity.Reset = 1;
            capacity.BytesPerSector = 0x1000;
            capacity.NumberOfFreeClusters = 0x7FFFFFFF;

            //set the values to camera
            SendSDKCommand(delegate { RightError = EDSDK.EdsSetCapacity(RightCamera.Ref, capacity); });
        }

        /// <summary>
        /// Tells the camera how much space is available on the host PC
        /// </summary>
        /// <param name="BytesPerSector">Bytes per sector on HD</param>
        /// <param name="NumberOfFreeClusters">Number of free clusters on HD</param>
        public void SetLeftCapacity(int BytesPerSector, int NumberOfFreeClusters)
        {
            //create new capacity struct
            EDSDK.EdsCapacity capacity = new EDSDK.EdsCapacity();

            //set given values
            capacity.Reset = 1;
            capacity.BytesPerSector = BytesPerSector;
            capacity.NumberOfFreeClusters = NumberOfFreeClusters;

            //set the values to camera
            SendSDKCommand(delegate { LeftError = EDSDK.EdsSetCapacity(LeftCamera.Ref, capacity); });
        }
        public void SetRightCapacity(int BytesPerSector, int NumberOfFreeClusters)
        {
            //create new capacity struct
            EDSDK.EdsCapacity capacity = new EDSDK.EdsCapacity();

            //set given values
            capacity.Reset = 1;
            capacity.BytesPerSector = BytesPerSector;
            capacity.NumberOfFreeClusters = NumberOfFreeClusters;

            //set the values to camera
            SendSDKCommand(delegate { RightError = EDSDK.EdsSetCapacity(RightCamera.Ref, capacity); });
        }

        /// <summary>
        /// Moves the focus (only works while in live view)
        /// </summary>
        /// <param name="Speed">Speed and direction of focus movement</param>
        public void SetLeftFocus(uint Speed)
        {
            if (IsLeftLiveViewOn) SendSDKCommand(delegate { LeftError = EDSDK.EdsSendCommand(LeftCamera.Ref, EDSDK.CameraCommand_DriveLensEvf, Speed); });
        }
        public void SetRightFocus(uint Speed)
        {
            if (IsRightLiveViewOn) SendSDKCommand(delegate { RightError = EDSDK.EdsSendCommand(RightCamera.Ref, EDSDK.CameraCommand_DriveLensEvf, Speed); });
        }

        /// <summary>
        /// Sets the WB of the live view while in live view
        /// </summary>
        /// <param name="x">X Coordinate</param>
        /// <param name="y">Y Coordinate</param>
        public void SetLeftManualWBEvf(ushort x, ushort y)
        {
            if (IsLeftLiveViewOn)
            {
                //converts the coordinates to a form the camera accepts
                byte[] xa = BitConverter.GetBytes(x);
                byte[] ya = BitConverter.GetBytes(y);
                uint coord = BitConverter.ToUInt32(new byte[] { xa[0], xa[1], ya[0], ya[1] }, 0);
                //send command to camera
                SendSDKCommand(delegate { LeftError = EDSDK.EdsSendCommand(LeftCamera.Ref, EDSDK.CameraCommand_DoClickWBEvf, coord); });
            }
        }
        public void SetRightManualWBEvf(ushort x, ushort y)
        {
            if (IsLeftLiveViewOn)
            {
                //converts the coordinates to a form the camera accepts
                byte[] xa = BitConverter.GetBytes(x);
                byte[] ya = BitConverter.GetBytes(y);
                uint coord = BitConverter.ToUInt32(new byte[] { xa[0], xa[1], ya[0], ya[1] }, 0);
                //send command to camera
                SendSDKCommand(delegate { LeftError = EDSDK.EdsSendCommand(LeftCamera.Ref, EDSDK.CameraCommand_DoClickWBEvf, coord); });
            }
        }
        /// <summary>
        /// Gets all volumes, folders and files existing on the camera
        /// </summary>
        /// <returns>A CameraFileEntry with all informations</returns>
        public CameraFileEntry GetLeftAllEntries()
        {
            //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;
            LeftError = EDSDK.EdsGetChildCount(LeftCamera.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;
                LeftError = EDSDK.EdsGetChildAtIndex(LeftCamera.Ref, i, out ChildPtr);
                EDSDK.EdsVolumeInfo vinfo = new EDSDK.EdsVolumeInfo();
                SendSDKCommand(delegate { LeftError = 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
                LeftError = EDSDK.EdsRelease(ChildPtr);
            }
            //add all volumes to the main entry and return it
            MainEntry.AddSubEntries(VolumeEntries.ToArray());
            return MainEntry;
        }
        public CameraFileEntry GetRightAllEntries()
        {
            //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;
            RightError = EDSDK.EdsGetChildCount(RightCamera.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;
                RightError = EDSDK.EdsGetChildAtIndex(RightCamera.Ref, i, out ChildPtr);
                EDSDK.EdsVolumeInfo vinfo = new EDSDK.EdsVolumeInfo();
                SendSDKCommand(delegate { RightError = 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
                RightError = EDSDK.EdsRelease(ChildPtr);
            }
            //add all volumes to the main entry and return it
            MainEntry.AddSubEntries(VolumeEntries.ToArray());
            return MainEntry;
        }

        /// <summary>
        /// Locks or unlocks the cameras UI
        /// </summary>
        /// <param name="LockState">True for locked, false to unlock</param>
        public void Left_UILock(bool LockState)
        {
            SendSDKCommand(delegate
            {
                if (LockState == true) LeftError = EDSDK.EdsSendStatusCommand(LeftCamera.Ref, EDSDK.CameraState_UILock, 0);
                else LeftError = EDSDK.EdsSendStatusCommand(LeftCamera.Ref, EDSDK.CameraState_UIUnLock, 0);
            });
        }
        public void Right_UILock(bool LockState)
        {
            SendSDKCommand(delegate
            {
                if (LockState == true) RightError = EDSDK.EdsSendStatusCommand(RightCamera.Ref, EDSDK.CameraState_UILock, 0);
                else RightError = EDSDK.EdsSendStatusCommand(RightCamera.Ref, EDSDK.CameraState_UIUnLock, 0);
            });
        }

        /// <summary>
        /// Gets the children of a camera folder/volume. Recursive method.
        /// </summary>
        /// <param name="ptr">Pointer to volume or folder</param>
        /// <returns></returns>
        private CameraFileEntry[] GetChildren(IntPtr ptr)
        {
            int ChildCount;
            //get children of first pointer
            LeftError = 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
                    LeftError = EDSDK.EdsGetChildAtIndex(ptr, i, out ChildPtr);
                    //get the information about this children
                    EDSDK.EdsDirectoryItemInfo ChildInfo = new EDSDK.EdsDirectoryItemInfo();
                    SendSDKCommand(delegate { LeftError = 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;
                        LeftError = EDSDK.EdsCreateMemoryStream(0, out stream);
                        SendSDKCommand(delegate { LeftError = 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
                    EDSDK.EdsRelease(ChildPtr);
                }
                return MainEntry;
            }
            else return null;
        }

        /// <summary>
        /// Converts an int to a bool
        /// </summary>
        /// <param name="val">Value</param>
        /// <returns>A bool created from the value</returns>
        private bool GetBool(int val)
        {
            if (val == 0) return false;
            else return true;
        }

        #endregion

        #endregion
    }

    /// <summary>
    /// A container for camera related information
    /// </summary>
    public class Camera
    {
        /// <summary>
        /// Pointer to SDK camera object
        /// </summary>
        public IntPtr Ref { get; private set; }
        /// <summary>
        /// Information about this camera
        /// </summary>
        public EDSDK.EdsDeviceInfo Info { get; private set; }
        /// <summary>
        /// Handles errors that happen with the SDK
        /// </summary>
        public uint Error
        {
            get { return EDSDK.EDS_ERR_OK; }
            set { if (value != EDSDK.EDS_ERR_OK) throw new Exception("SDK Error: " + value); }
        }

        /// <summary>
        /// Creates a new instance of the Camera class
        /// </summary>
        /// <param name="Reference">Pointer to the SDK camera object</param>
        public Camera(IntPtr Reference)
        {
            if (Reference == IntPtr.Zero) throw new ArgumentNullException("Camera pointer is zero");
            this.Ref = Reference;
            EDSDK.EdsDeviceInfo dinfo;
            Error = EDSDK.EdsGetDeviceInfo(Reference, out dinfo);
            this.Info = dinfo;
        }
    }

    /// <summary>
    /// Helper to convert between ID and string camera values
    /// </summary>
    public static class CameraValues
    {
        private static CultureInfo cInfo = new CultureInfo("en-US");

        /// <summary>
        /// Gets the Av string value from an Av ID
        /// </summary>
        /// <param name="v">The Av ID</param>
        /// <returns>the Av string</returns>
        public static string AV(uint v)
        {
            switch (v)
            {
                case 0x00:
                    return "Auto";
                case 0x08:
                    return "1";
                case 0x40:
                    return "11";
                case 0x0B:
                    return "1.1";
                case 0x43:
                    return "13 (1/3)";
                case 0x0C:
                    return "1.2";
                case 0x44:
                    return "13";
                case 0x0D:
                    return "1.2 (1/3)";
                case 0x45:
                    return "14";
                case 0x10:
                    return "1.4";
                case 0x48:
                    return "16";
                case 0x13:
                    return "1.6";
                case 0x4B:
                    return "18";
                case 0x14:
                    return "1.8";
                case 0x4C:
                    return "19";
                case 0x15:
                    return "1.8 (1/3)";
                case 0x4D:
                    return "20";
                case 0x18:
                    return "2";
                case 0x50:
                    return "22";
                case 0x1B:
                    return "2.2";
                case 0x53:
                    return "25";
                case 0x1C:
                    return "2.5";
                case 0x54:
                    return "27";
                case 0x1D:
                    return "2.5 (1/3)";
                case 0x55:
                    return "29";
                case 0x20:
                    return "2.8";
                case 0x58:
                    return "32";
                case 0x23:
                    return "3.2";
                case 0x5B:
                    return "36";
                case 0x24:
                    return "3.5";
                case 0x5C:
                    return "38";
                case 0x25:
                    return "3.5 (1/3)";
                case 0x5D:
                    return "40";
                case 0x28:
                    return "4";
                case 0x60:
                    return "45";
                case 0x2B:
                    return "4.5";
                case 0x63:
                    return "51";
                case 0x2C:
                    return "4.5 (1/3)";
                case 0x64:
                    return "54";
                case 0x2D:
                    return "5.0";
                case 0x65:
                    return "57";
                case 0x30:
                    return "5.6";
                case 0x68:
                    return "64";
                case 0x33:
                    return "6.3";
                case 0x6B:
                    return "72";
                case 0x34:
                    return "6.7";
                case 0x6C:
                    return "76";
                case 0x35:
                    return "7.1";
                case 0x6D:
                    return "80";
                case 0x38:
                    return " 8";
                case 0x70:
                    return "91";
                case 0x3B:
                    return "9";
                case 0x3C:
                    return "9.5";
                case 0x3D:
                    return "10";

                case 0xffffffff:
                default:
                    return "N/A";
            }
        }

        /// <summary>
        /// Gets the ISO string value from an ISO ID
        /// </summary>
        /// <param name="v">The ISO ID</param>
        /// <returns>the ISO string</returns>
        public static string ISO(uint v)
        {
            switch (v)
            {
                case 0x00000000:
                    return "Auto ISO";
                case 0x00000028:
                    return "ISO 6";
                case 0x00000030:
                    return "ISO 12";
                case 0x00000038:
                    return "ISO 25";
                case 0x00000040:
                    return "ISO 50";
                case 0x00000048:
                    return "ISO 100";
                case 0x0000004b:
                    return "ISO 125";
                case 0x0000004d:
                    return "ISO 160";
                case 0x00000050:
                    return "ISO 200";
                case 0x00000053:
                    return "ISO 250";
                case 0x00000055:
                    return "ISO 320";
                case 0x00000058:
                    return "ISO 400";
                case 0x0000005b:
                    return "ISO 500";
                case 0x0000005d:
                    return "ISO 640";
                case 0x00000060:
                    return "ISO 800";
                case 0x00000063:
                    return "ISO 1000";
                case 0x00000065:
                    return "ISO 1250";
                case 0x00000068:
                    return "ISO 1600";
                case 0x00000070:
                    return "ISO 3200";
                case 0x00000078:
                    return "ISO 6400";
                case 0x00000080:
                    return "ISO 12800";
                case 0x00000088:
                    return "ISO 25600";
                case 0x00000090:
                    return "ISO 51200";
                case 0x00000098:
                    return "ISO 102400";
                case 0xffffffff:
                default:
                    return "N/A";
            }
        }

        /// <summary>
        /// Gets the Tv string value from an Tv ID
        /// </summary>
        /// <param name="v">The Tv ID</param>
        /// <returns>the Tv string</returns>
        public static string TV(uint v)
        {
            switch (v)
            {
                case 0x00:
                    return "Auto";
                case 0x0C:
                    return "Bulb";
                case 0x5D:
                    return "1/25";
                case 0x10:
                    return "30\"";
                case 0x60:
                    return "1/30";
                case 0x13:
                    return "25\"";
                case 0x63:
                    return "1/40";
                case 0x14:
                    return "20\"";
                case 0x64:
                    return "1/45";
                case 0x15:
                    return "20\" (1/3)";
                case 0x65:
                    return "1/50";
                case 0x18:
                    return "15\"";
                case 0x68:
                    return "1/60";
                case 0x1B:
                    return "13\"";
                case 0x6B:
                    return "1/80";
                case 0x1C:
                    return "10\"";
                case 0x6C:
                    return "1/90";
                case 0x1D:
                    return "10\" (1/3)";
                case 0x6D:
                    return "1/100";
                case 0x20:
                    return "8\"";
                case 0x70:
                    return "1/125";
                case 0x23:
                    return "6\" (1/3)";
                case 0x73:
                    return "1/160";
                case 0x24:
                    return "6\"";
                case 0x74:
                    return "1/180";
                case 0x25:
                    return "5\"";
                case 0x75:
                    return "1/200";
                case 0x28:
                    return "4\"";
                case 0x78:
                    return "1/250";
                case 0x2B:
                    return "3\"2";
                case 0x7B:
                    return "1/320";
                case 0x2C:
                    return "3\"";
                case 0x7C:
                    return "1/350";
                case 0x2D:
                    return "2\"5";
                case 0x7D:
                    return "1/400";
                case 0x30:
                    return "2\"";
                case 0x80:
                    return "1/500";
                case 0x33:
                    return "1\"6";
                case 0x83:
                    return "1/640";
                case 0x34:
                    return "1\"5";
                case 0x84:
                    return "1/750";
                case 0x35:
                    return "1\"3";
                case 0x85:
                    return "1/800";
                case 0x38:
                    return "1\"";
                case 0x88:
                    return "1/1000";
                case 0x3B:
                    return "0\"8";
                case 0x8B:
                    return "1/1250";
                case 0x3C:
                    return "0\"7";
                case 0x8C:
                    return "1/1500";
                case 0x3D:
                    return "0\"6";
                case 0x8D:
                    return "1/1600";
                case 0x40:
                    return "0\"5";
                case 0x90:
                    return "1/2000";
                case 0x43:
                    return "0\"4";
                case 0x93:
                    return "1/2500";
                case 0x44:
                    return "0\"3";
                case 0x94:
                    return "1/3000";
                case 0x45:
                    return "0\"3 (1/3)";
                case 0x95:
                    return "1/3200";
                case 0x48:
                    return "1/4";
                case 0x98:
                    return "1/4000";
                case 0x4B:
                    return "1/5";
                case 0x9B:
                    return "1/5000";
                case 0x4C:
                    return "1/6";
                case 0x9C:
                    return "1/6000";
                case 0x4D:
                    return "1/6 (1/3)";
                case 0x9D:
                    return "1/6400";
                case 0x50:
                    return "1/8";
                case 0xA0:
                    return "1/8000";
                case 0x53:
                    return "1/10 (1/3)";
                case 0x54:
                    return "1/10";
                case 0x55:
                    return "1/13";
                case 0x58:
                    return "1/15";
                case 0x5B:
                    return "1/20 (1/3)";
                case 0x5C:
                    return "1/20";

                case 0xffffffff:
                default:
                    return "N/A";
            }
        }


        /// <summary>
        /// Gets the Av ID from an Av string value
        /// </summary>
        /// <param name="v">The Av string</param>
        /// <returns>the Av ID</returns>
        public static uint AV(string v)
        {
            switch (v)
            {
                case "Auto":
                    return 0x00;
                case "1":
                    return 0x08;
                case "11":
                    return 0x40;
                case "1.1":
                    return 0x0B;
                case "13 (1/3)":
                    return 0x43;
                case "1.2":
                    return 0x0C;
                case "13":
                    return 0x44;
                case "1.2 (1/3)":
                    return 0x0D;
                case "14":
                    return 0x45;
                case "1.4":
                    return 0x10;
                case "16":
                    return 0x48;
                case "1.6":
                    return 0x13;
                case "18":
                    return 0x4B;
                case "1.8":
                    return 0x14;
                case "19":
                    return 0x4C;
                case "1.8 (1/3)":
                    return 0x15;
                case "20":
                    return 0x4D;
                case "2":
                    return 0x18;
                case "22":
                    return 0x50;
                case "2.2":
                    return 0x1B;
                case "25":
                    return 0x53;
                case "2.5":
                    return 0x1C;
                case "27":
                    return 0x54;
                case "2.5 (1/3)":
                    return 0x1D;
                case "29":
                    return 0x55;
                case "2.8":
                    return 0x20;
                case "32":
                    return 0x58;
                case "3.2":
                    return 0x23;
                case "36":
                    return 0x5B;
                case "3.5":
                    return 0x24;
                case "38":
                    return 0x5C;
                case "3.5 (1/3)":
                    return 0x25;
                case "40":
                    return 0x5D;
                case "4":
                    return 0x28;
                case "45":
                    return 0x60;
                case "4.5":
                    return 0x2B;
                case "51":
                    return 0x63;
                case "4.5 (1/3)":
                    return 0x2C;
                case "54":
                    return 0x64;
                case "5.0":
                    return 0x2D;
                case "57":
                    return 0x65;
                case "5.6":
                    return 0x30;
                case "64":
                    return 0x68;
                case "6.3":
                    return 0x33;
                case "72":
                    return 0x6B;
                case "6.7":
                    return 0x34;
                case "76":
                    return 0x6C;
                case "7.1":
                    return 0x35;
                case "80":
                    return 0x6D;
                case " 8":
                    return 0x38;
                case "91":
                    return 0x70;
                case "9":
                    return 0x3B;
                case "9.5":
                    return 0x3C;
                case "10":
                    return 0x3D;

                case "N/A":
                default:
                    return 0xffffffff;
            }
        }

        /// <summary>
        /// Gets the ISO ID from an ISO string value
        /// </summary>
        /// <param name="v">The ISO string</param>
        /// <returns>the ISO ID</returns>
        public static uint ISO(string v)
        {
            switch (v)
            {
                case "Auto ISO":
                    return 0x00000000;
                case "ISO 6":
                    return 0x00000028;
                case "ISO 12":
                    return 0x00000030;
                case "ISO 25":
                    return 0x00000038;
                case "ISO 50":
                    return 0x00000040;
                case "ISO 100":
                    return 0x00000048;
                case "ISO 125":
                    return 0x0000004b;
                case "ISO 160":
                    return 0x0000004d;
                case "ISO 200":
                    return 0x00000050;
                case "ISO 250":
                    return 0x00000053;
                case "ISO 320":
                    return 0x00000055;
                case "ISO 400":
                    return 0x00000058;
                case "ISO 500":
                    return 0x0000005b;
                case "ISO 640":
                    return 0x0000005d;
                case "ISO 800":
                    return 0x00000060;
                case "ISO 1000":
                    return 0x00000063;
                case "ISO 1250":
                    return 0x00000065;
                case "ISO 1600":
                    return 0x00000068;
                case "ISO 3200":
                    return 0x00000070;
                case "ISO 6400":
                    return 0x00000078;
                case "ISO 12800":
                    return 0x00000080;
                case "ISO 25600":
                    return 0x00000088;
                case "ISO 51200":
                    return 0x00000090;
                case "ISO 102400":
                    return 0x00000098;

                case "N/A":
                default:
                    return 0xffffffff;
            }
        }

        /// <summary>
        /// Gets the Tv ID from an Tv string value
        /// </summary>
        /// <param name="v">The Tv string</param>
        /// <returns>the Tv ID</returns>
        public static uint TV(string v)
        {
            switch (v)
            {
                case "Auto":
                    return 0x00;
                case "Bulb":
                    return 0x0C;
                case "1/25":
                    return 0x5D;
                case "30\"":
                    return 0x10;
                case "1/30":
                    return 0x60;
                case "25\"":
                    return 0x13;
                case "1/40":
                    return 0x63;
                case "20\"":
                    return 0x14;
                case "1/45":
                    return 0x64;
                case "20\" (1/3)":
                    return 0x15;
                case "1/50":
                    return 0x65;
                case "15\"":
                    return 0x18;
                case "1/60":
                    return 0x68;
                case "13\"":
                    return 0x1B;
                case "1/80":
                    return 0x6B;
                case "10\"":
                    return 0x1C;
                case "1/90":
                    return 0x6C;
                case "10\" (1/3)":
                    return 0x1D;
                case "1/100":
                    return 0x6D;
                case "8\"":
                    return 0x20;
                case "1/125":
                    return 0x70;
                case "6\" (1/3)":
                    return 0x23;
                case "1/160":
                    return 0x73;
                case "6\"":
                    return 0x24;
                case "1/180":
                    return 0x74;
                case "5\"":
                    return 0x25;
                case "1/200":
                    return 0x75;
                case "4\"":
                    return 0x28;
                case "1/250":
                    return 0x78;
                case "3\"2":
                    return 0x2B;
                case "1/320":
                    return 0x7B;
                case "3\"":
                    return 0x2C;
                case "1/350":
                    return 0x7C;
                case "2\"5":
                    return 0x2D;
                case "1/400":
                    return 0x7D;
                case "2\"":
                    return 0x30;
                case "1/500":
                    return 0x80;
                case "1\"6":
                    return 0x33;
                case "1/640":
                    return 0x83;
                case "1\"5":
                    return 0x34;
                case "1/750":
                    return 0x84;
                case "1\"3":
                    return 0x35;
                case "1/800":
                    return 0x85;
                case "1\"":
                    return 0x38;
                case "1/1000":
                    return 0x88;
                case "0\"8":
                    return 0x3B;
                case "1/1250":
                    return 0x8B;
                case "0\"7":
                    return 0x3C;
                case "1/1500":
                    return 0x8C;
                case "0\"6":
                    return 0x3D;
                case "1/1600":
                    return 0x8D;
                case "0\"5":
                    return 0x40;
                case "1/2000":
                    return 0x90;
                case "0\"4":
                    return 0x43;
                case "1/2500":
                    return 0x93;
                case "0\"3":
                    return 0x44;
                case "1/3000":
                    return 0x94;
                case "0\"3 (1/3)":
                    return 0x45;
                case "1/3200":
                    return 0x95;
                case "1/4":
                    return 0x48;
                case "1/4000":
                    return 0x98;
                case "1/5":
                    return 0x4B;
                case "1/5000":
                    return 0x9B;
                case "1/6":
                    return 0x4C;
                case "1/6000":
                    return 0x9C;
                case "1/6 (1/3)":
                    return 0x4D;
                case "1/6400":
                    return 0x9D;
                case "1/8":
                    return 0x50;
                case "1/8000":
                    return 0xA0;
                case "1/10 (1/3)":
                    return 0x53;
                case "1/10":
                    return 0x54;
                case "1/13":
                    return 0x55;
                case "1/15":
                    return 0x58;
                case "1/20 (1/3)":
                    return 0x5B;
                case "1/20":
                    return 0x5C;

                case "N/A":
                default:
                    return 0xffffffff;
            }
        }
    }
    
    /// <summary>
    /// A storage for a camera filesystem entry
    /// </summary>
    public class CameraFileEntry
    {
        /// <summary>
        /// Name of this entry
        /// </summary>
        public string Name { get; private set; }
        /// <summary>
        /// States if this entry is a folder or not
        /// </summary>
        public bool IsFolder { get; private set; }
        /// <summary>
        /// Thumbnail of this entry (might be null if not available)
        /// </summary>
        public Bitmap Thumbnail { get; private set; }
        /// <summary>
        /// Subentries of this entry (i.e. subfolders)
        /// </summary>
        public CameraFileEntry[] Entries { get; private set; }

        /// <summary>
        /// Creates a new instance of the CameraFileEntry class
        /// </summary>
        /// <param name="Name">Name of this entry</param>
        /// <param name="IsFolder">True if this entry is a folder, false otherwise</param>
        public CameraFileEntry(string Name, bool IsFolder)
        {
            this.Name = Name;
            this.IsFolder = IsFolder;
        }

        /// <summary>
        /// Adds subentries (subfolders) to this entry
        /// </summary>
        /// <param name="Entries">the entries to add</param>
        public void AddSubEntries(CameraFileEntry[] Entries)
        {
            this.Entries = Entries;
        }

        /// <summary>
        /// Adds a thumbnail to this entry
        /// </summary>
        /// <param name="Thumbnail">The thumbnail to add</param>
        public void AddThumb(Bitmap Thumbnail)
        {
            this.Thumbnail = Thumbnail;
        }
    }
    
    /// <summary>
    /// Helper class to create or run code on STA threads
    /// </summary>
    public static class STAThread
    {
        /// <summary>
        /// The object that is used to lock the live view thread
        /// </summary>
        public static readonly object ExecLock = new object();
        /// <summary>
        /// States if the calling thread is an STA thread or not
        /// </summary>
        public static bool IsSTAThread
        {
            get { return Thread.CurrentThread.GetApartmentState() == ApartmentState.STA; }
        }

        /// <summary>
        /// The main thread where everything will be executed on
        /// </summary>
        private static Thread main;
        /// <summary>
        /// The action to be executed
        /// </summary>
        private static Action runAction;
        /// <summary>
        /// Storage for an exception that might have happened on the execution thread
        /// </summary>
        private static Exception runException;
        /// <summary>
        /// States if the execution thread is currently running
        /// </summary>
        private static bool isRunning = false;
        /// <summary>
        /// Lock object to make sure only one command at a time is executed
        /// </summary>
        private static object runLock = new object();
        /// <summary>
        /// Lock object to synchronize between execution and calling thread
        /// </summary>
        private static object threadLock = new object();

        /// <summary>
        /// Starts the execution thread
        /// </summary>
        internal static void Init()
        {
            if (!isRunning)
            {
                main = Create(SafeExecutionLoop);
                isRunning = true;
                main.Start();
            }
        }

        /// <summary>
        /// Shuts down the execution thread
        /// </summary>
        internal static void Shutdown()
        {
            if (isRunning)
            {
                isRunning = false;
                lock (threadLock) { Monitor.Pulse(threadLock); }
                main.Join();
            }
        }

        /// <summary>
        /// Creates an STA thread that can safely execute SDK commands
        /// </summary>
        /// <param name="a">The command to run on this thread</param>
        /// <returns>An STA thread</returns>
        public static Thread Create(Action a)
        {
            var thread = new Thread(new ThreadStart(a));
            thread.SetApartmentState(ApartmentState.STA);
            return thread;
        }


        /// <summary>
        /// Safely executes an SDK command
        /// </summary>
        /// <param name="a">The SDK command</param>
        public static void ExecuteSafely(Action a)
        {
            lock (runLock)
            {
                if (!isRunning) return;

                if (IsSTAThread)
                {
                    runAction = a;
                    lock (threadLock)
                    {
                        Monitor.Pulse(threadLock);
                        Monitor.Wait(threadLock);
                    }
                    if (runException != null)
                    {
                        throw runException;
                    }

                }
                else lock (ExecLock) { a(); }
            }
        }

        /// <summary>
        /// Safely executes an SDK command with return value
        /// </summary>
        /// <param name="func">The SDK command</param>
        /// <returns>the return value of the function</returns>
        public static T ExecuteSafely<T>(Func<T> func)
        {
            T result = default(T);
            ExecuteSafely(delegate { result = func(); });
            return result;
        }

        private static void SafeExecutionLoop()
        {
            lock (threadLock)
            {
                while (true)
                {
                    Monitor.Wait(threadLock);
                    if (!isRunning) return;
                    runException = null;
                    try { lock (ExecLock) { runAction(); } }
                    catch (Exception ex) { runException = ex; }
                    Monitor.Pulse(threadLock);
                }
            }
        }
    }
}



modified 15-Sep-15 21:59pm.

GeneralRe: Multiple camera invoke side by side in single Instance. Pin
Amar Chaudhary15-Sep-15 16:03
Amar Chaudhary15-Sep-15 16:03 
GeneralRe: Multiple camera invoke side by side in single Instance. Pin
Member 1083409815-Nov-15 23:13
Member 1083409815-Nov-15 23:13 
GeneralRe: Multiple camera invoke side by side in single Instance. Pin
Amar Chaudhary15-Nov-15 23:18
Amar Chaudhary15-Nov-15 23:18 
QuestionAuto focus on/off Pin
Member 1180824314-Jul-15 5:22
Member 1180824314-Jul-15 5:22 
AnswerRe: Auto focus on/off Pin
Johannes Bildstein14-Jul-15 12:57
professionalJohannes Bildstein14-Jul-15 12:57 
GeneralRe: Auto focus on/off Pin
Member 1180824314-Jul-15 19:54
Member 1180824314-Jul-15 19:54 
GeneralRe: Auto focus on/off Pin
Johannes Bildstein15-Jul-15 2:08
professionalJohannes Bildstein15-Jul-15 2:08 
GeneralRe: Auto focus on/off Pin
Member 1180824321-Jul-15 1:11
Member 1180824321-Jul-15 1:11 
QuestionHot Shoe event not working when we enable the Live view in sample application provider with SDK Pin
raghavendra R.S7-Jul-15 1:40
raghavendra R.S7-Jul-15 1:40 
QuestionTruncated view in Canon EDSDK sample code Pin
raghavendra R.S28-Jun-15 2:23
raghavendra R.S28-Jun-15 2:23 
Questioncamera shutdown event Pin
eg_soft25-Jun-15 0:58
eg_soft25-Jun-15 0:58 
AnswerRe: camera shutdown event Pin
Johannes Bildstein18-Jul-15 10:57
professionalJohannes Bildstein18-Jul-15 10:57 
QuestionCanon EDSDK low frame rate. Pin
raghavendra R.S21-Jun-15 20:23
raghavendra R.S21-Jun-15 20:23 
AnswerRe: Canon EDSDK low frame rate. Pin
Johannes Bildstein22-Jun-15 0:04
professionalJohannes Bildstein22-Jun-15 0:04 
GeneralRe: Canon EDSDK low frame rate. Pin
raghavendra R.S22-Jun-15 7:25
raghavendra R.S22-Jun-15 7:25 
GeneralRe: Canon EDSDK low frame rate. Pin
Benjamin Krause6-Aug-15 3:43
Benjamin Krause6-Aug-15 3:43 
GeneralRe: Canon EDSDK low frame rate. Pin
Johannes Bildstein6-Aug-15 4:16
professionalJohannes Bildstein6-Aug-15 4:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.