MacRuby pointer, referencing, dereferencing when using Cocoa Frameworks - objective-c

On MacRuby Pointer to typedef struct, I learnt how to dereference a pointer created with
x=Pointer.new_with_type
...
==> use x.value, or x[0]
Works a treat !
Now I want to learn what I believe to be the "opposite". I'm trying to use this API.
OSStatus SecKeychainCopySettings (
SecKeychainRef keychain,
SecKeychainSettings *outSettings
);
Second parameter must be a Pointer. But I never manage to get the real outSettings of the keychain opened, I only get the default settings.
framework 'Security'
keychainObject = Pointer.new_with_type('^{OpaqueSecKeychainRef}')
SecKeychainOpen("/Users/charbon/Library/Keychains/Josja.keychain",keychainObject)
#attempt #1
settings=Pointer.new_with_type('{SecKeychainSettings=IBBI}')
SecKeychainCopySettings(keychainObject.value, settings)
p settings.value
#<SecKeychainSettings version=0 lockOnSleep=false useLockInterval=false lockInterval=0>
#attempt #2
settings2=SecKeychainSettings.new
result = SecKeychainCopySettings(keychainObject.value, settings2)
p settings2
#<SecKeychainSettings version=0 lockOnSleep=false useLockInterval=false lockInterval=0>
The settings of the Keychain opened should read
#<SecKeychainSettings version=0 lockOnSleep=true useLockInterval=true lockInterval=1800>
What am I missing ?

Got it !
The doc to SecKeychainCopySettings mentions
outSettings
On return, a pointer to a keychain settings structure.
Since this structure is versioned, you must allocate the memory for it
and fill in the version of the structure before passing it to the
function.
So we can't just create a Pointer to SecKeychainSettings. We must set the version of the Struct that is pointed by the pointer to to something.
settings=Pointer.new_with_type('{SecKeychainSettings=IBBI}')
#settings[0] dereferences the Pointer
#for some reason, settings[0][0]=1 does not work, nor settings[0].version=1
settings[0]=[1,false,false,0]
#we are redefining the complete SecKeychainSettings struct
# [0]=version [1]=lockOnSleep [2]=useLockInterval [3]=lockInterval
result = SecKeychainCopySettings(keychainObject.value, settings)
p settings
=> #<SecKeychainSettings version=1 lockOnSleep=true useLockInterval=false lockInterval=300> irb(main):019:0>

Related

Fortran Functions with a pointer result in a normal assignment

After some discussion on the question found here Correct execution of Final routine in Fortran
I thought it will be useful to know when a function with a pointer result is appropriate to use with a normal or a pointer assignment. For example, given this simple function
function pointer_result(this)
implicit none
type(test_type),intent(in) pointer :: this
type(test_type), pointer :: pointer_result
allocate(pointer_result)
end function
I would normally do test=>pointer_result(test), where test has been declared with the pointer attribute. While the normal assignment test=pointer_result(test) is legal it means something different.
What does the normal assignment imply compared to the pointer assignment?
When does it make sense to use one or the other assignment?
A normal assignment
test = pointer_result()
means that the value of the current target of test will be overwritten by the value pointed to by the resulting pointer. If test points to some invalid address (is undefined or null) the program will crash or produce undefined results. The anonymous target allocated by the function will have no pointer to it any more and the memory will be leaked.
There is hardly any legitimate use for this, but it is likely to happen when one makes a typo and writes = instead of =>. It is a very easy one to make and several style guides recommend to never use pointer functions.

How one deals with multiple pointer level (like char**) in Squeak FFI

I want to deal with a structure like this struct foo {char *name; char **fields ; size_t nfields};
If I define corresponding structure in Squeak
ExternalStructure subclass: #Foo
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'FFI-Tests'.
and define the fields naively with
Foo class>fields
^#(
(name 'char*')
(fields 'char**')
(nfields 'unsigned long')
)
then generate the accessors with Foo defineFields, I get those undifferentiated types for name and fields:
Foo>>name
^ExternalData fromHandle: (handle pointerAt: 1) type: ExternalType char asPointerType
Foo>>fields
^ExternalData fromHandle: (handle pointerAt: 5) type: ExternalType char asPointerType
That is troubling, the second indirection is missing for the fields accessor.
How should I specify fields accessor in the spec?
If not possible, how do I define it manually?
And I have the same problem for this HDF5 function prototype: int H5Tget_array_dims(hid_t tid, hsize_t *dims[])
The following syntax is not accepted:
H5Tget_array_dims: tid with: dims
<cdecl: long 'H5Tget_array_dims'(Hid_t Hsize_t * * )>
The compiler barks argument expected -> before the second *...
I add to resort to void * instead, that is totally bypassing typechecking - less than ideal...
Any idea how to deal correctly with such prototype?
Since Compiler-mt.435, the parser will not complain anymore but call back to ExternalType>>asPointerToPointerType. See source.squeak.org/trunk/Compiler-mt.435.diff and source.squeak.org/FFI/FFI-Kernel-mt.96.diff
At the time of writing this, such pointer-to-pointer type will be treated as regular pointer type. So, you loose the information that the external type actually points to an array of pointers.
When would one need that information?
When coercing arguments in the FFI plugin during the call
When constructing the returned object in the FFI plugin during the call
When interpreting instances of ExternalData from struct fields and FFI call return values
In tools such as the object explorer
There already several kinds of RawBitsArray in Squeak. Adding String and ExternalStructure (incl. packed or union) to the mix, we have all kinds of objects in Squeak to map the inner-most dimension (i.e., int*, char*, void*). ExternalData can represent the other levels of the multi-dimensional array (i.e., int**, char**, void** and so on).
So, there are remaining tasks here:
Store that pointer dimension information maybe in a new external type to be found via ExternalType>>referencedType. We may want to put new information into compiledSpec. See http://forum.world.st/FFI-Plugin-Question-about-multi-dimensional-arrays-e-g-char-int-void-td5118484.html
Update value reading in ExternalArray to unwrap one pointer after the other; and let the code generator for struct-field accessors generate code in a similar fashion.
Extend argument coercing in the plugin to accept arrays of the already supported arrays (i.e. String etc.)

Incompatible pointer error when trying to get a HID device reference from HID manager on Mac

I'm trying to write a User-Space Device driver to pull some data from a custom HID device. I'm do the following to store a reference of the device in the HID manage into a variable.
CFSetRef device = IOHIDManagerCopyDevices(HIDManager);
after this I do the following to register my callback which is setting up the report read from the device (another area I'm struggling with.)
IOHIDDeviceRegisterInputReportCallback(device, report, ReadDailyDataResonposeSize, Handle_IOHIDDeviceIOHIDReportCallback, NULL);
I'm getting an error in the above function on the reference to 'device' saying "Incompatible pointer types passing CFSetRef"
If I try to change the type that device is when creating it though I get another saying that it needs to be a CFSetRef. So I'm a little confused, anyone have any advice. I'm very new to C as well as working on Macs. The documentation has come off pretty terse to me thus far.
EDIT: Here Is a link to the rest of my code for reference.
http://pastebin.com/rFsHisdh
This is the signature of the IOHIDDeviceRegisterInputReportCallback function according to the documentation:
CF_EXPORT void IOHIDDeviceRegisterInputReportCallback(
IOHIDDeviceRef device,
uint8_t *report,
CFIndex reportLength,
IOHIDReportCallback callback,
void *context) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;
As you can see the first argument should be a IOHIDDeviceRef and you are passing in a CFSetRef which provide[s] support for the mathematical concept of a set as Martin R answer indicates.
To get the elements of the set and pass the device (if any) you should do the following:
CFSetRef devices = IOHIDManagerCopyDevices(HIDManager);
CFIndex size = CFSetGetCount(devices);
if(size > 0) {
CFTypeRef array[size]; // array of IOHIDDeviceRef
CFSetGetValues(devices, array);
IOHIDDeviceRegisterInputReportCallback((IOHIDDeviceRef)array[0], report, ReadDailyDataResonposeSize, Handle_IOHIDDeviceIOHIDReportCallback, NULL);
}
Hope it helps.
IOHIDManagerCopyDevices() returns a set of IOHIDDeviceRef elements.
You have to get one element from the set and pass that to
IOHIDDeviceRegisterInputReportCallback().
I ran into this problem. You were pasting from an Apple document that is no longer correct ( https://developer.apple.com/library/archive/technotes/tn2187/_index.html ). Your report variable is allocated in the wrong format, causing an memory error during runtime. Do the following:
uint8_t *report = (uint8_t *)malloc(reportSize);
IOHIDDeviceRegisterInputReportCallback(device, report, ReadDailyDataResonposeSize, Handle_IOHIDDeviceIOHIDReportCallback, NULL);```

Managed struct being sent into an unmanaged function

int atClass1::read_file
(String^ file_path, /* Path tofile */
HdfCallVars % ret_vals)
This is my function. Within it I have lots of native c++ code. I ran into a serious problem though
/* Iterate through the links, filling in needed data as discovered. */
io_err = H5Literate (group_id, H5_INDEX_NAME, H5_ITER_NATIVE,
&i, get_sonar_data, (void*)& ret_vals);
will not compile! Says ret_vals is managed and I can't do pointerey ampersandey stuff to it. Am I in trouble? Or is there a way out of my dilemma? The H5 function is a call into the HDF5 library.
thanks,
saroj
In .Net there is no guarantee that an object will remain on it current memory position as the garbage collector will "compact" the heap space when it wants to.
To get a native pointer to a managed object you should "pin" the object:
pin_ptr<HdfCallVars> pinned = &ret_vals;
io_err = H5Literate (group_id, H5_INDEX_NAME, H5_ITER_NATIVE,
&i, get_sonar_data, (void*)pinned);
Note that the pointer will be unpinned after the variable pinned goes out of scope, if H5Literate stores the pointer for future use, you should pin the value using System::Runtime::InteropServices::GCHandle, like this:
GCHandle ^handle = GCHandle::Alloc(ret_vals);
io_err = H5Literate (group_id, H5_INDEX_NAME, H5_ITER_NATIVE,
&i, get_sonar_data, (void*)handle->AddrOfPinnedObject());
When you don't need the pointer anymore, you should free it:
handle->Free();

ComBSTR assignment

I'm confused about COM string assignments. Which of the following string assignment is correct. Why?
CComBSTR str;
.
.
Obj->str = L"" //Option1
OR should it be
Obj->str = CComBSTR(L"") //Option2
What is the reason
A real BSTR is:
temporarily allocated from the COM heap (via SysAllocString() and family)
a data structure in which the string data is preceded by its length, stored in a 32-bit value.
passed as a pointer to the fifth byte of that data structure, where the string data resides.
See the documentation:
MSDN: BSTR
Most functions which accept a BSTR will not crash when passed a BSTR created the simple assignment. This leads to confusion as people observe what seems to be working code from which they infer that a BSTR can be initialized just like any WCHAR *. That inference is incorrect.
Only real BSTRs can be passed to OLE Automation interfaces.
By using the CComBSTR() constructor, which calls SysAllocString(), your code will create a real BSTR. The CComBSTR() destructor will take care of returning the allocated storage to the system via SysFreeString().
If you pass the CComBSTR() to an API which takes ownership, be sure to call the .Detach() method to ensure the BSTR is not freed. BSTRs are not reference counted (unlike COM objects, which are), and therefore an attempt to free a BSTR more than once will crash.
If you use str = CComBSTR(L"") you use the constructor:
CComBSTR( LPCSTR pSrc );
If you use str = L"" you use the assignment operator:
CComBSTR& operator =(LPCSTR pSrc);
They both would initialize the CComBSTR object correctly.
Personally, I'd prefer option 1, because that doesn't require constructing a new CComBSTR object. (Whether their code does so behind the scenes is a different story, of course.)
Option 1 is preferred because it only does one allocation for the string where as option 2 does 2 (not withstanding the creation of a new temporary object for no particular reason). Unlike the bstr_t type in VC++ the ATL one does not do referenced counted strings so it will copy the entire string across.