最让.NET程序员苦恼的是,辛辛苦苦写出来的.NET程序,需要客户机上安装了.NET才能运行。仅为一个小小的应用程序去下载上百兆的.NET安装包,还得把它老老实实安装到客户机上,并占掉数百兆磁盘空间,这无疑是一件得不偿失的事情。.NET程序的这个弱点,也是影响.NET应用程序普及和价值的一个重要因素。 所谓“独立运行”,是指.NET应用程序脱离完整的.NET运行环境,像c语言编译的程序那样,在操作系统上直接运行。简单地说就是:客户电脑无需安装任何版本的.NET框架,你的.NET程序照样可以在他的电脑或服务器上运行。 .NET程序独立运行的基础是mono运行时以及它的程序集。mono是什么呢,mono是一款开源、免费、可定制的跨平台.NET运行环境,同时,它还包含了一系列具有重要意义的实用工具,当前最新的版本号是3.0.10,本文所采用的mono,即是这个版本号的windows版。
那么,到底怎么才能让你的.NET程序无障碍地在没有安装.NET平台的客户机“独立运行”呢,下面直奔主题。
一,建立跨平台的.NET环境与编译环境: 1、下载并安装mono的windows版,建议将它安装到c:/mono文件夹中。 2、安装cygwin。 A、建议将它安装到c:/cygwin文件夹中。 B、安装时,请将mingw-gcc、mingw-zlib、pkg-config、libiconv这几个组件选上,这是将.NET程序转化为本地程序的必要的编译环境。
二,启动cgywin并设置环境变量: 1、点击开始菜单或桌面上的cygwin图标,启动且进入cygwin环境。 2、输入下面的命令,设置或修改必要的环境变量: export PKG_CONFIG_PATH=/cygdrive/c/mono/lib/pkgconfig export PATH=$PATH:/cygdrive/c/mono/bin
三,将你的.NET程序转化为“独立程序”
请注意,这是本文的关键所在,很多地方的操作都有别于其它网文和mono官网所介绍的操作技术。
1,复制文件。把需编译的.NET EXE文件和对应的DLL文件复制到你在cygwin的工作文件夹中,如果你的windows用户名是xyz,那么这个文件夹就是 c:/cygwin/home/xyz/,(这一步不是必须的,如果你不怕麻烦而愿意多打字的话)。
2,转换与打包。通过下面的命令,将.net程序和类库打包并得到一个c程序源码(假设你需要转换的.NET文件是a.exe)。 mkbundle -c -o b.c -oo b.o a.exe -z 或者: mkbundle -c -o b.c -oo b.o a.exe aa.dll c://mono//lib//mono//4.5//mscorlib.dll -z 或者: mkbundle -c -o b.c -oo b.o --dept a.exe -z
3,修改得到的c文件: 这是本文的精华所在。 为什么要修改这个c文件,很简单: A,不希望与exe文件相关的类库全部打包到一个文件中,否则,太浪费,而且影响启动速度。 B,这个c文件是目标程序的关键文件,我希望在中间加上自己的东西,让我的程序如虎添翼。 C,我程序要在中文、日文这样的含有非英文字母的文件夹中运行。
3.1,需要添加和修改的内容: A,用VS或记事本打开b.c,把下面的代码复制到main函数之前,作一个准备。
#include <dir.h> #include "/usr/include/iconv.h" int gbk_utf8(char *inbuf,int inlen,char *outbuf,int outlen){ iconv_t cd; char **pin = &inbuf; char **pout = &outbuf; cd = iconv_open("utf-8","gbk"); if (cd == 0) return -1; memset(outbuf, 0, outlen); if (iconv(cd, pin, &inlen, pout, &outlen) == -1) return -1; iconv_close(cd); return 0; }
B、在main函数中,找到下面这两行并注释或删除掉: if (config_dir != NULL && getenv ("MONO_CFG_DIR") == NULL) mono_set_dirs (getenv ("MONO_PATH"), config_dir); C、接着,就在这行下边,即“mono_mkbundle_init”一行之前,输入下边的代码:
const char* lib = "//lib"; const char* etc = "//etc";
char p[strlen(argv[0])]; wsPRintf(p,"%s",argv[0]);
int l = 0; l = strlen(p); for(i=l-1; i>0; i--){ if(p[i] == '//'){ p[i] = '/0'; break; } }
l = strlen(p) + strlen(lib); char s_lib[l]; wsprintf(s_lib, "%s%s", p, lib);
l = strlen(s_lib); char* s_lib_utf8 = (char*)malloc(l*2); memset(s_lib_utf8, 0, l*2); gbk_utf8(s_lib, l, s_lib_utf8, l*2);
l = strlen(p) + strlen(etc); char s_etc[l]; wsprintf(s_etc, "%s%s", p, etc);
l = strlen(s_etc); char* s_etc_utf8 = (char*)malloc(l*2); memset(s_etc_utf8, 0, l*2); gbk_utf8(s_etc, l, s_etc_utf8, l*2);
mono_set_dirs(s_lib_utf8, s_etc_utf8);
接着在mono_mkbundle_init一行之后加入一行: chdir("c://");
最后,找到下面三行 #ifdef _WIN32 #include <windows.h> #endif 并在“#endif”后加入一行: #undef _WIN32
改完了,存盘退出。 (有人会说“输入这么多,为什么不写个函数以方便我将来复用?”,我说,这不是我的事。)
3.2,编译: 用下面这个命令生成你的目标文件“b.exe”。 gcc -mno-cygwin -o b.exe -Wall b.c `pkg-config --cflags --libs mono-2|dos2unix` b.o -lz -liconv
四,程序、类库、配置文件的组织: 这一步,是为你的程序安一个家,让它真的能跑起来。
1,在某个盘,比如D盘,建个文件夹,比如是“myapp” 把刚才编译得到的目标文件b.exe复制到D:/myapp文件夹中。 同时把c:/mono/bin/文件夹中的mono-2.0.dll、zlib1.dll、iconv.dll复制到d:/myapp中。
2,组织类库 在“d:/myapp”文件夹中,建lib和etc两个子目录。 在lib文件夹中,建名叫“mono”的文件夹。 在d:/myapp/lib/mono文件夹中,根据你.NET程序集版本号建一个文件夹,名字就是版本号,比如“4.5”,当然,你也可以把2.0、4.0也建好。 如果你没有将mscorlib.dll打包到.EXE中,请将c:/mono/lib/mono/4.5/mscorlib.dll,复制到 d:/myapp/lib/mono/4.5这个文件夹中。 在d:/myall/lib/mono文件夹中,建一个名为gac的文件夹,这个文件夹是用来放你的程序需要的mono版.NET类库的。 放些什么?放你的exe、dll文件中引用到的那些程序集的库文件(如果你已经把这些文件打包到了.exe中,那么你就不需要放任何文件)。 比如,你引用了System名字空间,那么,将c:/mono/lib/mono/gac文件夹下的System文件夹复制到D:/myapp/lib/mono/gac中就行了。
3,组织配置文件 把c:/mono/etc文件夹中的“mono”文件夹复制到d:/myapp/etc文件夹中。 用写字版打开config文件,找到并删除下列三行: <dllmap dll="gdiplus" target="/tmp/install/lib/libgdiplus.so" /> <dllmap dll="gdiplus.dll" target="/tmp/install/lib/libgdiplus.so" /> <dllmap dll="gtkhtml-3.0" target="libgtkhtml-3.8-15.dll"/>
通过上面的几个步骤,你的程序已经变成了可以独立运行的程序了,你把d:/myapp这个文件夹压缩打包,然后解压到没有安装.net的电脑上,试试。可能有读者会说,对本文某些地方不太理解或者有异议,那么,我欢迎讨论。你可以在这些地方与我取得联系:群号为103810355的QQ群里或者网址为www.linuxdot.net的网站上。
转自:https://www.linuxdot.net/bbsfile-3354
新闻热点
疑难解答