将 C 代码编译为 .NET - 第一部分






4.96/5 (75投票s)
借助这款新的 OrangeC/C++ 编译器后端,您可以将 C 代码编译为 .NET。
引言
许多以 C 语言编程并开始学习 C# / VB 或其他 .NET 编译语言的开发者,常常希望甚至需要调用他们用 C 语言编写的函数代码。
每当我上网搜索,或者谈论使用 Visual C++ 和编译器中的 /clr
选项,或使用 pInvoke
来调用 C 函数时。
于是,我和我的朋友 David(OrangeC/C++ 编译器的创建者)聊天时,我给了他一个想法:为他的编译器创建一个新的后端,该后端可以生成 CLR 代码。因此,当时并没有一个开源的 C 语言编译器可以生成 CLR 代码。
在分离了生成 x86 代码的编译器后端后,经过一系列修复和实现,新后端的开发工作得以启动。我们成功地编译了 Sqlite3,并使用 C# 代码编译的 DLL。
关于 OrangeC 至 .NET 的文章
C/C++ 编译器和 MSIL 后端的链接
您可以通过以下链接关注 OrangeC/C++ 编译器的开发或为该项目做出贡献。
- 官方网站: http://ladsoft.tripod.com/index.html
- OrangeC/C++ 编译器和工具链(GitHub): https://github.com/LADSoft/OrangeC
- 用于生成 .Net PE 或转储 IL 代码的库: https://github.com/LADSoft/DotNetPELib
(您可以在这篇文章中阅读更多关于 DotNetPeLib 的信息: https://codeproject.org.cn/Articles/1151739/DotNetPeLib-A-library-to-generate-Net-assemblies-i)
构建 Orangec 编译器和 Orangec for Msil
要构建 Orange C 编译器(MSIL 版本),您需要下载 OrangeC 编译器的完整代码。您可以在以下位置获取源代码:
或
要构建源代码,需要安装以下编译器之一:
- MinGW
- CLang
- Visual C/C++ 15
- OrangeC 编译器
下载并解压所有文件后,打开 CMD
,导航到 C:\orangec\src 文件夹,输入 config.bat。执行完 config.bat 后,输入:
omake fullbuild
这将构建整个 orangec 编译器。
执行完这些命令后,您将得到 occil.exe。
使用 OrangeC 编译器生成 .NET 可执行文件
要使用该编译器,只需在本文提供的链接中下载,将所有 zip 内容解压到 C:\ 文件夹,打开 CMD
并导航到 C:\OrangeC 文件夹。进入 occil 文件夹后,键入 config.bat。运行 config.bat 文件时,将在您的 CMD
上下文中创建一个新的环境变量,以便编译器能够找到 include 文件夹和其他库。
让我们创建一个简单的示例
创建一个名为 "float_to_ieee754.c" 的 C 文件,并将以下代码放入您的 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;
}
附注:需要将以下 DLL 从 bin 文件夹复制到源代码所在的同一文件夹:
- lscrtlil.dll
- lsmsilcrtl.dll
- occmsil.dll
现在,要在 CMD 中构建此代码,请键入:
occil /NProgram.FloatToBinary /9 float_to_ieee754.c
每个参数的解释
/N<NameSpace>.<Class>
/9: C99 模式
执行此命令后,编译器将生成引用您的代码的可执行文件。
要查看生成代码的 IL 代码,只需在命令行中添加 /S
参数即可。
occil /S /NProgram.FloatToBinary /9 float_to_ieee754.c
生成 IL 代码
.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{}
要执行生成的 EXE/DLL,需要有两 (2) 个 DLL 与可执行文件一起。您可以从 C:\occil\bin\ 获取这两个 DLL,只需将 DLL 复制到同一个 EXE 文件夹,然后执行生成的 EXE。
构建简单的 GUI 应用程序
该编译器尚未支持创建复杂的 Windows GUI 程序所需的所有功能,但可以编译简单的程序。对于本示例,我们将创建一个简单的窗口。
注意:目前要编译使用图形界面的代码,我们仍然需要声明我们的 main 函数。
对于这个简单的示例,让我们创建一个名为 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;
}
要构建此源代码,需要告知编译器使用了哪些库,因此,构建源代码的命令行将是:
occil /Lkernel32 /Luser32 /9 window.c
构建完成后,我们就可以执行该应用程序了。:)
从 C 代码创建 DLL 并在 C# 中使用
现在我们知道了如何从 C 代码创建 .NET EXE。接下来,让我们从 C 代码创建 DLL 并在 C# 代码中使用它。
让我们用 C 语言创建一个简单的堆栈,为此,创建一个名为 "stack.c" 的 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;
}
现在,您需要使用 /Wd 选项构建此源代码。这会告诉编译器您要生成一个 DLL。因此,要构建此文件,我们使用此命令行:
occil /ostackdll.dll /Wd /9 /NStackLib.Stack stack.c
现在,让我们创建 C# 项目。对于本文,我创建了一个 .NET 4.0 的 C# 项目。
为此,您可以使用 OCC 生成的 DLL,需要在 C# 项目中设置一些选项:
- 启用不安全代码
- 平台目标:x86
设置好这些选项后,在引用中添加 stack.dll,并编写使用该 DLL 的代码。在这种情况下,我编写了这个简单的示例程序:
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;
}
}
}
构建 EXE 后,不要忘记将 orangec 文件夹的 BIN 文件夹中的两个 DLL 复制过去。
使用 SQLite
现在我们知道了如何从 C 源代码创建和使用 DLL,让我们来使用 SQLite!
您可以在 \samples\sqlite3 文件夹中找到 SQLite 源代码。
要构建 SQLite 源代码,需要使用此命令行:
occil /9 /Wd /Lkernel32 sqlite3.c /Nsqlite3.sqlite
构建 SQLite 后,创建一个 C# 或任何其他您选择的 .NET 项目,添加编译后的 SQLite DLL 的引用,将项目类型设置为 x86,如果需要,启用不安全模式。在本例中,我创建了一个简单的 C# 项目并添加了一个使用 SQLite 的小程序。
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;
}
}
}
该项目位于 SQLite 测试文件夹内。
尚未实现的功能
- 复数未实现。
- 原子操作未实现。
- 线程和线程局部存储未实现。
- 运行时库是 msvcrtl.dll,并且不支持 C11 或 C99 对 CRTL 的添加。
- 数组未实现为托管数组,而是作为指向非托管内存的指针。
- 数组类型实际上实现为 .NET 类。
- 可变参数列表的处理方式类似于 C#,而不是 C(除非在调用非托管函数时)。
- 当可变参数列表传递给非托管代码时会进行封送处理,但这仅限于简单类型。
- 为传递在托管和非托管代码之间的函数指针(例如,用于 qsort 和
WNDPROC
风格的函数)生成了 thunks。但当指针放在结构中时,您需要给编译器一个提示。在函数指针定义中使用CALLBACK
,并将callback
定义为stdcall
函数。 - 在从非托管代码到托管代码的转换(由传递给非托管代码的函数指针使用)的 thunks 中,会进行封送处理,但这仅限于简单类型。
- 可变长度数组和 '
alloca
' 是通过托管内存分配器实现的,而不是通过 'localalloc
' MSIL 函数。 - 通过值传递给函数的结构在调用前会被复制到临时变量。
- 原生编译器中的许多编译器优化目前已关闭。
- 编译器不允许使用无原型函数。
有任何问题吗?
欢迎提问!:)
历史
- 2019 年 12 月 16 日
- 2019 年 9 月 8 日
- 添加了新的 OrangeC 编译器发布版本。
- 2017 年 10 月 12 日
- 添加了文章第二部分的链接。
- 更新了 zip 文件,包含新的可执行文件。
- 2017 年 3 月 9 日
- 添加了关于 DotNetPeLib 的文章链接。
- 2017 年 3 月 8 日
- 更新了 zip 文件,包含新的可执行文件。
- 更新了文章中的 occ 文件夹。
- 添加了项目的官方链接。
- 2017 年 2 月 10 日
- 更新了“尚未实现的功能”部分。
- 2016 年 11 月 5 日
- 更新了可执行文件。
- 添加了 DotNetPeLib 的链接。
- 更改了示例代码,使用
typedef
/struct
的真实名称而不是 "void
*"。 - 删除了关于使用 Borland 构建的说明。
- 移除了对 ILASM 的引用。
- 添加了“尚未实现的功能”部分。
- 2016 年 9 月 22 日
- 添加了“构建简单的 GUI 应用程序”部分。
- 添加了“使用 SQLite”部分。
- 更新了可供下载的可执行文件。
- 2016 年 9 月 13 日
- 文章创建。