Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Objective-C

Compiling Your C Code to .NET - Part 1

4.96/5 (77 votes)
17 Dec 2019CPOL7 min read 152.3K   1.7K  
With this new OrangeC/C++ compiler back-end, you can compile your C code to .NET

Introduction

Often, for those who program in C and are starting to program in C # / VB or some other compiled language for .NET, they want or even need to call function codes in C we write.

And whenever I researched on the Internet, or speak to use Visual C++ with the /clr in the compiler, or use pInvoke to call the C function.

So talking to my friend David, creator of OrangeC/C++ compiler, I gave him the idea to create a new back-end for your compiler, which generates CLR code, therefore, there was no C open-source compiler which generated CLR code.

After separating the compiler back-end that generates x86 code, the development of the new back-end was started after a number of fixes and implementations, we have succeeded to the compiler Sqlite3 and use the DLL compiled with a C# code.

Articles about OrangeC to .NET

Links of the C/C++ Compiler and the Back-end to MSIL

You can follow the development of the OrangeC/C++ compiler or make contributions in the project on the following links:

Building the Orangec Compiler and the Orangec for Msil

To build the Orange C compiler for MSIL, you need to download the complete code of OrangeC compiler, you can get the source in:

or:

To build the source, it is necessary to have one of these compilers:

  • MinGW
  • CLang
  • Visual C/C++ 15
  • OrangeC compiler

After the download and the extraction of all files, open the CMD navigate into folder C:\orangec\src, type, config.bat, after the execution of config.bat, type:

  • omake fullbuild

This will build all the orangec compiler.

After the execution of these commands, you will have the occil.exe.

Using the OrangeC Compiler to .NET Executables

To use the compiler, you simply download it at the link provided in this article, extract all the zip content in C:\ folder, open the CMD and navigate in to C:\OrangeC folder, after you are in the occil folder, type, config.bat, when running config.bat file, a new environment variable will be created in your CMD context, so the compiler can locate the include folder and other libs.

Let's Create A Small Example

Create a C file with name, "float_to_ieee754.c" and put this code in your C file:

C++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *strrev_t(char *str)
{
    char *p1, *p2;

    if (!str || !*str)
        return str;
    for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)
    {
        *p1 ^= *p2;
        *p2 ^= *p1;
        *p1 ^= *p2;
    }
    return str;
}

void floatToBinary(float f, char *str, int numeroDeBits)
{
    int i = 0;
    int strIndex = 0;
    union
    {
        float f;
        unsigned int i;
    }u;
    u.f = f;
    memset(str, '0', numeroDeBits);

    for (i = 0; i < numeroDeBits; i++)
    {
        str[strIndex++] = (u.i & (1 << 0)) ? '1' : '0';
        u.i >>= 1;
    }

      str[strIndex] = '\0';
 
    str = strrev_t(str);
}

int main()
{
    float input = 0.0;
    const int numeroDeBits = 32;
    char *str = (char*)malloc(sizeof(char) * numeroDeBits);
    printf("Type a float number to convert to binary: ");
    scanf("%f", &input);
    floatToBinary(input, str, numeroDeBits);
    printf("%s\n", str);
    if(str != NULL)
    free(str);     
    return 0;
}

P.S.: It's necessary to copy the following DLLs from the bin folder to the same folder where the source code is:

  • lscrtlil.dll
  • lsmsilcrtl.dll
  • occmsil.dll

Now, to build this code, you type in CMD:

occil /NProgram.FloatToBinary /9 float_to_ieee754.c

Explanation of each argument:

  • /N<NameSpace>.<Class>
  • /9: C99 mode

After executing this command, the compiler will generate the executable referring to your code.

To see get the IL code that refers to generated code, you just need to add the parameter /S in command line:

occil /S /NProgram.FloatToBinary /9 float_to_ieee754.c

Generate IL code:

MSIL
.corflags 3

.assembly float_to_ieee754{
}
.assembly extern mscorlib{
}
.assembly extern lsmsilcrtl{
    .ver 1:0:0:0
    .publickeytoken = (bc 9b 11 12 35 64 2d 7d )
}

