Skip to content

Interop with C#

Wang Renxin edited this page Mar 11, 2016 · 58 revisions

This is a sample to represent how to interop MY-BASIC with C#. First of all we need to make a C# declaration as:

using System;
using System.Runtime.InteropServices;
using int_t = System.Int32;
using real_t = System.Single;
using bool_t = System.Int32;

namespace script
{
	public static class my_basic
	{
		private const string LIB_NAME = "my_basic";

		private const bool_t TRUE = 1;
		private const bool_t FALSE = 0;

		public const int MB_FUNC_OK = 0;

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		public delegate int mb_func_t(IntPtr s, ref IntPtr l);

		[DllImport(LIB_NAME)]
		public static extern int mb_init();
		[DllImport(LIB_NAME)]
		public static extern int mb_dispose();
		[DllImport(LIB_NAME)]
		public static extern int mb_open(out IntPtr s);
		[DllImport(LIB_NAME)]
		public static extern int mb_close(out IntPtr s);
		[DllImport(LIB_NAME)]
		public static extern int mb_reset(out IntPtr s, bool_t clrf = FALSE);

		[DllImport(LIB_NAME)]
		public static extern int mb_register_func(IntPtr s, string n, [MarshalAs(UnmanagedType.FunctionPtr)]mb_func_t f);

		[DllImport(LIB_NAME)]
		public static extern int mb_attempt_func_begin(IntPtr s, ref IntPtr l);
		[DllImport(LIB_NAME)]
		public static extern int mb_attempt_func_end(IntPtr s, ref IntPtr l);
		[DllImport(LIB_NAME)]
		public static extern int mb_attempt_open_bracket(IntPtr s, ref IntPtr l);
		[DllImport(LIB_NAME)]
		public static extern int mb_attempt_close_bracket(IntPtr s, ref IntPtr l);
		[DllImport(LIB_NAME)]
		public static extern int mb_has_arg(IntPtr s, ref IntPtr l);
		[DllImport(LIB_NAME)]
		public static extern int mb_pop_int(IntPtr s, ref IntPtr l, out int_t val);
		[DllImport(LIB_NAME)]
		public static extern int mb_pop_real(IntPtr s, ref IntPtr l, out real_t val);
		[DllImport(LIB_NAME)]
		public static extern int mb_pop_string(IntPtr s, ref IntPtr l, out string val);
		[DllImport(LIB_NAME)]
		public static extern int mb_pop_usertype(IntPtr s, ref IntPtr l, out IntPtr val);
		[DllImport(LIB_NAME)]
		public static extern int mb_push_int(IntPtr s, ref IntPtr l, int_t val);
		[DllImport(LIB_NAME)]
		public static extern int mb_push_real(IntPtr s, ref IntPtr l, real_t val);
		[DllImport(LIB_NAME)]
		public static extern int mb_push_string(IntPtr s, ref IntPtr l, string val);
		[DllImport(LIB_NAME)]
		public static extern int mb_push_usertype(IntPtr s, ref IntPtr l, IntPtr val);

		[DllImport(LIB_NAME)]
		public static extern int mb_load_string(IntPtr s, string l, bool_t reset = TRUE);
		[DllImport(LIB_NAME)]
		public static extern int mb_load_file(IntPtr s, string f);
		[DllImport(LIB_NAME)]
		public static extern int mb_run(IntPtr s);
	}
}

Note this only a tutorial for the moment, so I didn't expose each API of MY-BASIC.

It's able to use MY-BASIC in C# with the declaration, considering we have a scripting interface for later invoking:

private int foo(System.IntPtr s, ref System.IntPtr l)
{
	int x;
	int y;

	script.my_basic.mb_attempt_open_bracket(s, ref l);

	script.my_basic.mb_pop_int(s, ref l, out x);
	script.my_basic.mb_pop_int(s, ref l, out y);

	script.my_basic.mb_attempt_close_bracket(s, ref l);

	script.my_basic.mb_push_real(s, ref l, (float)x / y);

	return script.my_basic.MB_FUNC_OK;
}

Try test it now:

System.IntPtr bas;
script.my_basic.mb_init();
script.my_basic.mb_open(out bas);
script.my_basic.mb_register_func(bas, "FOO", foo);
script.my_basic.mb_load_string(bas, "print foo(22, 7);", 1);
script.my_basic.mb_run(bas);
script.my_basic.mb_close(out bas);
script.my_basic.mb_dispose();
Clone this wiki locally