In the meantime I have also solved the errors with setting measurement unit and scan area ("Object is nil."). The setting of measurement unit and scan area had to be moved to the callback didSelectFunctionalUnit(). Now the modified code example below works like a charm on both Snow Leopard and Mavericks.
The only quirk remaining is that setting the scan area doesn't seem to have any effect: the resulting image MyScan.png is always a whole page although for testing I even changed the measurement unit to the (for me) more familiar centimeters and a smaller area...
Code: Select all
#ICDeviceLocationTypeMaskLocal = $00000100
#ICDeviceLocationTypeMaskRemote = $0000FE00
#ICDeviceTypeMaskScanner = $00000002
#ICDeviceTypeScanner = $00000002
ImportC ""
class_addMethod(Class.i, Selector.i, *Callback, Types.P-ASCII)
class_createInstance(Class.i, ExtraBytes.i)
class_addProtocol(Class.i, Protocol.i)
objc_allocateClassPair(ModelClass.i, NewClassName.P-ASCII, ExtraBytes.i)
objc_lookUpClass(ClassName.P-ASCII)
object_setClass(ObjectToModify.i, NewClass.i)
objc_registerClassPair(NewClass.i)
objc_getProtocol(ProtocolName.P-ASCII)
sel_registerName(MethodName.P-ASCII)
EndImport
ImportC "/System/Library/Frameworks/ImageCaptureCore.framework/ImageCaptureCore"
EndImport
ImportC "/System/Library/Frameworks/Quartz.framework/Quartz"
EndImport
Enumeration
#MainWindow
#scannerList
#scanButton
EndEnumeration
Global Dim scanners.i(0)
Global.i delegateClass, activeScanner, selectedScanner, scanning, selectedFunctionalUnit
Procedure scan()
Debug "initiating scan..."
scanning = 1
CocoaMessage(0, activeScanner, "requestScan")
EndProcedure
ProcedureC deviceDidBecomeReady(object.i, selector.i, device.i)
Protected FunctionalUnitArray.I
Protected NumFunctionalUnits.I
Debug "selected device is ready..."
scanFolder = CocoaMessage(0, 0, "NSURL fileURLWithPath:$", @"/Users/Shardik/Scans/")
scanFile = CocoaMessage(0, 0, "NSString stringWithString:$", @"MyScan")
; CocoaMessage(0, device, "requestSelectFunctionalUnit:", 0) ;0 for flatbed type
CocoaMessage(0, device, "requestSelectFunctionalUnit:", 3) ;3 for document feeder type
CocoaMessage(0, activeScanner, "setTransferMode:", 0) ;0 for file-mode
CocoaMessage(0, activeScanner, "setDownloadsDirectory:", scanFolder)
CocoaMessage(0, activeScanner, "setDocumentName:$", @"MyScan")
CocoaMessage(0, activeScanner, "setDocumentUTI:$", @"public.png")
FunctionalUnitArray = CocoaMessage(0, device, "availableFunctionalUnitTypes")
If FunctionalUnitArray
NumFunctionalUnits = CocoaMessage(0, FunctionalUnitArray, "count")
If NumFunctionalUnits
Debug "Functional unit types:"
For i = 0 To NumFunctionalUnits - 1
Select CocoaMessage(0, CocoaMessage(0, FunctionalUnitArray,
"objectAtIndex:", i), "integerValue")
Case 0
Debug "- Flatbed"
Case 1
Debug "- Positive transparency"
Case 2
Debug "- Negative transparency"
Case 3
Debug "- Document feeder"
EndSelect
Next i
EndIf
EndIf
EndProcedure
ProcedureC didAddDevice(object.i, selector.i, deviceBrowser.i, addedDevice.i, moreComing.i)
Debug "device found and added..."
deviceType.i = CocoaMessage(0, addedDevice, "type")
deviceName.s = PeekS(CocoaMessage(0, CocoaMessage(0, addedDevice, "name"), "UTF8String"), -1, #PB_UTF8)
If moreComing = #NO And (deviceType & #ICDeviceTypeMaskScanner) = #ICDeviceTypeScanner
newIndex = ArraySize(scanners()) + 1
ReDim scanners(newIndex)
scanners(newIndex) = addedDevice
AddGadgetItem(#scannerList, -1, deviceName)
CocoaMessage(0, addedDevice, "setDelegate:",
class_createInstance(delegateClass, 0)) ;PureBasic app controls the scanner
EndIf
EndProcedure
ProcedureC didRemoveDevice(object.i, selector.i, removedDevice.i)
Debug "device removed"
EndProcedure
ProcedureC didRemoveDeviceInBrowser(object.i, selector.i, deviceBrowser.i, removedDevice.i, moreGoing.i)
Debug "device removed in browser..."
CocoaMessage(0, removedDevice, "requestCloseSession")
CocoaMessage(0, removedDevice, "setDelegate:", 0)
RemoveGadgetItem(#scannerList, selectedScanner - 1)
ReDim scanners(ArraySize(scanners()) - 1)
If activeScanner = removedDevice
activeScanner = 0
EndIf
EndProcedure
ProcedureC didOpenSessionWithError(object.i, selector.i, device.i, error.i)
Debug "session opened... errors: " + Str(error)
EndProcedure
ProcedureC didCloseSessionWithError(object.i, selector.i, device.i, error.i)
Debug "session closed... errors: " + Str(error)
EndProcedure
ProcedureC didSelectFunctionalUnit(object.i, selector.i, scannerDevice.i, functionalUnit.i, error.i)
Protected scanArea.NSRect
If error = 0
If functionalUnit <> 0
Debug "functional unit selected: " + functionalUnit
CocoaMessage(0, functionalUnit, "setMeasurementUnit:", 0)
Debug "MeasurementUnit set to Inches"
With scanArea
\origin\x = 0
\origin\y = 0
\size\width = 10
\size\height = 10
EndWith
CocoaMessage(0, functionalUnit, "setScanArea:@", @scanArea)
CocoaMessage(@scanArea, functionalUnit, "scanArea")
Debug "scanArea set to (" + Str(scanArea\origin\x) + "," +
Str(scanArea\origin\y) + ")-(" +
Str(scanArea\origin\x + scanArea\size\width) + "," +
Str(scanArea\origin\y + scanArea\size\height) + ")"
EndIf
Else
Debug "functional unit selected... errors: " + Str(error)
EndIf
EndProcedure
ProcedureC didReceiveStatusInformation(object.i, selector.i, device.i, StatusDictionary.i)
Debug "receiving status info..."
If StatusDictionary
Debug PeekS(CocoaMessage(0, CocoaMessage(0, StatusDictionary,
"valueForKey:$", @"ICLocalizedStatusNotificationKey"), "UTF8String"),
-1, #PB_UTF8)
EndIf
EndProcedure
ProcedureC didScanToURL(object.i, selector.i, scannerDevice.i, URL.i, *data)
Debug "scanned to " +
PeekS(CocoaMessage(0, CocoaMessage(0, URL, "absoluteString"), "UTF8String"),
-1, #PB_UTF8)
scanning = 0
EndProcedure
ProcedureC didCompleteScanWithError(object.i, selector.i, scannerDevice.i, error.i)
Debug "scan completed... errors: " + Str(error)
scanning = 0
EndProcedure
ProcedureC didEncounterError(object.i, selector.i, device.i, error.i)
Debug "encountered error: " + Str(error)
scanning = 0
EndProcedure
wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
OpenWindow(#MainWindow, 100, 100, 800, 500, "Scanner Application", wFlags)
ListViewGadget(#scannerList, 0, 0, 200, 500)
ButtonGadget(#scanButton, 220, 10, 100, 30, "SCAN")
SetGadgetColor(#scannerList, #PB_Gadget_BackColor, RGB(200, 200, 255))
deviceBrowser = CocoaMessage(0, 0, "ICDeviceBrowser new")
delegateClass = objc_allocateClassPair(objc_lookUpClass("NSObject"), "PB_Delegate", 0) ;PureBasic app class
class_addMethod(delegateClass,
sel_registerName("didRemoveDevice:"), @didRemoveDevice(), "v@:@")
class_addMethod(delegateClass,
sel_registerName("deviceDidBecomeReady:"), @deviceDidBecomeReady(), "v@:@")
class_addMethod(delegateClass,
sel_registerName("device:didEncounterError:"), @didEncounterError(), "v@:@@")
class_addMethod(delegateClass,
sel_registerName("device:didOpenSessionWithError:"), @didOpenSessionWithError(), "v@:@@")
class_addMethod(delegateClass,
sel_registerName("device:didCloseSessionWithError:"), @didCloseSessionWithError(), "v@:@@")
class_addMethod(delegateClass,
sel_registerName("device:didReceiveStatusInformation:"), @didReceiveStatusInformation(), "v@:@@")
class_addMethod(delegateClass,
sel_registerName("deviceBrowser:didAddDevice:moreComing:"), @didAddDevice(), "v@:@@@")
class_addMethod(delegateClass,
sel_registerName("deviceBrowser:didRemoveDevice:moreGoing:"), @didRemoveDeviceInBrowser(), "v@:@@@")
class_addMethod(delegateClass,
sel_registerName("scannerDevice:didScanToURL:data:"), @didScanToURL(), "v@:@@@")
class_addMethod(delegateClass,
sel_registerName("scannerDevice:didCompleteScanWithError:"), @didCompleteScanWithError(), "v@:@@")
class_addMethod(delegateClass,
sel_registerName("scannerDevice:didSelectFunctionalUnit:error:"), @didSelectFunctionalUnit(), "v@:@@@")
objc_registerClassPair(delegateClass)
CocoaMessage(0, deviceBrowser, "setDelegate:", class_createInstance(delegateClass, 0)) ;PureBasic app controls the browser
CocoaMessage(0, deviceBrowser, "setBrowsedDeviceTypeMask:", #ICDeviceLocationTypeMaskLocal |
#ICDeviceLocationTypeMaskRemote |
#ICDeviceTypeMaskScanner)
CocoaMessage(0, deviceBrowser, "start")
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
appQuit = 1
Case #PB_Event_Gadget
Select EventGadget()
Case #scanButton
scan()
Case #scannerList
Select EventType()
Case #PB_EventType_LeftDoubleClick
selectedScanner = GetGadgetState(#scannerList) + 1
activeScanner = scanners(selectedScanner)
CocoaMessage(0, activeScanner, "requestOpenSession")
EndSelect
EndSelect
EndSelect
Until appQuit = 1
If activeScanner
CocoaMessage(0, activeScanner, "release")
EndIf
CocoaMessage(0, deviceBrowser, "stop")
; CocoaMessage(0, deviceBrowser, "release")
: I have modified the didReceiveStatusInformation() callback to also display the status which is contained in the status dictionary passed as a parameter to this callback.
: I have modified the deviceDidBecomeReady() callback to also display the available functional unit types.
: To prevent an invalid memory access (IMA) when closing the program after the scanner's power button had already been switched off, I have added
at the end of the code example. I have tested this code successfully with PB 5.44 x86 in both ASCII and Unicode mode on MacOS 10.6.8 (Snow Leopard) and with PB 5.44 x86 and x64 in both ASCII and Unicode mode on MacOS 10.7.5 (Lion).