.field public static uint16 * '_pctype'
.field public static void * '__stdin'
.field public static void * '__stdout'
.field public static void * '__stderr'
.namespace 'Program' {
.class public ansi sealed 'FloatToBinary' 
{.field private static valuetype 'Program.FloatToBinaryøint8[]' 'L_1' at $L_1
.data $L_1 = bytearray (
44 69 67 69 74 65 20 75
    6d 20 6e 75 6d 65 72 6f
    20 66 6c 6f 61 74 3a 20
    00 )
.field private static valuetype 'Program.FloatToBinaryøint8[]' 'L_2' at $L_2
.data $L_2 = bytearray (
25 66 00 )
.field private static valuetype 'Program.FloatToBinaryøint8[]' 'L_3' at $L_3
.data $L_3 = bytearray (
25 73 0a 00 )
.method public static hidebysig int32 'main'(int32'argc', void *'argv') cil managed{
    .locals (
        [0]    int8 * 'str/0',
        [1]    float32 'input/1'
    )
    .maxstack 3

// Line 43: int main()

L_4:
// Line 45:     float input = 0.0;

    ldloca.s    'input/1'
    ldc.r4    0
    stind.r4
// Line 46:     const int numeroDeBits = 32;

    ldloca.s    'str/0'
    ldc.i4.s    32
    conv.u4
    call    void * [lsmsilcrtl]lsmsilcrtl.rtl::malloc(uint32)
    stind.i4
// Line 48:     printf("Digite um numero float: ");

    ldsflda    valuetype 'Program.FloatToBinaryøint8[]' Program.FloatToBinary::'L_1'
    call    vararg int32 'printf'(void *, ...)
    pop
// Line 49:     scanf("%f", &input);

    ldsflda    valuetype 'Program.FloatToBinaryøint8[]' Program.FloatToBinary::'L_2'
    ldloca.s    'input/1'
    call    vararg int32 'scanf'(void *, ..., void *)
    pop
// Line 50:     floatToBinary(input, str, numeroDeBits);

    ldloc.1    
    ldloc.0    
    ldc.i4.s    32
    call    void Program.FloatToBinary::'floatToBinary'(float32, int8 *, int32)
// Line 51:     printf("%s\n", str);

    ldsflda    valuetype 'Program.FloatToBinaryøint8[]' Program.FloatToBinary::'L_3'
    ldloc.0    
    call    vararg int32 'printf'(void *, ..., void *)
    pop
// Line 52:     if(str != NULL)

    ldloc.0    
    brfalse.s    L_7
// Line 53:     free(str);     

    ldloc.0    
    call    void [lsmsilcrtl]lsmsilcrtl.rtl::free(void *)
L_7:
// Line 54:     return 0;

    ldc.i4.0    
// Line 55: }

L_5:
    ret
}
.method public static hidebysig int8 * 'strrev_t'(int8 *'str') cil managed{
    .locals (
        [0]    int8 * 'p1/0',
        [1]    int8 * 'p2/1'
    )
    .maxstack 3

// Line 5: char *strrev_t(char *str)

L_17:
// Line 7:     char *p1, *p2;

// Line 9:     if (!str || !*str)

    ldarg.0    
    brfalse.s    L_37
    ldarg.0    
    ldind.i1
    brtrue.s    L_20
L_37:
// Line 10:         return str;

    ldarg.0    
    br.s    L_18
L_20:
    ldarg.0    
    stloc.0    
    ldarg.0    
    call    uint32 'strlen'(void *)
    ldarg.0    
    add
    ldc.i4.1    
    sub
    stloc.1    
    br.s    L_26
L_25:
// Line 12:     {

// Line 13:         *p1 ^= *p2;

    ldloc.0    
    ldloc.0    
    ldind.i1
    ldloc.1    
    ldind.i1
    xor
    stind.i1
// Line 14:         *p2 ^= *p1;

    ldloc.1    
    ldloc.1    
    ldind.i1
    ldloc.0    
    ldind.i1
    xor
    stind.i1
// Line 15:         *p1 ^= *p2;

    ldloc.0    
    ldloc.0    
    ldind.i1
    ldloc.1    
    ldind.i1
    xor
    stind.i1
L_28:
    ldloc.0    
    ldc.i4.1    
    add
    stloc.0    
    ldloc.1    
    ldc.i4.1    
    sub
    stloc.1    
// Line 11:     for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)

L_26:
    ldloc.1    
    ldloc.0    
    bgt.s    L_25
L_27:
// Line 16:     }

    ldarg.0    
// Line 18: }

L_18:
    ret
}
.method public static hidebysig void 'floatToBinary'
(float32'f', int8 *'str', int32'numeroDeBits') cil managed{
    .locals (
        [0]    int32 'i/0',
        [1]    int32 'strIndex/1',
        [2]    valuetype 'Program.FloatToBinaryø__anontype_2486130647_0' 'u/2'
    )
    .maxstack 3

// Line 20: void floatToBinary(float f, char *str, int numeroDeBits)

L_41:
// Line 22:     int i = 0;

    ldloca.s    'i/0'
    ldc.i4.0    
    stind.i4
// Line 23:     int strIndex = 0;

    ldloca.s    'strIndex/1'
    ldc.i4.0    
    stind.i4
// Line 29:     u.f = f;

    ldloca.s    'u/2'
    ldc.i4.0    
    add
    ldarg.0    
    stind.r4
// Line 30:     memset(str, '0', numeroDeBits);

    ldarg.1    
    ldc.i4.s    48
    ldarg.2    
    conv.u4
    call    void * 'memset'(void *, int32, uint32)
    pop
// Line 31:

    ldc.i4.0    
    stloc.0    
    br.s    L_45
L_44:
// Line 33:     {

// Line 34:         str[strIndex++] = (u.i & (1 << 0)) ? '1' : '0';

    ldloc.1    
    ldarg.1    
    add
    ldloc.1    
    ldc.i4.1    
    add
    stloc.1    
    ldloca.s    'u/2'
    ldc.i4.0    
    add
    ldind.u4
    ldc.i4.1    
    and
    brfalse.s    L_56
    ldc.i4.s    49
    br.s    L_57
L_56:
    ldc.i4.s    48
L_57:
    conv.i1
    stind.i1
// Line 35:         u.i >>= 1;

    ldloca.s    'u/2'
    ldc.i4.0    
    add
    ldloca.s    'u/2'
    ldc.i4.0    
    add
    ldind.u4
    ldc.i4.1    
    shr.un
    stind.i4
L_47:
    ldloc.0    
    ldc.i4.1    
    add
    stloc.0    
// Line 32:     for (i = 0; i < numeroDeBits; i++)

L_45:
    ldloc.0    
    ldarg.2    
    blt.s    L_44
L_46:
// Line 36:     }

    ldloc.1    
    ldarg.1    
    add
    ldc.i4.0    
    stind.i1
// Line 39:   

    ldarg.1    
    call    int8 * Program.FloatToBinary::'strrev_t'(int8 *)
    starg.s    'str'
// Line 41: }

L_42:
    ret
}
.method private static hidebysig void '$Main'() cil managed{
    .locals (
        [0]    int32 'argc/0',
        [1]    void * 'argv/1',
        [2]    void * 'environ/2',
        [3]    void * 'newmode/3'
    )
    .entrypoint
    .maxstack 5

    call    uint16 * '__pctype_func'()
    stsfld    uint16 * '_pctype'
    call    void * '__iob_func'()
    dup
    stsfld    void * '__stdin'
    dup
    ldc.i4.s    32
    add
    stsfld    void * '__stdout'
    ldc.i4.s    64
    add
    stsfld    void * '__stderr'
    ldloca.s    'argc/0'
    ldloca.s    'argv/1'
    ldloca.s    'environ/2'
    ldc.i4.0    
    ldloca.s    'newmode/3'
    call    void '__getmainargs'(void *, void *, void *, int32, void *)
    ldloc.0    
    ldloc.1    
    call    int32 Program.FloatToBinary::'main'(int32, void *)
    call    void 'exit'(int32)
    ret
}
.method private static hidebysig int32 * '__GetErrno'() cil managed{
    .maxstack 1

    call    int32 * '_errno'()
    ret
}
.class nested public ansi sealed value sequential '__file2' {
 .pack 4 .size 16
}
.class nested public ansi sealed value sequential '__file__' {
 .pack 4 .size 32
.field public int16 'token'
.field public uint16 'flags'
.field public uint8 'hold'
.field public int32 'fd'
.field public int32 'level'
.field public int32 'bsize'
.field public uint8 * 'buffer'
.field public uint8 * 'curp'
.field public valuetype 'Program.FloatToBinaryø__file2' * 'extended'
}
.class nested public ansi sealed value sequential '__anontype_511211642_1' {
 .pack 1 .size 8
.field public int32 'quot'
.field public int32 'rem'
}
.class nested public ansi sealed value sequential '__anontype_511211642_2' {
 .pack 1 .size 8
.field public int32 'quot'
.field public int32 'rem'
}
.class nested public ansi sealed value sequential '__anontype_511211642_3' {
 .pack 1 .size 16
.field public int64 'quot'
.field public int64 'rem'
}
.class nested public ansi sealed value sequential '__anontype_2486130647_0' {
 .pack 4 .size 4
}
.class nested private explicit ansi sealed value 'int8[]' {
 .pack 1 .size 1
}
}
}
.method public static hidebysig pinvokeimpl("msvcrt.dll" cdecl) 
vararg int32 'printf'(void *) preservesig{}
.method public static hidebysig pinvokeimpl("msvcrt.dll" cdecl) 
vararg int32 'scanf'(void *) preservesig{}
.method public static hidebysig pinvokeimpl("msvcrt.dll" cdecl) 
uint32 'strlen'(void *) preservesig{}
.method public static hidebysig pinvokeimpl("msvcrt.dll" cdecl) 
void * 'memset'(void *, int32, uint32) preservesig{}
.method public static hidebysig pinvokeimpl("msvcrt.dll" stdcall) 
uint16 * '__pctype_func'() preservesig{}
.method public static hidebysig pinvokeimpl("msvcrt.dll" stdcall) 
void * '__iob_func'() preservesig{}
.method public static hidebysig pinvokeimpl("msvcrt.dll" stdcall) 
void '__getmainargs'(void *, void *, void *, int32, void *) preservesig{}
.method public static hidebysig pinvokeimpl("msvcrt.dll" stdcall) 
void 'exit'(int32) preservesig{}
.method public static hidebysig pinvokeimpl("msvcrt.dll" stdcall) 
int32 * '_errno'() preservesig{}

