Interop: string value handling

Hi, everyone.

I want to return managed string in my unmanaged code.

Do you have the sample code?

Unmanaged string is simply a zero terminated byte array. Return it as byte array and then convert to string on the managed side.

Do you mean a managed function like below:

[MethodImpl(MethodImplOptions.InternalCall)]
public extern static string GetString();

If so, then:

TinyCLR_Result InteropTest::GetString___STATIC___STRING(const TinyCLR_Interop_MethodData md) {
    auto ip = reinterpret_cast<const TinyCLR_Interop_Provider*>(md.ApiProvider.FindDefault(&md.ApiProvider, TinyCLR_Api_Type::InteropProvider));

    TinyCLR_Interop_ClrValue result;
    TinyCLR_Result ret;

    if ((ret = ip->GetReturn(ip, md.Stack, result)) != TinyCLR_Result::Success) return ret;
    if ((ret = ip->CreateString(ip, "Hello, world!", result)) != TinyCLR_Result::Success) return ret;

    return TinyCLR_Result::Success;
}
2 Likes

Oh thanks, I will need this.

Compiling c++ code with debug build is there any output print to serial or USB function just embedded in tinyclr?

From TinyCLR core, no, there’s currently no way to debug print. Though we can look into adding that. Today you can find an instance of a UART provider and write whatever data you want.

Ok thanks John. If needed I will use UART service provider.

Yes. Thanks. :blush:

Um… Don’t work this snippet. :disappointed_relieved:

Do you have any hint?

image

TinyCLR_Result Interop_Seeed_TinyCLR_WioLTE_Seeed_TinyCLR_WioLTE_WioLTENative::GetString___STATIC___STRING(const TinyCLR_Interop_MethodData md)
{
	auto ip = reinterpret_cast<const TinyCLR_Interop_Provider*>(md.ApiProvider.FindDefault(&md.ApiProvider, TinyCLR_Api_Type::InteropProvider));

	TinyCLR_Interop_ClrValue result;
	TinyCLR_Result ret;

	if ((ret = ip->GetReturn(ip, md.Stack, result)) != TinyCLR_Result::Success) return ret;
	if ((ret = ip->CreateString(ip, "Hello, world!", result)) != TinyCLR_Result::Success) return ret;

	return TinyCLR_Result::Success;
}

I confirm same problem. I can receive string from managed code but I can’t return. I think CreateString fails.

Sorry about that, forgot one function call. The below should work:

TinyCLR_Result Interop_mscorlib_System_Runtime_InteropServices_Marshal::GetString___STATIC___STRING(const TinyCLR_Interop_MethodData md) {
    auto ip = reinterpret_cast<const TinyCLR_Interop_Provider*>(md.ApiProvider.FindDefault(&md.ApiProvider, TinyCLR_Api_Type::InteropProvider));

    TinyCLR_Interop_ClrValue result, str;
    TinyCLR_Result ret;

    if ((ret = ip->GetReturn(ip, md.Stack, result)) != TinyCLR_Result::Success) return ret;
    if ((ret = ip->CreateString(ip, "Hello, world!", str)) != TinyCLR_Result::Success) return ret;
    if ((ret = ip->AssignObjectReference(ip, result, str.Object)) != TinyCLR_Result::Success) return ret;

    return TinyCLR_Result::Success;
}

A bit of detail on interops:

TinyCLR_Interop_ClrObject is the actual object that lives in memory, essentially all of its fields and object id. This includes both reference and value types. Of course this data is opaque so don’t try to interact with it outside of the interop API. It’s an implementation detail subject to change.

TinyCLR_Interop_ClrObjectReference is a pointer to an object in memory. Notably this does not track the type of the pointed to object so it can be freely reassigned to any object. Be careful to only assign valid types though otherwise you’ll get exceptions and corruption in managed code.

TinyCLR_Interop_ClrTypeId is a tag that uniquely represents a given managed type. There’s no useful data you can get from it. You can only look one up by name and use it to create new objects.

TinyCLR_Interop_ClrValue is a helper struct that makes working with objects and object references easier. The various functions that work with this type automatically set the Object and Data fields as appropriate.

So for example, when you call GetArgument, Ref is the actual argument slot in memory the CLR writes to. This can’t be changed since it’s managed by the CLR. It uses this pointer to find what is actually in the argument. Object will be set to whatever Ref points to. If it’s an actual managed object, you can use GetField and others to interact with it. If it’s a string, array, or numeric type, you can use the Data union to actually inspect and change the data. Keep in mind the size of the array and string is readonly. The same principles apply for fields too.

GetReturn sets the slot that the CLR uses to find what return value to use. For functions that return objects, it has no data by default that it points to so when you call it, only Ref is set. Data and Object aren’t valid. You need to find whatever object you want to return from somewhere else and assign it to the return slot using AssignObjectReference (this is why my first example failed, the create functions create a new pointer to point to each object, so the return slot was lost). Numeric is a special case. For functions that return numeric data, Data.Numeric is valid and can be manipulated directly like usual – no assignment is needed.

2 Likes

I see.
It’s fine. Thanks. :slight_smile: