none
InterOp: Use an unmanaged DLL in a .NET project (Nikon SDK). RRS feed

  • Question

  • Hi all!

    I want to wrap a DLL so that it can be used in a .NET project.
    More specific: The Nikon SDK (MAID3) provides a couple of header-files (*.h), one DLL and one MD3-file (which I think is an DLL also).
    The DLL contains all base functionality and the MD3 file contains functionality specific for one type of camera.
    The functionality found in the DLL is for controlling a camera through code.

    The problem is that I'm not sure how to import the DLL.

    I've tried adding it as a reference to the .NET project. No luck though, since it's not a COM-DLL.
    The same goes for .NET Reflector which couldn't load it.

    I loaded the DLL using an InterOp native call to LoadLibrary and then a call to GetProcAddress. Both methods worked fine, the DLL was loaded and I got a reference to the EntryPoint-method through GetProcAddress. The name of the EntryPoint-method was found using dumpbin.exe /exports DLLNAME.dll. The thing I noticed was that the only method exported was the entrypoint method. None of the methods described in the documentation / header files were outputted.
    I guess that if I had a reference to the other methods / structures I could make it work.

    I also tried using DLLImport("PATHTODLL", EntryPoint:="ENTRYPOINTMETHODNAME") but I couldn't verify that it pointed to the right method.

    Where should I go from here?

    Kind regards
    / Richard
    Tuesday, November 4, 2008 10:58 AM

Answers

  • Seriously consider using Managed C++ to wrap the library. The advantage: the "It Just Works" (hey, it actually does, these days) feature of managed C++ allows you to consume and call the Nikon APIs as written. The challenge then, is to write a managed wrapper that provides a CLR-compatible interface for the clients of the managed C++ wrapper dll. General plot summary: Managed C++ allows you to code directly against native APIs using classic C++ syntax, and then mix-and-match managed and unmanaged code within the same module/file/function using tragically horribly nasty managed C++ syntax.

    Advantages: instant accessibility to the Nikon APIs; disadvantages: Managed C++ syntax is a bit .... verbose; and syntax for some of the CLR types is not entirely obvious. However, given a choice between trying to marshal to this API in C#, and wading through the ugly ugly Managed-C++ syntax, it's a no-brainer. I have successfully wrapped some very comlex APIs this way with great success. (Steinberg VST uses a very similar kind of calling scheme, and also used to suffer from the same 32-/64-bit portability flaws).

    As the previous poster correctly points out: 64-bit support ain't happening, though. In Nikon's defense, "void FAR*" says to me that they survived the 16-to-32 bit catastrophe; no great wonder that an old 16-bit API doesn't make it into 64-bit world.

    • Edited by Robin E Davies Tuesday, November 4, 2008 11:15 PM
    • Marked as answer by Zhi-Xin Ye Monday, November 10, 2008 10:56 AM
    Tuesday, November 4, 2008 11:06 PM

