ANSI C作为C#项目的核心?这是可能的吗?

4
我正在编写一个非GUI应用程序,希望它能在OS X和Windows之间实现跨平台。我正在考虑以下架构,但不知道它是否适用于Windows端:
(平台特定入口点)-> ANSI C主循环->执行数据处理/逻辑的ANSI C模型代码->(平台特定辅助程序)
所以我计划将核心部分写成常规的ANSI C,因为A)它应该是平台无关的,B)我非常熟悉C,C)它可以胜任工作并且做得很好。
(平台特定入口点)可以根据需要编写任何必要的内容,这是少量代码,对我来说无所谓。
(平台特定辅助程序)是棘手的问题。这些是解析XML、访问数据库、图形工具包等内容。这些在C中并不容易。现代语言/框架会免费提供这些内容。在OS X上,这些代码将使用Objective-C编写,与Cocoa进行交互。在Windows上,我认为最好的选择是使用C#。
因此,在Windows上,我的架构(简化)如下:
(C#或C?)-> ANSI C-> C#
这可行吗?一些想法/建议如下。
1)将我的C核心编译为.dll——这很好,但似乎没有办法调用我的C#辅助程序,除非我能以某种方式获取函数指针并将它们传递给我的核心,但这似乎不太可能。
2)编译一个C .exe和一个C# .exe,并通过共享内存或某种IPC进行通信。我并不完全反对这个方法,但它显然会引入很多复杂性,所以并不理想。
3)使用C++而不是C#,它可以为我提供一些不错的数据管理和辅助代码。我可以很容易地混合它们。我所做的工作可能很容易移植到Linux上。但我真的不喜欢C++,而且我不希望这变成第三方库的盛宴。虽然这并不是什么大问题,但现在已经是2010年了。任何基本的数据管理都应该内置。而且针对Linux真的不是一个优先考虑的目标。
请注意,其他类似问题中SO建议的“完全”替代方案都不可行;Java、RealBasic、Mono等等。这是一个执行软实时游戏/模拟目的的极度性能密集型应用程序,我需要C及其相关技术来正确完成(也许你不需要,但我需要)。
5个回答

6
简短回答:可以。你可以很容易地从.NET访问非托管代码,但是你需要转换数据。
长话短说:不要这么做。你知道mono项目吗?它是.NET的跨平台实现。他们比微软落后了一些,但仍然提供了适用于许多人的解决方案。如果可能的话,我强烈建议保持托管代码,如果你来回切换到C代码,那么你就打败了.NET的目的。这也将有助于将您的项目复杂性降低十倍。
如果您需要使用C ANSI进行低级别访问,我建议您向.NET核心公开一个小且经过充分测试的C ANSI api来执行任何低级别操作。
C# Core <==> Small C ANSI Helper Library

2
我认为OP想让非托管代码调用C#,而不是相反。这可以通过COM完成: ANSI C Core <==> C/C++ COM包装器 <==> C# - Polyfun
是的,我想让非托管代码调用C#,而不是相反。我的C ANSI API实际上是高级别的东西,而“低级别”的部分则在C#中。正如我在OP中所说,我不想使用mono。很想看到有关COM包装技术的详细说明。它如何进行桥接? - Nektarios
我只想指出,在这种情况下,“marshal your data”听起来有点可怕,但从技术上讲是不正确的。例如,对C库的P/Invoke调用并不是对一个单独的应用程序域的调用。它可能比从C++应用程序调用相同的C API的开销还要小。因此,我不确定提问者为什么会提到IPC,因为这是不必要的。在C库和C#前端之间的映射层甚至不需要被隔离,因为它是特定于Windows的。 - kprobst
我提到IPC是因为我在谈论两个完全独立的可执行文件之间的通信。 - Nektarios
似乎没有什么需要使用COM包装器的地方。你可以直接将函数指针(为了方便起见,可以打包到结构体中)传递给C代码。使用COM来完成同样的事情会是一种非常绕弯的方式,因为CCW的虚函数表就是那些结构体,只不过从C语言调用起来不太方便。 - Pavel Minaev

1

我喜欢Aren的答案(看看Mono),但如果你真的想使用C + C#,你可以使用SWIG自动地生成C代码的包装器。它有一个学习曲线,但如果你想从C#调用的C函数数量足够多,那么这个工具是值得一试的。SWIG不支持Objective C,但有一个分支提供了不完全的支持。

更新:哦,你想主要从C中调用C#?抱歉,SWIG并不是为此而设计的。然而,C#确实允许这样做。你可以使用C#或C/C++入口点(C#入口点可能更容易),并将指向C#函数(委托)的指针传递到C代码中。

