Getting a string value from a C function that doesn't _return_ the value

The following C function is passed a handle, a parameter string, and a value string. It returns an integer (0 for success, another number if there’s an error). The value I’m looking for, which corresponds to the parameter I’m sending, seems to be handled as a pointer.

COAX_GRABBER_API int32_t GetGrabberParam(int32_t * handle, char * param, char * value);

The size of the value string is variable - could be a single digit, could be a very long string.

Here’s how I’m calling it now:

var returnValue as int32
var cParam as CString = "DeviceSerialNumber"
var cValue as CString = "XXXXXXXXXXXXX"

returnValue = GetCameraParam(fgHandle, cParam, cValue)

And this is Xojo External Function GetCameraParam:

Private Soft Declare Function GetCameraParam Lib LibCoaxLinkGrabber (byref fgHandle as int32, param as CString, byref cValue as CString) As Integer

But I’m getting back nothing. The DLL’s error logger is showing me the correct text of the parameter I’m calling (“DeviceSerialNumber”), and I know the result is an 11 character long string. The logger says that the size of the result is “11” but the size of “lv string” (what I assume is its internal name for the string I’m passing byref) is only 5.

Doesn’t seem to matter how big I make the string I’m sending it (if I double the number of Xs or if i delete them all and send an empty string, I get the same numbers, 11 and 5, in the logger.

So what’s the best way to retrieve the value string here?

param as CString, byref cValue as CString

why are you calling cValue byRef but not param?

1 Like

The C function returns an integer, not value – what I need is value

I don’t know what value is, this function is supposed to retrieve the value that corresponds to param – if I send the value argument as an empty string, how do I get back what’s put into that string? Byref seemed the most logical way to do it, because it would basically be like a pointer to the string. But I guess not.

typically this is done in one of several ways:

  1. You allocate storage for the string, then pass a pointer to the string. This would not be byRef.
  2. the library allocates storage for the string, and passes you back the pointer. Thus, your pointer would need to be defined as byRef.

Deciding which is correct really depends on the API for the library.

My point is that the C API you showed has this:

char * param, char * value

but your Xojo declare uses this:

param as CString, byref cValue as CString

See the discrepancy?

2 Likes

I don’t. I mean, I see the difference between param and value, but not the discrepancy.

With param, I am sending the function an argument - a string to lookup. So, logically it makes sense to me that I just send it a string straight up. And I can see that this is working, in the DLL’s log output.

With value, I am expecting to get a result of the lookup done by the function somehow. I don’t know how big that result will be so how can I allocate storage for the string? The code for the function shows that internally it is creating a temporary string called tmp, and allocating the size of that string based on the result it gets from looking up the parameter. It then copies the tmp string to value:

			strncpy_s(value, strlen(value) + 1, tmp.c_str(), count);

If I was working in C, then value would be available to me in the code that calls this function, since the function is using a pointer to value.

So how do I get value in Xojo, is my question.

Did you try to use a memoryblock instead of of a string for the char * some?

Something like that?

When I do this I get a compile error:

CoaxLinkFrameGrabber.fg.GetCameraParam Declaration
External functions cannot use objects as parameters
Private Soft Declare Function GetCameraParam Lib LibCoaxLinkGrabber (byref fgHandle as int32, param as CString, cValue as memoryblock) As Integer

Change the cstring to ptr

Or memoryblock instead of ptr
I’m kinda suggesting this without having anything in front of me to test right now.

1 Like

And make sure you use “int32_t” so int32 as the return value in the declare, not Integer

Private Soft Declare Function GetCameraParam Lib LibCoaxLinkGrabber (byref fgHandle as int32, param as ptr, cValue as ptr) As int32

Thank you. That worked. Kind of.

The issue is not at all with param. Sending that as a cstring works, unless that’s a bad idea for some reason. But sending cValue as a memoryblock is getting the result.

The problem is that I don’t know how big the memoryblock needs to be up front. If I allocate 11 characters, for this particular value, it works. If I allocate none, then I get nothing back. I guess I can just pick some arbitrarily large size but that seems inefficient. There are hundreds of parameters I have access to, and the sizes are all over the place.

Is there a way to do this when the size of the returned value is unknown upfront? or should I just brute force it and not care about that?

Yes. thanks for catching that - I’m converting old code I wrote a few years ago that was not correct. Missed that one.

1 Like

Normally the sizes are defined in c mostly or of a known maximum size. Could there be a maximum size defined in a header file?

Otherwise just use something you expect is large enough?

Out of experience i expect a header or docs to have such information.

Does the return value have some meaning?

The code looks like it changes the size as needed in C. See: Getting a string value from a C function that doesn't _return_ the value - #5 by Perry_Paolantonio

If I set the initial value to have three more Xs than I know this particular parameter to contain, it’s returning:

MC151AID002XXX
but it should be
MC151AID002

So I guess I need to pick a character that won’t ever show up, make the memory block bigger, then parse the result to remove the extras from the end?

I basically made the default value for the memoryblock to be 200 ~'s and then I replace them with nothing. Seems a but clunky but it works. I just don’t really know how big the biggest response would be, though most seem to be less than 30 characters.

Why not just allocate a memoryblock with nothing in it. All the bytes will be null.

var cValue as new memoryblock(256)

I still think you should try the suggestion @Mike_D gave you. In your declare, change:

cValue as CString

To

byref cValue as CString

That should handle the variable size thing all on its own.

@Greg_O - I was thinking the opposite, actually, that the problem was the Byref and removing it would solve the issue.

In any case, in C, the * symbol means “pointer to”
so

char* param, char* value

means the library is expecting a pointer to a char for both variables.

But Perry wrote:

param as CString, byref cValue as CString

Which has ByRef on the 2nd parameter but not the first. This is the discrepancy I noticed.

What’s going on:

  • In a Xojo declare, a CString is basically a convenience syntax, meaning “pass the pointer to the string.” It also null-terminates the string to follow the C convention.
  • ByRef means “pointer to”

So if you write in a Xojo Declare:
byref cValue as CString

you are saying “pass a pointer to a pointer to a string”. Normally, in C this would be writen as

 char** value

Which is correct? The fact that when Perry changed it to cValue as Ptr and it worked, suggests the ByRef was incorrect, and the library was expecting a regular pointer.

That should be something the library documentation (or source code?) tells you.

Also, this is funny - when I searched for the library, the only mention I could find was a Xojo forum post from 2021. Guess who wrote it?

And guess what the solution was?

Make sure that the person working on your dylib hasn’t done the same thing, so you’re not passing a pointer to a pointer into the grabber.
See Help calling a function from dylib - #19 by anon20074439 :smile: