使用Mono打造轻量级的.NET程序运行时

??在使用Mono让.NET程序跨平台运行这篇文章中,我们已经对Mono以及.NET程序的运行机制有了初步的理解。今天我想来谈谈”使用Mono打造轻量级的.NET运行时”这样一个话题。为什么我会有这样一种想法呢?因为Mono和.NET都可以执行IL代码,所以我用Mono来作为.NET程序的运行时是一个顺理成章的想法。由于.NET程序需要.NET Framework提供运行支持,所以当目标设备没有安装.NET Framework或者.NET Framework版本不对的时候,我们的程序都无法顺利运行。强迫用户安装.NET框架无疑会影响用户体验,在Windows XP尚未停止服务前,国内软件厂商为了兼容这些用户,通常会选择C++这类语言来编写原生应用,这就造成了国内.NET技术长期不被重视的现状。

考虑.NET版本的兼容

??在考虑使用Mono来作为.NET应用程序的运行时前,首先我们来考虑.NET版本的兼容问题。假设我们使用.NET Framework 3.5版本生成了一个应用程序,那么这个应用程序将在安装了.NET Framework v3.5的计算机上运行,如果目标计算机上没有安装.NET Framework v3.5,这个应用程序将无法正常运行。这个时候,我们可以有两种解决方案:第一种,强迫用户安装.NET Framework v3.5,无论是将该框架集成到安装包中,还是在安装软件的时候自动从网上下载安装,显然这种方式都会影响用户的使用体验让用户对应用程序的印象大打折扣;第二种,尝试让应用程序和.NET版本兼容。我们知道Android程序有一个最低API版本的设置,这样能够保证应用程序在不低于该API版本的设备上运行。这里我们选择这种思路,在.NET程序中,我们可以通过应用程序配置文件中的supportedRuntime节点来指定应用程序运行的.NET Framework版本。例如下面的配置文件能够保证应用程序在.NET Framework v2.0到v3.5间的版本上运行。

<?xml version="1.0"?>
<configuration>
    <startup>
    <supportedRuntime version="v2.0.50727"/>
    <supportedRuntime version="v3.0"/>
    <supportedRuntime version="v3.5"/>
    </startup>
</configuration>

虽然说这样能够保证应用程序的兼容性,可是你这个应用程序的命运却是掌握在.NET Framework手里的,如果用户的计算机上没有安装.NET Framework我们一样还是没辙儿,那么怎么办呢?我们来搭建Mono运行时。

Mono运行时的搭建

??我们在前面说过,Mono主要由三部分组成,即C#编译器(mcs.exe)、Mono运行时(mono.exe)和基础类库。因为我们这里是为了让编写的.NET应用程序运行在Mono运行时中,所以我们这里需要的是Mono运行时(mono.exe)和基础类库。我们建立如下的目录结构:

下面来说说这些目录各自的结构和功能:

* bin目录:放置Mono运行时的目录,主要放置mono.exe、mono-2.0.dll、libgio-2.0-0.dll、libglib-2.0-0.dll、libgthread-2.0-0.dll共5个文件。

* lib目录:放置Mono依赖库的目录,主要放置.NET库目录(此处以4.0为例)、Gac库目录。其中Gac库目录下的Accessibility、Mono.Posix、System、System.Drawing、System.Windows.Forms共5个子目录是我们开发WindowsForm需要使用到的依赖库。

* etc目录:放置我们编写的程序及其相关文件,主程序的文件名为Main.exe。

??好了,现在我们就具备了一个非常轻量级的.NET程序运行环境(其实整个环境的大小在40M左右),注意以上文件都可以在安装Mono在其安装目录内找到。根据博主目前了解到的资料来看,通过Mono运行时来运行文件主要有命令行和一种被称为Mono Embedding的方案。特别地,第二种方案可以直接将运行时嵌入到程序内,我们熟悉的Unity3D引擎就是将整个脚本的运行时嵌入到了C++程序中,但是这种方式比较复杂,暂时博主还没有弄清楚它的内部机制,所以我们这里选择第一种方案。可是它要用命令行啊,迫使普通用户来使用命令行工具是件痛苦的事情,就像我们常常被Git搞得晕头转向一样。那么,我们就用程序来模拟命令行好了!什么?用程序来模拟命令行?这个用C#来写简直不能更简单了好吗?请注意我们这里是不能使用.NET Framework里的功能的,因为它就是一个引导程序嘛,如果引导程序都需要依赖.NET,那我们这个程序怎么搞啊。

??好嘛,那就写C++原生应用吧,它是无需任何依赖的。而在C++中模拟命令行主要有WinExec、ShellExecute和CreateProcess三种方法,关于这三种方法的异同大家可以自行了解,这里我们选择最简单的WinExec。代码如下:

#include <Windows.h>

int main(int agrc,char *args[])
{

    /* 执行命令 */
    WinExec("bin\\mono.exe etc\\Main.exe",SW_NORMAL);
    return 0;
}

我们将编译好的程序命名为Launcher.exe,放置我们前面定义的Mono运行时目录结构的根目录下,这个文件将作为启动文件暴露给用户,当用户点击这个程序后就可以打开主文件Main.exe。好了,现在我们来验证下我们的想法:

作为对比,我们给出正常情况下程序的运行截图:

这样我们现在这个程序就基本实现了脱离.NET框架运行,为什么说是基本呢?因为.NET中的基础类库是作为.NET框架中的一部分存在的,即它并非是CLR的内容。所以我们现在使用到的大部分的基础类库都是Mono重新实现的版本,如果我们使用的某一个库在Mono中没有相应的实现,那么我们就需要自己想办法来解决依赖问题了。现在这个方案每次运行的时候都会闪出命令行窗口,虽然不影响使用,但对一个追求完美的人来说就是瑕疵啦,怎么解决呢?答案就是Mono Embedding。