To execute the generated EXE/DLL, it is necessary to have two DLLs with the executable, you can get these two DLLs in C:\occil\bin\, you just copy the DLLs to the same EXE folder, and execute the generated EXE.

Image 1

Building a Simple GUI Application

The compiler does not yet support everything that is necessary to create a complex program with GUI for Windows, but can compile simple programs, for this example, let's create a simple Window.

*NOTE*: To compile the code to use graphical interface, at the moment we still need to declare our main.

For this simple sample, let's create a C file called window.c.

C++
#include <windows.h>
#include <stdio.h>

const char g_szClassName[] = "WindowClass";

void createButtons(HWND hwnd)
{
    CreateWindow("button", "Beep",
        WS_VISIBLE | WS_CHILD,
        20, 50, 80, 25,
        hwnd, (HMENU)1, NULL, NULL);

    CreateWindow("button", "Quit",
        WS_VISIBLE | WS_CHILD,
        120, 50, 80, 25,
        hwnd, (HMENU)2, NULL, NULL);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
        createButtons(hwnd);
        break;
    case WM_COMMAND:
    {
        if (LOWORD(wParam) == 1)
            Beep(40, 50);

        if (LOWORD(wParam) == 2) {
            MessageBox(hwnd, "Goodbye, cruel world!", "Note", MB_OK);
            PostQuitMessage(0);
        }

        break;
    }
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = g_szClassName;
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, "Window Registration Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    hwnd = CreateWindowExA(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        "Test window in .Net!! :)",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 230, 150,
        NULL, NULL, hInstance, NULL);

    if (hwnd == NULL)
    {
        MessageBox(NULL, "Window Creation Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

int main(int argc, char* argv[])
{
    STARTUPINFOA si;
    GetStartupInfoA(&si);
    int ret = WinMain(GetModuleHandleA(NULL), NULL, "", (si.dwFlags & 1) ? si.wShowWindow : 10);
    return ret;
}

To build this source, it is necessary to inform the compiler what libs are used in the source, so, the command line to build the source will be:

occil /Lkernel32 /Luser32 /9 window.c

After build, we can execute the application. :)

Image 2

Creating and Using a DLL From Your C Code in C#

Now we know how to create a .NET EXE from your C code. Now, let's create a DLL from your C code and use it with your C# code.

Let's create a simple stack in C, for this, create a C file with name, "stack.c" and insert this code in the file:

C++
#include <stdio.h>
#include <stdlib.h>

typedef struct _stack_
{
    int size;
    int totalitems;
    int* stack;
} stack;

stack* pl_initastack(int size);
void pl_push(stack* pl, int elemento, int* success);
int pl_pop(stack* pl, int* success);
int pl_top(stack *pl, int* success);
int pl_base(stack* pl, int *success);
int pl_stackfull(stack* pl);
int pl_stackempty(stack* pl);
void pl_freestack(stack *pl);
void pl_cleanstack(stack *pl);

stack* pl_initastack(int size)
{
    stack* pl = (stack*)malloc(sizeof(stack));
    pl->stack = (int*)malloc(sizeof(int) * size);
    pl->size = size;
    pl->totalitems = 0;
    return pl;
}

void pl_push(stack* pl, int elemento, int* success)
{
    if (!pl_stackfull(pl))
        pl->stack[pl->totalitems++] = elemento;
    else
        *success = 0;
}

int pl_pop(stack* pl, int* success)
{
    if (!pl_stackempty(pl))
    {
        *success = 1;
        return pl->stack[--pl->totalitems];
    }
    else
    {
        *success = 0;
        return -1;
    }
}

int pl_top(stack *pl, int* success)
{
    if (pl_stackempty(pl))
    {
        *success = 0;
        return -1;
    }
    else
    {
        *success = 1;
        return pl->stack[pl->totalitems - 1];
    }
}

int pl_base(stack* pl, int *success)
{
    if (pl_stackempty(pl))
    {
        *success = 0;
        return -1;
    }
    else
    {
        *success = 1;
        return pl->stack[0];
    }
}

int pl_stackfull(stack* pl)
{
    return pl->totalitems >= pl->size;
}

int pl_stackempty(stack* pl)
{
    return pl->totalitems == 0;
}

void pl_freestack(stack* pl)
{
    free(pl->stack);
    free(pl);
}

void pl_cleanstack(stack *pl)
{
    pl->stack = malloc(sizeof(int) * pl->size);
    pl->totalitems = 0;
}

Now, you need to build this source with option /Wd. This tells the compiler that you want to generate a DLL, so to build this file, we use this command line:

occil /ostackdll.dll /Wd /9 /NStackLib.Stack stack.c

Now, let's create the C# project. For this article, I created a C# project for .NET 4.0.

For that, you can use the DLL generated by OCC, you need to set some settings in C# project:

  • Enable Unsafe code
  • Platform target: x86

After you set these options, add the stack.dll in references and write the code to use the DLL, in this case, I wrote this simple sample program:

C#
using System;
using StackLib;

namespace Stack
{
    unsafe class Program
    {
        static void Main(string[] args)
        {
            StackLib.Stack._stack_* stk = null;
            stk = StackLib.Stack.pl_initastack(5);

            int success = 1;
            Console.WriteLine("Pushing values to stack...");
            for(int i=0; (success == 1); i++)
            {
                int val = i * 10;
                StackLib.Stack.pl_push(stk, val, &success);
            }

            Console.WriteLine("Base value in stack: {0}", StackLib.Stack.pl_base(stk, &success));
            Console.WriteLine("Top value in stack.: {0}", StackLib.Stack.pl_top(stk, &success));

            Console.WriteLine("Poping values from stack");
            while(true)
            {
                int val = StackLib.Stack.pl_pop(stk, &success);
                if (success == 0)
                    break;
                Console.WriteLine("{0}", val);
            }

            StackLib.Stack.pl_freestack(stk);
            stk = null;
        }
    }
}

After you build the EXE, do not forget to copy the two DLLs that are in the BIN folder of orangec folder.

Image 3

Using the SQLite

Now that we know how to create and use a DLL from the source code in C, let's use the SQLite!

You can find the source SQLite within the folder, \samples\sqlite3.

To build the SQLite source, it's necessary to use this command line:

occil /9 /Wd /Lkernel32 sqlite3.c /Nsqlite3.sqlite

After build the SQLite, create a C# or any other .NET project of your choice, add the reference of compiled DLL of the SQlite in your project, set the project type to x86 and if necessary, enable the unsafe mode, in my case, I created a simple C# project and added a small program to use the SQLite:

C#
using System;
using System.IO;

using sqlite3;
using lsmsilcrtl;

namespace sqliteil
{
    unsafe class Program
    {
        static string[] Names { get; } = new string[]
        {
            "Bob",
            "Tom",
            "Carlos",
            "Marcos",
            "Alexandre",
            "Alex",
            "Morgana",
            "Maria",
            "Jose",
            "Joao",
            "Marcos",
            "Gustavo",
            "Roberto",
            "Rodrigo",
            "Teste"
        };

        static int Main(string[] args)
        {
            String dbName = "dbtest.db";

            if (File.Exists(dbName))
                File.Delete(dbName);

            sqlite.sqlite3* db;
            // Create the database
            int rc = sqlite.sqlite3_open(CString.ToPointer(dbName), &db);
            if (rc != 0)
            {
                Console.WriteLine("Fail to create the database :(");
                return -1;
            }

            // Create the table
            sqlite.sqlite3_stmt* stmt;
            sqlite3.sqlite.sqlite3_prepare_v2(db, CString.ToPointer
            ("CREATE TABLE demo (name TEXT, age INTEGER);"), -1, &stmt, null);
            rc = sqlite.sqlite3_step(stmt);
            if (rc != 101)
            {
                Console.WriteLine("Fail to create the table :(");
                return -1;
            }
            sqlite.sqlite3_finalize(stmt);

            // Insert some data in table
            foreach (var name in Names)
            {
                var insertLine = String.Format("insert into demo (name, age) 
                values ('{0}', {1});", name, new Random().Next(1, 99));
                var query = CString.ToPointer(insertLine);
                sqlite.sqlite3_prepare_v2(db, query, insertLine.Length, &stmt, null);
                rc = sqlite.sqlite3_step(stmt);
                if (rc != 101)
                {
                    Console.WriteLine("Fail to insert the name: {0}", name);
                }
                sqlite.sqlite3_finalize(stmt);
            }

            // Read the inserted data...
            var select = "SELECT * FROM demo;";
            rc = sqlite.sqlite3_prepare_v2(db, CString.ToPointer(select), select.Length, &stmt, null);
            if(rc == 0)
            {
                bool done = false;
                while(!done)
                {
                    switch(rc = sqlite.sqlite3_step(stmt))
                    {
                        case 5:
                        case 101:
                            done = true;
                            break;
                        case 100:
                            {
                                string name = 
                                   new CString(sqlite.sqlite3_column_text(stmt, 0)).ToString();
                                int age = sqlite.sqlite3_column_int(stmt, 1);
                                Console.WriteLine("Name: {0} -- Age: {1}", name, age);
                                rc = 0;
                            }
                            break;
                        default:
                            done = true;
                            break;
                    }
                }
            }

            sqlite.sqlite3_close(db);
            return 0;
        }
    }
}

This project is inside the SQLite test folder.

What is Not Implemented Yet

  1. Complex numbers aren't implemented
  2. Atomics aren't implemented
  3. thread and thread local storage aren't implemented
  4. runtime library is msvcrtl.dll, and doesn't support C11 or C99 additions to the CRTL
  5. arrays aren't implemented as managed arrays but as pointers to unmanaged memory
  6. array types are actually implemented as .NET classes
  7. variable length argument lists are done in the C# style rather than in the C style - except during calls to unmanaged functions
  8. variable length argument lists get marshalling performed when being passed to unmanaged code, but this only handles simple types
  9. thunks are generated for pointers-to-functions passed between managed and unmanaged code (e.g. for qsort and for WNDPROC style functions) but when the pointers are placed in a structure, you need to give the compiler a hint. Use CALLBACK in the function pointer definition and make the callback a stdcall function
  10. in the thunks for the transition from unmanaged to managed code used by function pointers passed to unmanaged code marshalling is performed, but this only handles simple types
  11. variable length arrays and 'alloca' are implemented with a managed memory allocator instead of with the 'localalloc' MSIL function
  12. structures passed by value to functions get copied to temporary variables before the call
  13. many compiler optimizations found in the native version of the compiler are currently turned off
  14. The compiler will not allow use of unprototyped functions

Any Questions?

Feel free to ask! :)

History

  • 16-Dez-2019
    •  
  • 08-Set-2019
    • Added new OrangeC compiler Release
  • 12-Oct-2017
    • Added link to part 2 of article
    • Updated the zip with the new executables
  • 09-Mar-2017
    • Added link to article about DotNetPeLib
  • 08-Mar-2017
    • Updated the zip file with new executables
    • Updated occ folder in article
    • Added the official link to the project
  • 10-Feb-2017
    • Updated in what is not implemented yet
  • 05-Nov-2016
    • Updated executables
    • Added link to DotNetPeLib
    • Changes in the examples code to use typedef/struct real name instead of "void*"
    • Removed the explanation about build with Borland
    • Removed references to ILASM
    • Added what is not implemented yet
  • 22-Sep-2016
    • Added section: Building a simple GUI application
    • Added section: Using the SQLite
    • Updated the executables files to download
  • 13-Sep-2016
    • Article created

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)