I am trying to figure out how to call the Nikon SDK libraries from Swift but have little knowledge of loading and calling dynamic libraries or how to allocate memory and call functions with what appear to be lots of pointers.
I came across a recent post by @eskimo that seems to be the kind of approach I should be taking hence my post here.(How to call c function from .dylib file in SPM package swift 5)
Below is some basic information on the SDK itself and details of the files included in their sample program.
So it appears that there are different modules that can be loaded supporting different camera models and there is a MAID interface that is used by the client application to interface with the module (and camera device).
I assume the libNkPTPDriver2.dylib is the interface to the device and is used by the "Type0024 Module.bundle". I also assume that the "Type0024 Module.bundle" contains the client library that will be used by the app to access the camera device.
No idea about the other two libraries (Royalmile.framework and Carbon.framework, the latter appears to be an Apple framework!).
I have created a macOS Swift application in the same project as their sample application and included the same set of external libraries (embed&sign) and created a bridging header file and import the "Maid3.h", "Maid3d1.h" and "CtrlSample.h" files.
I have also created a function to load the libraries - modelled on the similar functions in the Function.cpp file - however the module seems to be placing in a PlugIns folder when the app gets built. I have zero experience using plugins but assume they are dynamically loaded hence the existence of the Search_Module() and Load_Module() functions in the sample.
So in an attempt to use the library from Swift I have created the class shown below.
I would appreciate it if someone could cast their eye over this and tell me if there is a better way of doing this and if this is the correct approach then were am I going wrong because calling the function currently throws an exception.
import Foundation
class NikonZ6Controller {
static let shared: NikonZ6Controller = NikonZ6Controller()
var gBundle: CFBundle? = nil
var g_pMAIDEntryPoint: LPMAIDEntryPointProc?
var g_bFileRemoved: UCHAR = 0
var g_ulCameraType: ULONG = 0 // CameraType
var modUrl: CFURL?
var pRefMod: LPRefObj?
var buf = [Int8](repeating: 0, count: 250)
var ulModID: ULONG = 0
var ulSrcID: ULONG = 0
var wSel: UWORD = 0;
var bRet: Bool = false;
var refMod: tagRefObj?
func load()->Bool{
guard self.Search_Module() else {
return false
}
guard self.Load_Module() else {
return false
}
// Allocate memory for reference to Module object.
self.pRefMod = UnsafeMutablePointer<RefObj>.allocate(capacity: MemoryLayout<RefObj>.size)
if ( self.pRefMod == nil ) {
print( "There is not enough memory." );
return false
}
InitRefObj( pRef: self.pRefMod! );
// Allocate memory for Module object.
self.pRefMod?.pointee.pObject = UnsafeMutablePointer<NkMAIDObject>.allocate(capacity: MemoryLayout<NkMAIDObject>.size)
if ( self.pRefMod?.pointee.pObject == nil ) {
puts( "There is not enough memory." );
if ( self.pRefMod != nil ) { free( self.pRefMod ); }
return false;
}
// Open Module object
self.pRefMod!.pointee.pObject!.pointee.refClient = UnsafeMutableRawPointer(self.pRefMod);
let intRepresentation = UInt64(bitPattern:Int64(Int(bitPattern: self.pRefMod!.pointee.pObject!)))
bRet = Command_Open( pParentObj: nil, // When Module_Object will be opend, "pParentObj" is "NULL".
pChildObj: intRepresentation, // Pointer to Module_Object
ulChildID: ulModID ); // Module object ID set by Client
if ( bRet == false ) {
print( "Module object can't be opened.\n" );
if ( self.pRefMod!.pointee.pObject != nil ) { free( self.pRefMod!.pointee.pObject ); }
if ( self.pRefMod != nil ) { free( self.pRefMod ); }
return false;
}
return true
}
func Search_Module()->Bool
{
let moduleName = "Type0024 Module.bundle" as CFString
let bundle = CFBundleGetMainBundle();
if(bundle != nil)
{
let pluginURL = CFBundleCopyBuiltInPlugInsURL( bundle );
if(pluginURL != nil)
{
let moduleURL = CFURLCreateCopyAppendingPathComponent( kCFAllocatorDefault, pluginURL, moduleName, false );
if(moduleURL != nil)
{
if(CFURLResourceIsReachable( moduleURL, nil ))
{
self.modUrl = moduleURL
return true
}
}
}
}
return false
}
func Load_Module( )->Bool
{
if(self.modUrl != nil)
{
if(self.gBundle != nil)
{
self.gBundle = nil;
}
self.gBundle = CFBundleCreate( kCFAllocatorDefault, self.modUrl! );
}
if(self.gBundle == nil)
{
return false
}
// Load and link dynamic CFBundle object
if(!CFBundleLoadExecutable(self.gBundle))
{
self.gBundle = nil;
return false
}
// Get entry point from BundleRef
// Set the pointer for Maid entry point LPMAIDEntryPointProc type variabl
self.g_pMAIDEntryPoint = CFBundleGetFunctionPointerForName( gBundle, "MAIDEntryPoint" as CFString).load(as: LPMAIDEntryPointProc.self)
return (self.g_pMAIDEntryPoint != nil)
}
func CallMAIDEntryPoint(
_ pObject: LPNkMAIDObject?, // module, source, item, or data object
_ ulCommand: ULONG , // Command, one of eNkMAIDCommand
_ ulParam: ULONG , // parameter for the command
_ ulDataType: ULONG , // Data type, one of eNkMAIDDataType
_ data: NKPARAM , // Pointer or long integer
_ pfnComplete: LPNKFUNC? , // Completion function, may be NULL
_ refComplete: NKREF? )->Int32? // Value passed to pfnComplete
{
if let ep = self.g_pMAIDEntryPoint {
let res = ep( pObject, ulCommand, ulParam, ulDataType, data, pfnComplete, refComplete );
return res
} else {
return nil
}
}
func Command_Open( pParentObj: LPNkMAIDObject?, pChildObj: NKPARAM, ulChildID: ULONG)->Bool
{
let lResult = CallMAIDEntryPoint( nil, kNkMAIDCommand_Open.rawValue, ulChildID,
kNkMAIDDataType_ObjectPtr.rawValue, pChildObj, nil, nil );
return lResult == kNkMAIDResult_NoError.rawValue;
}
//------------------------------------------------------------------------------------------------
//
func Command_Close( pObject: LPNkMAIDObject)->Bool
{
let nResult = CallMAIDEntryPoint( pObject, kNkMAIDCommand_Close.rawValue, 0, 0, 0, nil, nil );
return nResult == kNkMAIDResult_NoError.rawValue;
}
func InitRefObj( pRef: LPRefObj )
{
pRef.pointee.pObject = nil;
pRef.pointee.lMyID = 0x8000;
pRef.pointee.pRefParent = nil;
pRef.pointee.ulChildCount = 0;
pRef.pointee.pRefChildArray = nil;
pRef.pointee.ulCapCount = 0;
pRef.pointee.pCapArray = nil;
}
}