小结

??本文通过Mono实现了一个轻量级的.NET程序运行环境,从某种程度上来说,它间接地实现了.NET程序脱离.NET Framework运行。这个方案目前看起来存在的主要问题是库依赖的问题,我们现在这个环境有将近40M左右的体积,这是因为我们将常用的库都放在了lib目录中,可是在实际运行中,这些库并非完全都会用到,因此如何根据程序来生成合适的lib目录,是解决运行时环境体积的有效方法。如果靠手动来解决这个问题,这显得困难重重,因为在平时微软将这些库都给我们了,它就在我们的计算机上,所以我们从来没有关注过这个问题。现在当我们面对这个问题的时候,反射可能是种不错的想法,但这种想法的验证就要等到以后啦!

时间: 2024-11-17 23:59:46

使用Mono打造轻量级的.NET程序运行时的相关文章

解决IntelliJ IDEA控制台乱码问题[包含程序运行时的log4j日志以及tomcat日志乱码]

这里使用的IntelliJ IDEA版本为[IntelliJ IDEA 14.1.4]: 一.控制台打印的程序运行时的log4j日志中包含中文乱码 在IDEA安装目录的bin目录下找到名为"idea.exe.vmoptions"的文件: 使用文本编译软件(Notepad++等)打开此文件,在文件内容从末尾追加一行设置(-Dfile.encoding=UTF-8),表示指定编码为UTF-8: 重启IDEA,再次测试,log4j日志不再乱码: 但是发现tomcat启动日志乱码了(修改IDE

Java程序运行时,数据都保存到什么地方?

程序运行时,我们最好对数据保存到什么地方做到心中有数.特别要注意的是内存的分配.有六个地方都可以保存数据: 寄存器 这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部.然而,寄存器的数量十分有限,所以寄存器是根据需要由编译器分配.我们对此没有直接的控制权,也不可能在自己的程序里找到寄存器存在的任何踪迹. 堆栈 驻留于常规RAM(随机访问存储器)区域,但可通过它的"堆栈指针"获得处理的直接支持.堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存.这是一种特别

程序运行时的ds cs

程序 assume cs:code,ds:data data segment db 'unix' db 'fork' data ends code segment start: mov al,'a'mov bl ,'b'mov ax,4c00hint 21h code endsend start ———————————————————————————————————————————————— debug结果 Microsoft Windows [Version 6.1.7601]Copyrigh

HttpRuntime应用程序运行时

System.Web.HttpRuntime类是整个Asp.net服务器处理的入口. 这个类提供了一系列的静态属性,反映web应用程序域的设置信息,而且每个web应用程序域中存在一个System.Web.Runtime类. 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Web; 6 using System.Web.UI; 7

解决qt程序运行时的cannot create Qt for Embedded Linux data directory: /tmp/qtembedded-0

方法1: 1.mkdir /tmp 2.挂载 mount -t tmpfs -o size=32m none /tmp 方法2: 上面的user 0h说明你是以root用户的身份运行.可以尝试切换一下用户重新运行试试 方法3: 把/tmp下的数据文件qtembedded-0删除在运行.

程序运行时 0xC0000005: 读取位置 0x00000000 时发生访问冲突 ,可能是 com 组件引入各种问题

在使用com组件事,可能引入很多不是问题的问题,比如CString 定义出运行时出错等等,这些问题解决的办法就是初始化组件 然后释放组件, 在使用组件时,如果仅仅用在按钮事件或者别的mfc 对话框类里面 可以正常使用,但是用到界面线程,而界面线程函数里面调用了函数com 组件的函数,必须初始化.不然就是上面错误 ::CoInitialize(NULL); 组件初始化函数 ::CoUninitialize(); 组件释放函数

openCV编译的程序运行时error while loading shared libraries

出现这类错误表示,系统不知道xxx.so放在哪个目录下,这时候就要在/etc/ld.so.conf中加入xxx.so所在的目录. 一般而言,有很多的so会存放在/usr/local/lib这个目录底下,去这个目录底下找,果然发现自己所需要的.so文件. 所以,在/etc/ld.so.conf中加入/usr/local/lib这一行,保存之后,再运行:/sbin/ldconfig –v更新一下配置即可.

基于Mono和VSCode打造轻量级跨平台IDE

??最近Visual Studio推出Mac版本的消息迅速在技术圈里刷屏,当工程师们最喜欢的笔记本电脑Mac,邂逅地球上最强大的集成开发环境Visual Studio的时候,会碰撞出怎样精彩的火花呢?在微软新任CEO纳德拉的"移动为先.云为先"战略下,微软的转变渐渐开始让人欣喜,从.NET Core.VSCode.TypeScript再到近期的Visual Studio For Mac,这一系列动作让我们感觉到,微软的技术栈越来越多地向着开源和跨平台两个方向努力.我们曾经固执地认为,微

iOS动态性 运行时runtime初探(强制获取并修改私有变量,强制增加及修改私有方法等)

借助前辈的力量综合一下资料. OC是运行时语言,只有在程序运行时,才会去确定对象的类型,并调用类与对象相应的方法.利用runtime机制让我们可以在程序运行时动态修改类.对象中的所有属性.方法,就算是私有方法以及私有属性都是可以动态修改的.本文旨在对runtime的部分特性小试牛刀,更多更全的方法可以参考系统API文件<objc/runtime.h>,demo例子可以参见CSDN的runtime高级编程系列文章. 我们出发吧! 先看一个非常平常的Father类: #import <Fou