MonoTouch: Help converting a couple lines of Objective C to C# - xamarin.ios

Since coming from a pure C# background I am having a little bit of trouble with some of the Objective C syntax. I am playing around with the Audio Queue and I am trying to set the InputAudioQueue property EnableLevelMetering and get the CurrentLevelMeterDB property. I found a couple examples in Objective C.
Setting the property:
UInt32 enabledLevelMeter = true;
AudioQueueSetProperty(queue,kAudioQueueProperty_EnableLevelMetering,&enabledLevelMeter,sizeof(UInt32));
Getting the value:
AudioQueueLevelMeterState levelMeter;
UInt32 levelMeterSize = sizeof(AudioQueueLevelMeterState);
AudioQueueGetProperty(queue,kAudioQueueProperty_CurrentLevelMeterDB,&levelMeter,&levelMeterSize);
Float32 peakDB = levelMeter.mPeakPower;
Float32 averageDB = levelMeter.mAveragePower;
The API reference for AudioQueue is very minimal. I am not sure what to use for the following values in C#.
public bool SetProperty (AudioQueueProperty property, int dataSize, IntPtr propertyData)
public IntPtr GetProperty (AudioQueueProperty property, out int size)
Could somebody help a me out? Thanks.

Once you have your AudioQueue created (input or output), you can call:
int enabled = queue.GetProperty<int> (AudioQueueProperty.EnableLevelMetering);
Setting it is a little bit more annoying, you have to use:
queue.SetProperty (AudioQueueProperty.EnableLevelMetering, 4, (IntPtr) &enabled);

Related

Misleading exception message in GatewayMethodInboundMessageMapper with un-annotated parameters