All replies

  • Hi again!

    It seems like the entrypoint method is somewhat of a core-method where you pass variables / references through it.

    The definitions of the structure / method is:
        typedef struct tagNkMAIDObject
        {
            ULONG    ulType;            // One of eNkMAIDObjectType
            ULONG    ulID;
            void FAR*    refClient;
            void FAR*    refModule;
        } NkMAIDObject, FAR* LPNkMAIDObject;


    SLONG    CallMAIDEntryPoint(
            LPNkMAIDObject    pObject,                // module, source, item, or data object
            ULONG                ulCommand,            // Command, one of eNkMAIDCommand
            ULONG                ulParam,                // parameter for the command
            ULONG                ulDataType,            // Data type, one of eNkMAIDDataType
            ULONG            data,                    // Pointer or long integer
            void FAR*            pfnComplete,        // Completion function, may be NULL
            void FAR*                refComplete );        // Value passed to pfnComplete

    How would an DllImport-declaration look like the method above?

    Thanks!
    Tuesday, November 4, 2008 12:46 PM
  • That's about as miserable an API as anyone could invent.  Declare the ULONG as int, the void pointers as IntPtr, the pObject argument as ref.  Pass IntPtr.Zero for the last 2 arguments.  You'll need to use Marshal.AllocHGlobal to initialize the IntPtr members.  How to do that properly is unguessable from the declarations.  Avoid using this API on a 64-bit operating system, it won't work.
    Hans Passant.
    Tuesday, November 4, 2008 1:20 PM
    Moderator
  • Seriously consider using Managed C++ to wrap the library. The advantage: the "It Just Works" (hey, it actually does, these days) feature of managed C++ allows you to consume and call the Nikon APIs as written. The challenge then, is to write a managed wrapper that provides a CLR-compatible interface for the clients of the managed C++ wrapper dll. General plot summary: Managed C++ allows you to code directly against native APIs using classic C++ syntax, and then mix-and-match managed and unmanaged code within the same module/file/function using tragically horribly nasty managed C++ syntax.

    Advantages: instant accessibility to the Nikon APIs; disadvantages: Managed C++ syntax is a bit .... verbose; and syntax for some of the CLR types is not entirely obvious. However, given a choice between trying to marshal to this API in C#, and wading through the ugly ugly Managed-C++ syntax, it's a no-brainer. I have successfully wrapped some very comlex APIs this way with great success. (Steinberg VST uses a very similar kind of calling scheme, and also used to suffer from the same 32-/64-bit portability flaws).

    As the previous poster correctly points out: 64-bit support ain't happening, though. In Nikon's defense, "void FAR*" says to me that they survived the 16-to-32 bit catastrophe; no great wonder that an old 16-bit API doesn't make it into 64-bit world.

    • Edited by Robin E Davies Tuesday, November 4, 2008 11:15 PM
    • Marked as answer by Zhi-Xin Ye Monday, November 10, 2008 10:56 AM
    Tuesday, November 4, 2008 11:06 PM
  • Funny I should stumble into this thread! I'm looking for the same kind of wrapper information for the same Nikon SDK files. I have somewhat committed myself to a final school project using a C# GUI with calls to the Nikon dll's to control a variety of the Nikon cameras. I have a 2-inch stack of printouts from various sources that all show in a general way how to set up simple wrappers for unmanaged code but they are not very clear. I'm not sure that the Prof will let me change the scope of what I have committed to, so any suggestions for the wrapper code would be greatly appreciated.

    Friday, January 9, 2009 11:33 PM
  • HI, I am wondering if you were able to resolve this. i am looking for a jumpstart to be able to use the SDK with either VB.NET or with C#. Any sample code that can help me with this would be greatly appreciated.

    Thank you.

    Michael
    Sunday, March 1, 2009 7:01 PM
  • Been working with the D300/D200 SDK for a few years now. You can easily get it to work with C#, or any other language.

    Steps:
    1. Modify the sample code to create a class, be sure to remove all global and static variables.
    2. For the callbacks to work correctly they must be in static scope, however if you intend on using more than one camera you cannot accept this.  Modify tagNkMAIDObject to look like this.
    typedef struct tagNkMAIDObject
    {
    ULONG ulType; // One of eNkMAIDObjectType
    ULONG ulID;
    NKREF refClient;
    NKREF refModule;
    void* object;
    } NkMAIDObject, FAR* LPNkMAIDObject;

    Also modify CallMAIDEntryPoint to set the pObject->object
    SLONG <OBJECTNAME>::CallMAIDEntryPoint( 
    LPNkMAIDObject pObject, // module, source, item, or data object
    ULONG ulCommand, // Command, one of eNkMAIDCommand
    ULONG ulParam, // parameter for the command
    ULONG ulDataType, // Data type, one of eNkMAIDDataType
    NKPARAM data, // Pointer or long integer
    LPNKFUNC pfnComplete, // Completion function, may be NULL
    NKREF refComplete ) // Value passed to pfnComplete
    {
    try{
    if ((this!=NULL) && ( pObject !=NULL))
    pObject->object = this;
    }catch(...){
    }
    }
    3. Rename the original call back functions removing "CALLPASCAL CALLBACK " place these functions inside your class.
    4. Create new global call back functions that cast  (<ObjectName>*)pRefParent->pObject->object;  Simply call <ObjectName>-><CallbackFunction>
    5. Rename all references to the old call back functions and point them to your new one.
    6. Make the module name settable when you create your class.  (Module *.md3 uses global address space)
    7. Make global functions that can be called with a void* pointer to the object (this will allow you to create an object for the dll):
    ex double __stdcall GetExposureStatus(void* object); 
    the call would look like (<ObjectName>*)object->getExposureStatus();
    8. Create an ordered .def file containing all the global functions you need to access.
    9. Compile and use in your desired language.

    Note: If you have to use a global be sure to use a CRITICAL_SECTION so that multiple instances can't use the same variable.
    Note: You will need one .md3 file for each camera you control, even if they are both d300.. etc. 
    This took about 3 weeks to code, good luck!


    Wednesday, January 20, 2010 7:32 PM
  • Hi,

    I'm encountering a common problem and didn't find any help on forms.
    I'd like to wrap in .NET the sdk provided for my D700 (it must be quite similar to D300)  in order to capture photo from an app.
    I'm not experimented enough to do such a thing so could you please help me.
    Would it be possible to show me a piece of code that could lead me to wrap common functions?

    Thanks in advance.
    Morgan.
    Thursday, February 11, 2010 9:33 AM
  • Hi,  I'd be very interested in seeing this source.  It's for a commercial project so we'd be happy to discuss terms?

    Wednesday, September 22, 2010 11:11 AM
  • @FatalException - I'm an experienced .NET developer but very rusty on C++.  Do you have any more detail on how to do this?  I'm not sure where to start with your steps 8, 6, and some others.  Could you give a more detailed explanation, or (even better) be willing to work with it yourself?  I can provide everything you need, and maybe a case of beer or something :)
    Tuesday, December 7, 2010 5:02 PM
  • Hi,

    I am working with the Nikon D5100 camera and need to interface with it trough .net.

    I read your thread on wrapper code and would like to acquire the api for commercial use.
    I have zero c++ skills - only c# and need to operate this camera asap.

    Please respond with terms and pricing or even a reference to some other purchasable .net sdk.

    Regards

    Francois

    Tuesday, May 29, 2012 8:43 AM
  • I wrote a C# wrapper for the Nikon MAID SDK. Get it here:

    http://sourceforge.net/p/nikoncswrapper/wiki/Home/

    Monday, December 31, 2012 8:32 AM