Interop questions

I try to create functions with Interop, but I encounter some difficulties:

  • as RLP entry point is different from mainboard, RLP address base shouldn’t it be place in a variable as pin: Netduino3.RLP = 0x2002F000 and FEZ.RLP=0x20016000 … it could be usefull,

  • will the API functions be documented ?

  • when you say : “look in the map file to find out where the interop definition variable Interop_InteropTest got placed” in, I get for example:

 .rel.iplt      0x000000002001601c        0x0 FEZInterop.obj
                0x000000002001601c                . = ALIGN (0x4)

.data           0x0000000020016027        0x0 load address 0x0000000020016028
 .data          0x0000000020016027        0x0 FEZInterop.obj
 .data          0x0000000020016027        0x0 FEZInterop_FEZInterop_MyNativeClass.obj
                0x0000000020016028        0xc
                0x0000000020016028        0xc FEZInterop.obj
                0x0000000020016028                Interop_FEZInterop
                0x0000000020016034       0x14
                0x0000000020016034       0x14 FEZInterop.obj

.igot.plt       0x0000000020016048        0x0
 .igot.plt      0x0000000020016048        0x0 FEZInterop.obj
                0x0000000020016048                . = ALIGN (0x4)

.bss            0x0000000020016048        0x0
                0x0000000020016048                __bss_start__ = .
 .bss           0x0000000020016048        0x0 FEZInterop.obj
 .bss           0x0000000020016048        0x0 FEZInterop_FEZInterop_MyNativeClass.obj
                0x0000000020016048                __bss_end__ = .
                0x0000000020016048                end = .
OUTPUT(FEZInterop.elf elf32-littlearm)

.comment        0x0000000000000000       0x38
 .comment       0x0000000000000000       0x38 FEZInterop.obj
                                         0x39 (size before relaxing)
 .comment       0x0000000000000038       0x39 FEZInterop_FEZInterop_MyNativeClass.obj

                0x0000000000000000       0x33
                0x0000000000000000       0x33 FEZInterop.obj
                0x0000000000000033       0x33 FEZInterop_FEZInterop_MyNativeClass.obj

How you identify correct line (which beginning with bss_start ? or just the one with class name ?)

The RLI region for each device is just a smaller area in RAM we set aside for RLI use. You don’t have to use it if you don’t want to. You could use more, less, or put interops somewhere else as your project may require. The region we do define should be mentioned on each product page in the docs. For example, see the tip on

The API functions will be documented in more detail as we go along.

It looks like you didn’t name your project InteropTest, but instead FEZInterop. So you’ll want to look for “Interop_FEZInterop”. It’s in the FEZInterop.obj object under “”. Based on your map file it is at address 0x0000000020016028.


Thanks John for any help but I’m again in troubles: I use a netduino 3 board.
So I think beginning for RLP code is: 0x2002F000, it’s what I’ve add to scatterfile.
I use WSL to compile code. my test function is just containing:

#include "InteropTest.h"

TinyCLR_Result Interop_InteropTest_InteropTest_BlinkTest::Blink___VOID(const TinyCLR_Interop_MethodData md) {

    return TinyCLR_Result::Success;

When I compile I get a .bin file which is add as file in Embedded resources, and a map where I can find that function begin with address: 0x2002F074

                0x000000002002f044       0x30
                0x000000002002f044       0x30 InteropTest.obj
                0x000000002002f074        0xc
                0x000000002002f074        0xc InteropTest.obj
                0x000000002002f074                Interop_InteropTest

.igot.plt       0x000000002002f080        0x0
 .igot.plt      0x000000002002f080        0x0 InteropTest.obj
                0x000000002002f080                . = ALIGN (0x4)

Building is ok but when I debug, the last line throw an exception:

           var interop = Resources.GetBytes(Resources.BinaryResources.BlinkTest);
           Marshal.Copy(interop,0,new IntPtr(0x2002F000),interop.Length );
           Interop.Add(new IntPtr(0x2002F074));

Exception details:

    #### Exception System.InvalidOperationException - CLR_E_INVALID_OPERATION (1) ####
    #### Message: 
    #### System.Runtime.InteropServices.Interop::Add [IP: 0000] ####
    #### InteropTest.Program::SetInterop [IP: 0026] ####
    #### InteropTest.Program::Main [IP: 0004] ####

That exception will get thrown if the system cannot find an assembly with the name in the interop or the checksum doesn’t match.

I’d start by doing a clean build of the interops using the latest 0.5.0 extension and firmwares and verifying the Interop_InteropTest struct is up to date.

Next, I’d look at the generated bin file with a hex viewer. Go to offset 0x74 and make sure you can see the struct data there. At 0x74 you should see a char* (the name), at 0x78 you should see the checksum, then at 0x7C should be a void* to the method table. If the checksum matches, go to the char* in the file. You can do this by getting the pointer from 0x74, then subtracting off the base 0x2002F000 from it, and going to that offset in the file. You should see the ASCII data “InteropTest\0” there.

I use 0.5.0 version of TinyClr.
I create a simpler file with just a method : void Blink(void) which return success.
It seems to be an error in checksum (CRC-32 ?)
Others informations seems correct.

Generated file is:

Looks like the name pointer is correct. It’s pointing to offset 0x0E which is “InteropTest\0” as you’d expect.

If the checksum is incorrect, that will prevent loading. Make sure you build the InteropTest project, making sure it’s set to generate bare stubs. Open InteropTest.cpp. At the very bottom you should see the checksum. It should match what you see in the screenshot you posted (reversed, of course) and also match what is in That file is up one directory from the stub folder. Make sure it is also the one that gets deployed. If you open that with the hex editor, make sure the uint32 at file offset 0x14 matches.

Make sure the IntPtr you pass in managed code is 0x2002004C, based on the image above. The map file should confirm that.

The checksum is not the CRC-32 of the interop files themselves. Instead, it’s an accumulated checksum of various attributes of the methods, properties, fields, and others that are present in the assembly.

1 Like

Ok, I restart a new fresh project to avoid conflict.
So checksum is correct in pe, cpp and bin files. (Previously I check in old project, and it was ok. I think not because I was wrongly thinking it was CRC-32).

It is the one who is deployed:

Looking for a device on transport 'USB'.
Found device port 'USB' with ID 'a292a54f-d027-4b90-bfb0-eb6d75e4df73' for transport 'Usb'.
Starting device deployment.
Attempting to connect to device 'USB:Netduino 3': iteration 0.
Opening port '\\?\usb#vid_1b9f&pid_0110#6&1bce8032&1&5#{c13bcfe9-5e84-4187-9baa-45597ffcbb6f}'.
Attaching debugger engine.
Debugger engine attached.
Querying device assemblies.
Found assemblies:
	- InteropTest2 v1.0.0.0.
	- mscorlib v0.5.0.0.
	- GHIElectronics.TinyCLR.Devices v0.5.0.0.
Generating device specific assemblies.
Deploying assemblies:
	- InteropTest2 v1.0.0.0 with size 2 288 bytes at 'D:\Projets\Programmation\Dépôts Git\GroveToTinyCLR\InteropTest2\InteropTest2\bin\Debug\pe\'.
	- mscorlib v0.5.0.0 with size 76 636 bytes at 'D:\Projets\Programmation\Dépôts Git\GroveToTinyCLR\InteropTest2\InteropTest2\bin\Debug\pe\'.
	- GHIElectronics.TinyCLR.Devices v0.5.0.0 with size 39 492 bytes at 'D:\Projets\Programmation\Dépôts Git\GroveToTinyCLR\InteropTest2\InteropTest2\bin\Debug\pe\'.
Total deployment size is 118 416 bytes.
Incrementally deploying assemblies to the device:
All assemblies on the device are up to date. No deployment was necessary.

My mistake was that when .bin file was created, I add it as resource in Project, so .cpp file with method and checksum was changing, so I need to recompile bin, recheck start point.

Now all is OK, I can start to do use of Native API !!! :blush:
Great thanks John !!!

Another question:
with code:

TinyCLR_Result Interop_InteropTest2_InteropTest2_MyClass::Blink___STATIC___I4(const TinyCLR_Interop_MethodData md) {
	auto ip = (const TinyCLR_Interop_Provider*)md.ApiProvider.FindDefault(&md.ApiProvider, TinyCLR_Api_Type::InteropProvider);
	auto gpioProvider = (const TinyCLR_Gpio_Provider*)md.ApiProvider.FindDefault(&md.ApiProvider, TinyCLR_Api_Type::GpioProvider);
	auto timeProvider = (const TinyCLR_Time_Provider*)md.ApiProvider.FindDefault(&md.ApiProvider, TinyCLR_Api_Type::TimeProvider);

	TinyCLR_Interop_ManagedValue ret;
	ret.Type= TinyCLR_Interop_ManagedValueType::I4;
	ip->GetReturn(ip, md.Stack, ret);

	ret.Data.Numeric->I4 = 1;

	if (timeProvider != nullptr)
		timeProvider->DelayNoInterrupt(timeProvider, 1000000);
		ret.Data.Numeric->I4 |= 2;

	if (gpioProvider != nullptr) {
		gpioProvider->AcquirePin(gpioProvider, 45);
		gpioProvider->Write(gpioProvider, 45, TinyCLR_Gpio_PinValue::High);
		gpioProvider->ReleasePin(gpioProvider, 45);
		ret.Data.Numeric->I4 |= 4;
		timeProvider->DelayNoInterrupt(timeProvider, 1000000);
		ret.Data.Numeric->I4 |= 8;
	if (gpioProvider != nullptr) {
		gpioProvider->AcquirePin(gpioProvider, 45);
		gpioProvider->Write(gpioProvider, 45, TinyCLR_Gpio_PinValue::Low);
		gpioProvider->ReleasePin(gpioProvider, 45);
		ret.Data.Numeric->I4 |= 16;

    return TinyCLR_Result::Success;

Return value (with ret, not function return value) is 0b10101, indicating timeProvider is always nullptr.
Is that normal ? (I don’t have understand Something with this API) or is it a bug ?
Pin 45 which correspond to power_led on netduino is on with first gpioProvider->Write function call, but never off although second Write call is passed with Low param).
Again is it a bug ? or is due to fact that the two call of write function are too fast ?

Glad you got it working.

As for TimeProvider, I believe it is because there is no time provider registered as the default. The only defaults the core itself registers are memory, task, api, and interop (also the only APIs it registers). TimeProvider is provided by the device port and the port currently doesn’t set anything as default for time. You can confirm this with a call to GetDefaultSelector for TimeProvider, it should return nullptr as well. “GHIElectronics.TinyCLR.NativeApis.STM32F4.TimeProvider” is the name of the time provider registered for the STM32F4 targets, so you can call FindByName to get it.

An easy way to set all the providers registered with the system is to use the managed API under System.Runtime.InteropServices.Api.FindAll.

As for the gpio, I believe it’s because you’re not setting the drive mode. Just like in managed code, you need to set the drive mode to output since it defaults to input.

First call doesn’t set ouput mode but it works. That said, I was thinking same thing, and try to set to output on second call, but nothing happened.

ReleasePin sets it back to the default state which is input. Try acquiring the pin only once and not releasing it, at least to test.

Already test without success.

Next I would test pin 45 in pure managed code with no interop loaded. See if it can be controlled successfully.

1 Like

I’ll give it a try next week.

Try in managed code: Ok.

I’ve retried with just two functions in native code: TurnOn and TurnOff functions, it works too ! I don’t have placed any AcquirePin nor ReleasePin as I open pin in managed code, it explains perhaps why it works.

Next challenge is to retrieve arguments of function … Perhaps with brainpad ? :grinning:

1 Like

@John_Brochue when you said “you can call FindByName” is from native code ? I can’t find this command in TinyClr.h.

To return a value from a method

TinyCLR_Result Interop_TestInterop_TestInterop_LedInterop::GetResult___I4(const TinyCLR_Interop_MethodData md) {
	auto ip = (const TinyCLR_Interop_Provider*)md.ApiProvider.FindDefault(&md.ApiProvider, TinyCLR_Api_Type::InteropProvider);

	TinyCLR_Interop_ManagedValue self;
	TinyCLR_Interop_ManagedValue ret;

	ret.Type = TinyCLR_Interop_ManagedValueType::I4;
	ip->GetThisObject(ip, md.Stack, self);
	ip->GetReturn(ip, md.Stack, ret);
        // Return a value, 22 here
	ret.Data.Numeric->I4 = 22;

    return TinyCLR_Result::Success;

To retrieve an argument of a method

TinyCLR_Result Interop_TestInterop_TestInterop_LedInterop::GetResultFromParam___I4__I4(const TinyCLR_Interop_MethodData md) {

	auto ip = (const TinyCLR_Interop_Provider*)md.ApiProvider.FindDefault(&md.ApiProvider, TinyCLR_Api_Type::InteropProvider);

	TinyCLR_Interop_ManagedValue self;
	TinyCLR_Interop_ManagedValue argument;
	TinyCLR_Interop_ManagedValue ret;

	ret.Type = TinyCLR_Interop_ManagedValueType::I4;
	ip->GetThisObject(ip, md.Stack, self);
	ip->GetReturn(ip, md.Stack, ret);

        // Argument 1 is an index of the argument to retrieve, 
        // For a static method, first argument is at index 0, 
        // For an instance method, first argument is at index 1 as index 0 correspond to the this pointer
	ip->GetArgument(ip, md.Stack, 1, argument);
        // Return value is here value of argument +1
	ret.Data.Numeric->I4 = argument.Data.Numeric->I4+1;

	return TinyCLR_Result::Success;

Sorry about that, it’s just TinyCLR_Api_Provider::Find

Don’t worry ! I know you are on hard work with 0.6.0, and we all learn from errors which raise question !
I hope I can post soon a “To use time Interop functions” :slight_smile:

1 Like