The following code throws a MessagingException with message At most one parameter (or expression via method-level #Payload) may be mapped to the payload or Message. Found more than one on method [public abstract java.lang.Integer org.example.PayloadAndGatewayHeader$ArithmeticGateway.add(int,int)].
#MessagingGateway
interface ArithmeticGateway {
#Gateway(requestChannel = "add.input", headers = #GatewayHeader(name = "operand", expression = "#args[1]"))
Integer add(#Payload final int a, final int b);
}
The desired functionality could be achieved with something like:
#MessagingGateway
interface ArithmeticGateway {
#Gateway(requestChannel = "add.input", headers = #GatewayHeader(name = "operand", expression = "#args[1]"))
#Payload("#args[0]")
Integer add(final int a, final int b);
}
Should the first version also work? Nevertheless I believe the error message could be improved.
A sample project can be found here. Please check org.example.PayloadAndGatewayHeader and org.example.PayloadAndGatewayHeaderTest.
EDIT
The purpose of #GatewayHeader was to show why one may want to have additional parameters that will not be part of the payload but I am afraid it created confusion. Here is a more streamlined example:
#MessagingGateway
interface ArithmeticGateway {
#Gateway(requestChannel = "identity.input")
Integer identity(#Payload final int a, final int unused);
}
Shouldn't the unused parameter be ignored since there is already another one that is annotated with #Payload?
You can't mix parameter annotations (which are static) with expressions (which are dynamic) because the static code analysis can't anticipate what the dynamic expression will resolve to at runtime. It is probably unlikely, but there theoretically could be conditions in the expression. In any case, it can't determine at analysis time that the expression will provide a value for #args[1] at runtime (it could, of course for this simple case, but not all cases are this simple).
Use one or the other; use your second approach or
Integer add(#Payload final int a, #Header("operand") final int b);

constant expression required while using constants

private static int counter = 0; public static final int
CLIENT_REQUEST_TIME = counter++;
...
switch (command) { case Command.CLIENT_REQUEST_TIME:
...
but here it comes the
"Error:(20, 25) java: constant expression required",
for the case statement.
but why on earth? since CLIENT_REQUEST_TIME is constant
I know this is something that should be rather addressed to Oracle and I can choose from millions of workarounds... but just in case someone can see some logic behind, it will certainly make me sleep better tonight :)
This error comes because you set not constant property to constant field. I am not understand what this code must to do, but possibly you can change it to look like this:
private static int counter = 0;
public static int CLIENT_REQUEST_TIME = counter++;

CLI layer Object^ to byte array conversion

I have C# sample application with code
SampleClass cls = new SampleClass();
byte[] b = new byte[] { 1, 2, 3 };
object obj = b;
cls.Send(obj);
SampleClass is seperate dll which is implemented using Managed C++(CLI).
Now in Send method i am doing following.
void Send(Object^ data)
{
cli::array<System::Byte>^ b = data; //it is giving error
}
How can i convert Object^ to cli::array^?
cli::array<System::Byte>^ b = data; //it is giving error
It is a compile error. That statement is not valid in C++/CLI, just like it is never valid in C#. And you solve it the same way as you do in C#, you must use a cast to make the conversion.
That rule isn't there just to make your life difficult, a conversion like this is very risky and apt to throw an InvalidCastException. Having to use a cast operator alerts the reader, helps you debug the program and convinces the compiler that you know what you're doing.
Casting managed object references in C++/CLI is done with the safe_cast<> keyword. Fix:
auto b = safe_cast<cli::array<System::Byte>^>(data);

JNA - Use structure array as byref argument

I know parts of this issue is covered by some posts here and I have looked at them and tested some but with no luck.
I have this native method signature which should populate the provided CBadgeData structure array with results:
int elc_GetBadges(int nHandle, char* cErr, int* nRecCount, CBadgeData** arr)
The CBadgeData structure is implemented as follows:
package test.elcprog;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
public class CBadgeData extends Structure{
public static class ByReference extends CBadgeData implements Structure.ByReference { }
public int nBadgeID, nTrigger, nExtraData;
public String cName;
public CBadgeData(Pointer pointer){
super(pointer);
}
public CBadgeData(){ }
public String ToString() {
return nBadgeID + "," + nTrigger + "," + nExtraData + "," + cName;
}
#Override
protected List getFieldOrder() {
String[] s = new String[]{"nBadgeID","nTrigger","nExtraData","cName"};
return Arrays.asList(s);
}
}
My last try to craft this argument and call the method looked like this:
CBadgeData.ByReference[] badges = new CBadgeData.ByReference[max_items];
new CBadgeData.ByReference().toArray(badges);
int ret = inst.elc_GetBadges(handle, err, recCount, badges);
It fails with segmentation error.
My Question is what Java type should be provided here as an argument for the native CBadgeData** in the call to elc_GetBadges?
EDIT -1-
Populating the array myself (with or without terminating null pointer) didn't work and caused further Seg crashes. I then used Pointer[] arg as technomage suggested:
Pointer[] pointers = new Pointer[max_items];
for(int i=0; i<max_items; i++){
pointers[i] = new CBadgeData.ByReference().getPointer();
}
int ret = inst.elc_GetBadges(handle, err, recCount, pointers);
This caused no error but seems to not make any changes to the returning struct which should have contain 4 items in this case:
int bid = new CBadgeData(pointers[i]).nBadgeID; // this returns null for all items
Using explicit read() / write() on the struct led to Seg crashes again (on the read):
Any idea what am I still missing here?
EDIT -2-
Interestingly enough - using the Memory.get directly, after calling the native method, gets the correct results:
Memory m= (Memory)pointers[0];
System.out.println("1st int: "+m.getInt(0)); // this gets 24289 which is 5ee1
System.out.println("2nd int: "+m.getInt(4)); // this gets 3
System.out.println("3rd int: "+m.getInt(8)); // this gets 255
System.out.println("String: "+m.getString(12)); // this gets "Badge[5EE1]" as supposed
But the read() still crashes. Any thoughts?
I'm inferring that CBadgeData** input is intended to be an array of pointer to CBadgeData.
As such, the Structure.ByReference tagging is correct.
Structure.toArray() is probably not appropriate here, or at least not necessary (it allocates a contiguous block of structs in memory). You can just populate your array with CBadgeData.ByReference instances.
Perhaps your callee is expecting a NULL pointer at the end of the array? I don't see another indicator of the array length to the callee.
CBadgeData.ByReference[] badges = new CBadgeData.ByReference[max_items+1];
for (int i=0;i < badges.length-1;i++) {
badges[i] = new CBadgeData.ByReference();
}
badges[badges.length-1] = null;
Pretty sure that works. If for whatever reason there's a bug handling Structure.ByReference[], I know that Pointer[] is reliable and will do the same thing.
EDIT
If you use Pointer[] instead of Structure.ByReference[] (please post a bug to the project site if Structure.ByReference[] does not work), you will have to manually call Structure.write/read before/after your native function call, since JNA will not know that the pointers reference structures that need to be synched with native memory. I'd bet, however, that the cause of your crashes when using Structure.ByReference[] was simply that JNA was automatically calling Structure.read() after the call and triggered the same error that you see when calling it explicitly.
If you get a segfault on read, it likely means that your structure fields aren't properly aligned or defined, or (less likely) that you have corrupt data that can't be read properly. To diagnose this, set jna.dump_memory=true and print out your struct after calling Structure.write() to see if the contents of the structure appear as you'd expect. It'd also help to post the native and JNA forms of your structure here, if possible.

C++/CLI from tracking reference to (native) reference - wrapping

I need a C# interface to call some native C++ code via the CLI dialect. The C# interface uses the out attribute specifier in front of the required parameters. That translates to a % tracking reference in C++/CLI.
The method I has the following signature and body (it is calling another native method to do the job):
virtual void __clrcall GetMetrics(unsigned int %width, unsigned int %height, unsigned int %colourDepth, int %left, int %top) sealed
{
mRenderWindow->getMetrics(width, height, colourDepth, left, top);
}
Now the code won't compile because of a few compile time errors (all being related to not being able to convert parameter 1 from 'unsigned int' to 'unsigned int &').
As a modest C++ programmer, to me CLI is looking like Dutch to a German speaker. What can be done to make this wrapper work properly in CLI?
Like it was also suggested in a deleted answer, I did the obvious and used local variables to pass the relevant values around:
virtual void __clrcall GetMetrics(unsigned int %width, unsigned int %height, unsigned int %colourDepth, int %left, int %top) sealed
{
unsigned int w = width, h = height, c = colourDepth;
int l = left, t = top;
mRenderWindow->getMetrics(w, h, c, l, t);
width = w; height = h; colourDepth = c; left = l; top = t;
}
It was a bit obvious since the rather intuitive mechanism of tracked references: they're affected by the garbage collector's work and are not really that static/constant as normal &references when they're prone to be put somewhere else in memory. Thus this is the only way reliable enough to overcome the issue. Thanks to the initial answer.
If your parameters use 'out' on the C# side, you need to define your C++/CLI parameters like this: [Out] unsigned int ^%width
Here's an example:
virtual void __clrcall GetMetrics([Out] unsigned int ^%width)
{
width = gcnew UInt32(42);
}
Then on your C# side, you'll get back 42:
ValueType vt;
var res = cppClass.GetMetrics(out vt);
//vt == 42
In order to use the [Out] parameter on the C++/CLI side you'll need to include:
using namespace System::Runtime::InteropServices;
Hope this helps!
You can use pin_ptr so that 'width' doesn't move when native code changes it. The managed side suffers from pin_ptr, but I don't think you can get around that if you want native code directly access it without 'w'.
virtual void __clrcall GetMetrics(unsigned int %width, unsigned int %height, unsigned int %colourDepth, int %left, int %top) sealed
{
pin_ptr<unsigned int> pw = &width; //do the same for height
mRenderWindow->getMetrics(*pw, h, c, l, t);
}

Resources