假设你想从C#传递一个void(string)函数到C。首先,我不知道C代码如何直接获取指向C#函数的指针(这可能是可能的,我只是不知道如何)。相反,我会在C#代码中启动程序,并让C#代码将自己传递给C代码。

像这样:

// Visual C code:
// (.NET functions use the __stdcall calling convention by default.)
typedef void (__stdcall *Callback)(PCWSTR);
void __declspec(dllexport) Foo(Callback c)
{
    c(L"Hello world");
}

// C# code:
// (A delegate declaration can include marshaling commands that
// control how argument types are converted.)
public delegate void Callback([MarshalAs(UnmanagedType.LPWStr)] string message);
void PrintOut(string message) { Console.WriteLine(message); }

这里有一个C函数"Foo",它可以接收指向C#函数"PrintOut"(或者其他C函数)的指针,以及一个回调typedef。我们使用__declspec(dllexport)使得C#代码可以调用它。

在C#端,我们有一个委托声明,它大致相当于C中的typedef,以及一个名为"PrintOut"的函数,我们想要将其传递给C。

假设您将C代码编译成名为Foo.dll的DLL文件,您需要一个C# P/Invoke声明,并编写实际调用Foo的代码:

[DllImport("Foo")]
public static extern void Foo(Callback c);

public void CallFoo()
{
    Foo(PrintOut);
}

上面的代码可能一开始会起作用,但有一个“陷阱”:上面的代码将PrintOut包装在一个委托中,垃圾回收器最终会释放该委托,除非您保留对它的引用。因此,如果您希望C代码对C#方法具有永久引用,我们必须更改上面的C#代码以保留对委托的引用:
[DllImport("Foo")]
public static extern void Foo(Callback c);

static Callback PrintOutRef; // to prevent garbage collection of the delegate

public void CallFoo()
{
    Foo(PrintOutRef = PrintOut);
}

希望这能有所帮助!

一定要更新更多细节 - "..将指向C#函数(委托)的指针传递到C代码中"我想要的是像这样的东西(C#初始化代码)->将指向C#辅助函数foo和bar的指针传递到C代码中。稍后,C代码调用函数foo,并带有参数“char * mydata”,C#代码将字符写入该数组以进行返回。这将是我绝对首选的通信方式,所以请像我说的那样详细说明。 - Nektarios

1
首先,回答一个具体的疑虑:您可以将委托封送为函数指针到本机 C 代码 - 实际上,它 "只是有效的",P / Invoke 将处理所有的包装:
// C#
class ManagedEntryPoint {

    [DllImport("core", CallingConvention=CallingConvention.Cdecl)]
    static extern void NativeEntryPoint(Func<int, int, float> helper);

    static float Helper(int, int) { ... }

    static void Main() {
        NativeEntryPoint(Helper);
    }
}

// C
void NativeEntryPoint(float (*helper)(int, int)) {
    float x = helper(1, 2);
    ...
}

然而,我认为这没有太多意义 - 使用C++/CLI编译器更容易。请注意,这并不意味着您实际上必须使用C ++ - 您可以坚持与C兼容的子集,这是其中的95%(我预计您在实践中唯一需要做的事情就是显式转换malloc的返回)。您将本机C函数编译为本机.lib,然后将其链接到使用/clr编译的可执行文件。 C++/CLI将自行处理所有封送,您无需编写P / Invoke声明等。


1
为什么不全部使用C++呢?你可以覆盖所有平台并获得所有你所说的C语言没有的东西。C#只适用于.NET相关的内容,而C/C++仍然可以在Windows上运行,只需获取你需要的API调用即可完成你想要做的任何事情。

我想我真的要选择C++。但是我想要轻松(内置)访问数据库,轻松(内置)解析XML等等...这些在C++中并不容易实现。我不担心调用WPF或MFC或DX等任何东西,这不是问题。 - Nektarios

0

完全没有理由使用 ANSI C 而不是 C++。此外,如果您有 C++ 代码,编写一个用于在 C# 中使用的 C++/CLI 包装器非常简单(据我所知),而且 C++ 不会比 C 慢两个浮点数相加(许多其他操作实际上更快,例如排序)。除此之外,C++ 在您可以做什么方面非常灵活。

C++ <--> C++/CLI <--> C# 可能是最简单的方法,因为 C++/CLI 互操作可以轻松双向工作。


不,我不会说编写C++/CLI包装器是微不足道的。我放弃使用C++/CLI的部分原因是它很难让C#调用旧的C++代码 - 需要太多的包装工作,使得SWIG更具吸引力。 - Qwertie
它在某种程度上是“琐碎的”,因为它相对简单。但从另一个角度来看,它并不是琐碎的,因为它仍然很繁琐。SWIG 明显更快。 - Pavel Minaev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接