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

Posted

技术标签:

【中文标题】ANSI C 作为 C# 项目的核心?这可能吗?【英文标题】:ANSI C as core of a C# project? Is this possible? 【发布时间】:2010-06-16 15:21:07 【问题描述】:

我正在编写一个非 GUI 应用程序,我想成为 OS X 和 Windows 之间的跨平台。我正在看下面的架构,但不知道它是否可以在windows端工作:

(平台特定入口点)-> ANSI C 主循环 => 执行数据处理/逻辑的 ANSI C 模型代码 =>(平台特定助手)

所以我打算用常规的 ANSI C 编写核心内容,因为 A) 它应该独立于平台,B) 我对 C 非常熟悉,C) 它可以完成这项工作并且做得很好

(平台特定入口点)可以写成完成工作所需的任何内容,这是少量代码,对我来说无所谓。

(平台特定的助手)是很棘手的。这就像解析 XML、访问数据库、图形工具包之类的东西。在 C 中不容易做的事情。现代语言/框架将免费提供的东西。在 OS X 上,此代码将使用与 Cocoa 交互的 Objective-C 编写。在 Windows 上,我认为我最好的选择是使用 C#

所以在 Windows 上我的架构(简化)看起来像

(C# 还是 C?) -> ANSI C -> C#

这可能吗?到目前为止的一些想法/建议..

1) 将我的 C 内核编译为 .dll ——这很好,但似乎没有办法调用我的 C# 助手,除非我能以某种方式获取函数指针并将它们传递给我的内核,但这似乎不太可能

2) 编译一个 C .exe 和一个 C# .exe 并让它们通过共享内存或某种 IPC 进行通信。我并不完全反对这一点,但它显然引入了很多复杂性,因此看起来并不理想

3) 代替 C# 使用 C++,它为我提供了一些不错的数据管理内容和不错的帮助代码。我可以很容易地混合它。我所做的工作可能很容易移植到 Linux。但我真的不喜欢 C++,而且我不希望这变成 3rd-party-library-fest。并不是说这是什么大不了的事,而是 2010 年......应该内置任何用于基本数据管理的东西。针对 Linux 确实不是优先事项。

请注意,正如我在 SO 上看到的其他类似问题中所建议的那样,没有“全部”替代方案是可以的; java、RealBasic、mono.. 这是一个性能极其密集的应用程序,为游戏/模拟目的进行软实时,我需要 C 和朋友来做正确的事情(也许你不这样做,但我这样做)

【问题讨论】:

【参考方案1】:

简短回答:是的。您可以很容易地从 .NET 访问非托管代码,但您必须整理数据。

长答案:不要这样做。你知道mono project吗?它是 .NET 的 x 平台实现。他们有点落后于微软,但他们仍然提供适用于许多人的解决方案。如果可能的话,我强烈建议保留托管代码,如果您正在使用 C 代码,那么您将违背 .NET 的目的。这也将有助于将您的项目复杂性降低十倍。

如果您需要 C ANSI 进行低级访问,我建议您将 small经过良好测试的 C ANSI api 暴露给您的 .NET 核心以执行任何低级访问级别的东西。

C# 核心 小型 C ANSI 帮助程序库

【讨论】:

我认为 OP 希望非托管代码调用 C#,而不是相反。这可以通过 COM 完成:ANSI C Core C/C++ COM wrapper C# 是的,我希望非托管代码调用 C#,而不是相反。我的 C ANSI API 实际上是高级别的东西,C# 中的“低级”正如我在 OP 中所说的,我不想使用单声道。希望看到有关 COM 包装器技术的详细说明。它如何桥接? 我只想指出,“编组你的数据”在这种情况下听起来有点吓人,但这在技术上是不正确的。例如,对 C 库的 P/Invoke 调用不是对单独应用程序域的调用。它可能不会比从 C++ 应用程序调用相同的 C API 更多开销。所以我不确定为什么问这个问题的人提到了 IPC,因为没有必要。 C 库和 C# 前端之间的映射层甚至不需要隔离,因为它是特定于 Windows 的。 我提到 IPC 是因为我说的是 2 个完全独立的可执行文件相互通信 COM 包装器似乎不会为图片带来任何东西。您也可以将函数指针(为方便起见打包到结构中)直接传递给 C 代码。 COM 将是做同样事情的一种非常迂回的方式,CCW 的 vtables 就是那些结构,除了从 C 调用它不太方便。【参考方案2】:

首先,回答一个具体的问题:您可以将委托编组为指向您的本机 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++ 兼容的 C 子集,即其中的 95%(我希望您唯一需要做的事情在实践中不同的是显式转换 malloc) 的返回值。您将本机 C 函数编译为本机 .lib,然后将其链接到使用 /clr 编译的可执行文件。 C++/CLI 会自己处理所有的封送处理,您不需要编写 P/Invoke 声明等。

【讨论】:

【参考方案3】:

你为什么不用 c++ 来做这一切呢?您可以访问所有平台并获得您说 C 没有的所有东西。 C# 仅适用于 dot net 的东西,c/c++ 仍然适用于 Windows,只需获取所需的 api 调用即可。

【讨论】:

我想我将不得不使用 C++。但我想做简单的(内置)数据库访问、简单的(内置)XML 解析等。我没有从 C++ 中得到这些我不关心调用 WPF 或 MFC 或 DX 或其他什么,那不是问题【参考方案4】:

我喜欢 Aren 的回答(看看 Mono),但如果你真的想使用 C + C#,你可以使用 SWIG 自动生成 C 代码的包装器。它有一个学习曲线,但是如果您想从 C# 调用的 C 函数的数量足够多,那么值得付出努力。 SWIG 不支持现成的 Objective C,但有一个分支对它的支持尚未完成。

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

假设您想将 void(string) 函数从 C# 传递给 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 函数)的指针,以及一个 Callback typedef。我们使用 __declspec(dllexport) 以便 C# 代码可以调用它。

在 C# 方面,我们有一个委托声明,大致类似于 C 中的 typedef,以及一个我们想要传递给 C 的函数“PrintOut”。

假设您将 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# C 代码的辅助函数 foo 和 bar。稍后,C 代码调用函数 foo,参数为“char *mydata”,C# 代码将字符写入该数组以返回。这将是我绝对首选的沟通方式,所以请像我说的那样,详细说明【参考方案5】:

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

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

【讨论】:

不,我不会说编写 C++/CLI 包装器是微不足道的。我退出使用 C++/CLI 的部分原因是允许 C# 调用计划旧的 C++ 代码是多么困难 - 涉及太多的包装工作,这使得 SWIG 看起来更有吸引力。 从某种意义上说它是“微不足道的”,它相对简单。从某种意义上说,这并不是微不足道的,它仍然很乏味。 SWIG 肯定更快。

以上是关于ANSI C 作为 C# 项目的核心?这可能吗?的主要内容,如果未能解决你的问题,请参考以下文章

c# .net listview 与分组

c# 将字符串作为代码执行...值得付出努力吗?

C#在在一个解决方案里面有俩个项目,引用时说此项目作为引用将导致循环依赖项,怎么解决呀

C# - 是不是可以从 a 到 z 排列组合框项目?

C# 4.0:我可以使用 TimeSpan 作为具有默认值的可选参数吗?

Windows核心编程之核心总结(第二章 字符和字符串处理)(2018.5.27)