C#程序程序进行编译时,不为形式参数是怎么处理被引用的dll

转载请注明出处!
C#生成dll文件是Visual Studio2015自动生成的,把项目的程序集信息(类的字段、属性、方法函数等信息)封装在dll中。然后其他项目可以通过引用dll来调用封装好的程序集中的信息。以下是我经常使用的VS2015生成并调用的C#
一、生成C#的dll文件
1、选择解决方案下要生成dll文件的项目,右键该项目-&属性-&应用程序。
2、在应用程序面板中有一个“输出类型”的下拉框,选择“类库”,Ctrl+S保存
3、点击菜单栏的“生成”选项-&重新生成解决方案。(也可以在要生成dll的解决方案或某一项目上右键-&重新生成解决方案)
4、此时VS已经自动将解决方案下的“输出类型”为“类库”的项目生成了dll文件,dll文件的路径就在工程项目文件夹下的bin\Debug\下。可以右键项目-&在文件资源管理器中打开文件夹,然后可以找到bin\Debug\下的dll文件。例如在此我的dll文件名为Test01.dll。
二、使用生成的dll文件
1、找到你要使用的dll文件(我一般把dll文件放在要使用的项目\bin\Debug\路径下)
2、在工程项目下的引用位置,右键-&添加引用-&浏览-&选择你要使用的dll文件-&确定,此时在引用管理器的浏览界面,会出现你要引用的dll文件名称,确认打勾后点击确定。此时你的项目已经引用了dll文件,可以调用dll文件中封装的信息。
3、在此我只列举一个用程序集调用dll中信息的例子。
例如在项目Test01中命名空间为Test01,有一个MyClass的类,该类中有Name属性、Age字段、ShowName()方法,生成dll文件后,让Test02项目引用该dll,然后在Test02的脚本中就可以引用Test01项目中的命名空间了(using
Test01;),也可以调用MyClass类及其的属性、字段、方法等;代码如下:
Test01项目中的MyClass类,其中命名空间为Test01。
Test02项目中引用dll中Test01的命名空间(using Test01;),然后通过程序集Assembly来访问dll中的MyClass类信息。
运行后控制台输出如下:
小弟也是自己学习C#的dll生成和调用,分享给大家,若有不对,希望大家指正……嘿嘿。
下一篇讲给大家分享关于C#的反射机制(包含本篇中调用dll程序集信息的扩展),敬请期待……
C#从零开始——生成dll文件并调用
学过c的课程,了解一点语法,从现在开始学习C#并记录下来。用来生成dll文件:
在控制台用命令行生成dll文件:
调用dll文件:
来张全家福:
最后的运行结果就不来...
C# 创建与引用类库项目dll
一,类的访问修饰符先看下三种类的访问修饰符情况:public class//公开类,都能访问该类
internal class//仅能在当前项目访问类
class//同internal class所以...
C# cs封装成dll
C# 讲类封装成dll文件
在.NET中的C# DLL文件的生成与使用
1.生成.DLL文件
步骤1:打开Microsoft Visual Studio 2010,点击【文件】→【新建】→【项目】→【类库】,如下图所示。修改名称和位置后,点击【确定】。
C#生成dll,使用dll
C# 创建dll,使用dll
20、C#:使用VS2010生成dll文件
本文来自:http://blog.csdn.net/tzjly/article/details/6730997
有两种方法:
但是一般这个使用
打开VS2008,依次点击:菜单-...
C#项目如何生成dll文件
1.命令方式
打开Visual Studio安装目录下的开发人员命令提示
译 File.cs 以产生 File.exe
csc File.cs 编译 File.cs 以产生 File.dll
使用C#生成dll文件并调用
有两种方法:
但是一般这个使用
打开VS2008,依次点击:菜单-&文件-&新建项目-&项目类型visual C#(这里假设为该项目所取的名字是DllBuild)-&类库(注意必须...
使用VS2013创建C#的dll文件
之前在看C#书的时候,讲到了命名空间这一部分,于是去网上搜了一下如何用VS2013来创建dll文件,看到大家很多都是用C++里的win32来创建的,感觉很不方便,这VS2013里现在都是有各种模版文件...
C#创建和调用DLL
一、写在前面 C# 语言是一种简单但功能强大的编程语言,用于编写企业应用程序。 C# 语言从C和 C++语言演化而来,在语句、表达式和运算符方面使用了许多 C++ 功能。 C# 语言在类型安全性、版本...
没有更多推荐了,下次自动登录
现在的位置:
& 综合 & 正文
C#程序实现动态调用DLL的研究
开发高手》
期中的《化功大法——将
》一文,介绍了如何把一个动态链接库作为一个资源嵌入到可执行文件,在可执行文件运行时,自动从资源中释放出来,通过静态加载延迟实现DLL函数的动态加载,程序退出后实现临时文件的自动删除,从而为解决“
”提供了一种解决方案。这是一个很好的设计思想,而且该作者也用
实现了,在
上也有相似的
程序,但在某一技术论坛上提起这种设计方法时,有网友提出:“这种方法好是好,但就是启动速度太慢”。这是因为程序启动时实现
释放,然后再加载释放出来的
,这个过程会耗费一定的时间。鉴于此问题,经过思索,提出另一个设计方案:
作为资源文件嵌入程序,但不需进行
释放及其重新加载。本文就是对该设计方案的原理分析及使用
编程来实现该设计方案。
与应用程序
动态链接库(也称为
Dynamic Link Library
”的缩写)是
Microsoft Windows
最重要的组成要素之一,打开
系统文件夹,你会发现文件夹中有很多
就是将一些主要的系统功能以
模块的形式实现。
动态链接库是不能直接执行的,也不能接收消息,它只是一个独立的文件,其中包含能被程序或其它
调用来完成一定操作的函数
方法。注:
中一般称为“方法”
,但这些函数不是执行程序本身的一部分,而是根据进程的需要按需载入,此时才能发挥作用。
只有在应用程序需要时才被系统加载到进程的虚拟空间中,成为调用进程的一部分,此时该
也只能被该进程的线程访问,它的句柄可以被调用进程所使用,而调用进程的句柄也可以被该
所使用。在内存中,一个
只有一个实例,且它的编制与具体的和编译器都没有关系,所以可以通过
来实现混合语言编程。
函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。
下面列出了当程序使用
时提供的一些优点:
使用较少的资源
当多个程序使用同一个函数库时,
可以减少在磁盘和物理内存中加载的代码的重复量。这不仅可以大大影响在前台运行的程序,而且可以大大影响其他在
操作系统上运行的程序。
推广模块式体系结构
有助于促进模块式程序的开发。这可以帮助您开发要求提供多个语言版本的大型程序或要求具有模块式体系结构的程序。模块式程序的一个示例是具有多个可以在运行时动态加载的模块的计帐程序。
简化部署和安装
中的函数需要更新或修复时,部署和安装
不要求重新建立程序与该
的链接。此外,如果多个程序使用同一个
,那么多个程序都将从该更新或修复中获益。当您使用定期更新或修复的第三方
时,此问题可能会更频繁地出现。
每种编程语言调用
的方法都不尽相同,在此只对用
的方法进行介绍。首先
您需要了解什么是托管
什么是非托管。一般可以认为:非托管代码主要是基于
平台开发的
的组件,托管代码是基于
平台开发的。如果您想深入了解托管与非托管的关系与区别,及它们的运行机制,请您自行查找资料,本文件在此不作讨论。
中的非托管函数一般方法
语言源程序中声明外部方法,其基本形式是:
[DLLImport(“DLL
返回变量类型
(参数列表)
文件:包含定义外部方法的库文件。
访问修饰符,除了
以外在声明方法时可以使用的修饰符。
返回变量类型:在
文件中你需调用方法的返回变量类型。
方法名称:在
文件中你需调用方法的名称。
参数列表:在
文件中你需调用方法的列表。
:需要在程序声明中使用
System.Runtime.InteropServices
命名空间。
只能放置在方法声明上。
文件必须位于程序当前目录或系统定义的查询路径中(即:系统环境变量中
所设置的路径)。
返回变量类型、方法名称、参数列表一定要与
文件中的定义相一致。
若要使用其它函数名,可以使用
EntryPoint
属性设置,如:
[DllImport("user32.dll", EntryPoint="MessageBoxA")]
static extern int MsgBox(int hWnd, string msg, string caption, int type);
其它可选的
DllImportAttribute
指示用在入口点中的字符集,如:
CharSet=CharSet.Ansi
SetLastError
指示方法是否保留
SetLastError=true
ExactSpelling
EntryPoint
是否必须与指示的入口点的拼写完全匹配,如:
ExactSpelling=false
PreserveSig
指示方法的签名应当被保留还是被转换,
PreserveSig=true
CallingConvention
指示入口点的调用约定,
CallingConvention=CallingConvention.Winapi
此外,关于“数据封送处理”及“封送数字和逻辑标量”请参阅其它一些文章
,新建一个项目,项目名称为“
”,模板为“
应用程序”。
在“工具箱”的“
窗体”项中双击“
”项,向“
”窗体中添加一个按钮。
改变按钮的属性:
弹出提示框
,并将按钮
调整到适当大小,移到适当位置。
在类视图中双击“
”,打开“
”代码视图,在“
namespace Tzb
”上面输入“
using System.Runtime.InteropS
”,以导入该命名空间。
[设计]”视图中双击按钮
”方法上面使用关键字
声明方法“
属性附加到该方法,这里我们要使用的是“
MessageBoxA
”函数,具体代码如下:
[DllImport("user32.dll", EntryPoint="MessageBoxA")]
static extern int MsgBox(int hWnd, string msg, string caption, int type);
”方法体内添加如下代码,以调用方法“
MsgBox(0," 这就是用 DllImport 调用 DLL 弹出的提示框哦! "," 挑战杯 ",0x30);
运行该程序,并点击按钮
,便弹出如下提示框:
动态装载、调用
中的非托管函数
在上面已经说明了如何用
中的非托管函数,但是这个是全局的函数,假若
中的非托管函数有一个静态变量
,每次调用这个函数的时候,静态变量
。结果,当需要重新计数时,就不能得出想要的结果。下面将用例子说明:
Visual C++ 6.0
Win32 Dynamic-Link Library
”工程,工程名称为“
”选择界面中选择“
A simple dll project
,添加如下代码:
// 导出函数,使用“ _stdcall ” 标准调用
extern "C" _declspec(dllexport)int _stdcall count(int init);
int _stdcall count(int init)
{//count 函数,使用参数 init 初始化静态的整形变量 S ,并使 S 自加 1 后返回该值
static int S=
进行编译,得到
(在工程目录下的
文件夹中)。
用DllImport
打开项目“
窗体中添加一个按钮。
改变按钮的属性:
函数”,并将按钮
调整到适当大小,移到适当位置。
”代码视图,使用关键字
声明方法“
”,并使其具有来自
的导出函数
的实现,代码如下:
[DllImport("Count.dll")]
static extern int count(int init);
[设计]”视图中双击按钮
”方法体内添加如下代码:
MessageBox.Show(" 用 DllImport 调用 DLL 中的 count 函数, /n 传入的实参为 0 ,得到的结果是: "+count(0).ToString()," 挑战杯 ");
MessageBox.Show(" 用 DllImport 调用 DLL 中的 count 函数, /n 传入的实参为 10 ,得到的结果是: "+count(10).ToString()+"/n 结果可不是想要的 11 哦!!! "," 挑战杯 ");
MessageBox.Show(" 所得结果表明: /n 用 DllImport 调用 DLL 中的非托管 /n 函数是全局的、静态的函数!!! "," 挑战杯 ");
复制到项目“
文件夹中,按“
运行该程序,并点击按钮
,便弹出如下三个提示框:
个提示框显示的是调用“
”的结果,第
个提示框显示的是调用“
”的结果,由所得结果可以证明“用
中的非托管函数是全局的、静态的函数”。所以,有时候并不能达到我们目的,因此我们需要使用下面所介绍的方法:
中的函数。
是不能像动态
load/unload assembly
那样,所以只能借助
函数了。在
kernel32.dll
中,与动态库调用有关的函数包括
LoadLibrary
AfxLoadLibrary
),装载动态库。
GetProcAddress
,获取要引入的函数,将符号名或标识号转换为
内部地址。
FreeLibrary
AfxFreeLibrary
),释放动态链接库。
它们的原型分别是:
HMODULE LoadLibrary(LPCTSTR lpFileName);
FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);
BOOL FreeLibrary(HMODULE hModule);
现在,我们可以用
IntPtr hModule=LoadLibrary(“Count.dll”);
IntPtr farProc=GetProcAddress(hModule,”
_count@4”
来获得函数的入口地址。
但是,知道函数的入口地址后,怎样调用这个函数呢?因为在
中是没有函数指针的,没有像
那样的函数指针调用方式来调用函数,所以我们得借助其它方法。经过研究,发现我们可以通过结合使用
System.Reflection.Emit
System.Reflection.Assembly
里的类和函数达到我们的目的。为了以后使用方便及实现代码的复用,我们可以编写一个类。
类的编写:
打开项目“
”,打开类视图,右击“
”,选择“添加”
“类”,类名设置为“
dynamic loading dll
的每个单词的开头字母。
添加所需的命名空间及声明参数传递方式枚举:
using System.Runtime.InteropS // 用 DllImport 需用此 命名空间
using System.R // 使用 Assembly 类需用此 命名空间
using System.Reflection.E // 使用 ILGenerator 需用此 命名空间
public class dld
”上面添加如下代码声明参数传递方式枚举:
/// &summary&
/// 参数传递方式枚举 ,ByValue 表示值传递 ,ByRef 表示址传递
/// &/summary&
public enum ModePass
ByValue = 0x0001,
ByRef = 0x0002
LoadLibrary
GetProcAddress
FreeLibrary
及私有变量
/// &summary&
/// 原型是 :HMODULE LoadLibrary(LPCTSTR lpFileName);
/// &/summary&
/// &param name="lpFileName"&DLL 文件名 &/param&
/// &returns& 函数库模块的句柄 &/returns&
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
/// &summary&
/// 原型是 : FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);
/// &/summary&
/// &param name="hModule"& 包含需调用函数的函数库模块的句柄 &/param&
/// &param name="lpProcName"& 调用函数的名称 &/param&
/// &returns& 函数指针 &/returns&
[DllImport("kernel32.dll")]
static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
/// &summary&
/// 原型是 : BOOL FreeLibrary(HMODULE hModule);
/// &/summary&
/// &param name="hModule"& 需释放的函数库模块的句柄 &/param&
/// &returns& 是否已释放指定的 Dll&/returns&
[DllImport("kernel32",EntryPoint="FreeLibrary",SetLastError=true)]
static extern bool FreeLibrary(IntPtr hModule);
/// &summary&
/// Loadlibrary 返回的函数库模块的句柄
/// &/summary&
private IntPtr hModule=IntPtr.Z
/// &summary&
/// GetProcAddress 返回的函数指针
/// &/summary&
private IntPtr farProc=IntPtr.Z
方法,并为了调用时方便,重载了这个方法:
/// &summary&
/// 装载 Dll
/// &/summary&
/// &param name="lpFileName"&DLL 文件名 &/param&
public void LoadDll(string lpFileName)
hModule=LoadLibrary(lpFileName);
if(hModule==IntPtr.Zero)
throw(new Exception(" 没有找到 :"+lpFileName+"." ));
若已有已装载
的句柄,可以使用
方法的第二个版本:
public void LoadDll(IntPtr HMODULE)
if(HMODULE==IntPtr.Zero)
throw(new Exception(" 所传入的函数库模块的句柄 HMODULE 为空 ." ));
hModule=HMODULE;
方法,并为了调用时方便,也重载了这个方法,方法的具体代码及注释如下:
/// &summary&
/// 获得函数指针
/// &/summary&
/// &param name="lpProcName"& 调用函数的名称 &/param&
public void LoadFun(string lpProcName)
{ // 若函数库模块的句柄为空,则抛出异常
if(hModule==IntPtr.Zero)
throw(new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !"));
// 取得函数指针
farProc = GetProcAddress(hModule,lpProcName);
// 若函数指针,则抛出异常
if(farProc==IntPtr.Zero)
throw(new Exception(" 没有找到 :"+lpProcName+" 这个函数的入口点 "));
/// &summary&
/// 获得函数指针
/// &/summary&
/// &param name="lpFileName"& 包含需调用函数的 DLL 文件名 &/param&
/// &param name="lpProcName"& 调用函数的名称 &/param&
public void LoadFun(string lpFileName,string lpProcName)
{ // 取得函数库模块的句柄
hModule=LoadLibrary(lpFileName);
// 若函数库模块的句柄为空,则抛出异常
if(hModule==IntPtr.Zero)
throw(new Exception(" 没有找到 :"+lpFileName+"." ));
// 取得函数指针
farProc = GetProcAddress(hModule,lpProcName);
// 若函数指针,则抛出异常
if(farProc==IntPtr.Zero)
throw(new Exception(" 没有找到 :"+lpProcName+" 这个函数的入口点 "));
方法也进行了重载:
/// &summary&
/// 卸载 Dll
/// &/summary&
public void UnLoadDll()
FreeLibrary(hModule);
hModule=IntPtr.Z
farProc=IntPtr.Z
方法的第一个版本:
/// &summary&
/// 调用所设定的函数
/// &/summary&
/// &param name="ObjArray_Parameter"& 实参 &/param&
/// &param name="TypeArray_ParameterType"& 实参类型 &/param&
/// &param name="ModePassArray_Parameter"& 实参传送方式 &/param&
/// &param name="Type_Return"& 返回类型 &/param&
/// &returns& 返回所调用函数的 object&/returns&
object Invoke(object[] ObjArray_Parameter,Type[]
TypeArray_ParameterType,ModePass[] ModePassArray_Parameter,Type
Type_Return)
// 下面 3 个 if 是进行安全检查 , 若不能通过 , 则抛出异常
if(hModule==IntPtr.Zero)
throw(new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !"));
if(farProc==IntPtr.Zero)
throw(new Exception(" 函数指针为空 , 请确保已进行 LoadFun 操作 !" ) );
if(ObjArray_Parameter.Length!=ModePassArray_Parameter.Length)
throw(new Exception(" 参数个数及其传递方式的个数不匹配 ." ) );
// 下面是创建 MyAssemblyName 对象并设置其 Name 属性
AssemblyName MyAssemblyName = new AssemblyName();
MyAssemblyName.Name = "InvokeFun";
// 生成单模块配件
AssemblyBuilder MyAssemblyBuilder =AppDomain.CurrentDomain.DefineDynamicAssembly(MyAssemblyName,AssemblyBuilderAccess.Run);
ModuleBuilder MyModuleBuilder =MyAssemblyBuilder.DefineDynamicModule("InvokeDll");
// 定义要调用的方法 , 方法名为“ MyFun ”,返回类型是“ Type_Return ”参数类型是“ TypeArray_ParameterType ”
MethodBuilder
MyMethodBuilder
=MyModuleBuilder.DefineGlobalMethod("MyFun",MethodAttributes.Public|
MethodAttributes.Static,Type_Return,TypeArray_ParameterType);
// 获取一个 ILGenerator ,用于发送所需的 IL
ILGenerator IL = MyMethodBuilder.GetILGenerator();
for (i = 0; i & ObjArray_Parameter.L i++)
{// 用循环将参数依次压入堆栈
switch (ModePassArray_Parameter[i])
case ModePass.ByValue:
IL.Emit(OpCodes.Ldarg, i);
case ModePass.ByRef:
IL.Emit(OpCodes.Ldarga, i);
throw(new Exception(" 第 " +(i+1).ToString() + " 个参数没有给定正确的传递方式 ." ) );
if (IntPtr.Size == 4) {// 判断处理器类型
IL.Emit(OpCodes.Ldc_I4, farProc.ToInt32());
else if (IntPtr.Size == 8)
IL.Emit(OpCodes.Ldc_I8, farProc.ToInt64());
throw new PlatformNotSupportedException();
IL.EmitCalli(OpCodes.Calli,CallingConvention.StdCall,Type_Return,TypeArray_ParameterType);
IL.Emit(OpCodes.Ret); // 返回值
MyModuleBuilder.CreateGlobalFunctions();
// 取得方法信息
MethodInfo MyMethodInfo = MyModuleBuilder.GetMethod("MyFun");
return MyMethodInfo.Invoke(null, ObjArray_Parameter);// 调用方法,并返回其值
方法的第二个版本,它是调用了第一个版本的:
/// &summary&
/// 调用所设定的函数
/// &/summary&
/// &param name="IntPtr_Function"& 函数指针 &/param&
/// &param name="ObjArray_Parameter"& 实参 &/param&
/// &param name="TypeArray_ParameterType"& 实参类型 &/param&
/// &param name="ModePassArray_Parameter"& 实参传送方式 &/param&
/// &param name="Type_Return"& 返回类型 &/param&
/// &returns& 返回所调用函数的 object&/returns&
object Invoke(IntPtr IntPtr_Function,object[] ObjArray_Parameter,Type[]
TypeArray_ParameterType,ModePass[] ModePassArray_Parameter,Type
Type_Return)
// 下面 2 个 if 是进行安全检查 , 若不能通过 , 则抛出异常
if(hModule==IntPtr.Zero)
throw(new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !"));
if(IntPtr_Function==IntPtr.Zero)
throw(new Exception(" 函数指针 IntPtr_Function 为空 !" ) );
farProc=IntPtr_F
return Invoke(ObjArray_Parameter,TypeArray_ParameterType,ModePassArray_Parameter,Type_Return);
类的使用:
打开项目“
窗体中添加
三个按钮。
属性分别为
LoadLibrary
方法”,“
”,并调整到适当的大小及位置
[设计]”视图中双击按钮
”方法体上面添加代码,创建一个
/// &summary&
/// 创建一个 dld 类对象
/// &/summary&
private dld myfun=new dld();
”方法体内添加如下代码:
myfun.LoadDll("Count.dll"); // 加载 "Count.dll"
myfun.LoadFun("_count@4"); // 调入函数 count, "_count@4" 是它的入口,可通过 Depends 查看
[设计]”视图中双击按钮
”方法体内添加如下代码:
object[] Parameters = new object[]{(int)0}; // 实参为 0
Type[] ParameterTypes = new Type[]{typeof(int)}; // 实参类型为 int
ModePass[] themode=new ModePass[]{ModePass.ByValue}; // 传送方式为值传
Type Type_Return = typeof(int); // 返回类型为 int
// 弹出提示框,显示调用 myfun.Invoke 方法的结果,即调用 count 函数
MessageBox.Show(" 这是您装载该 Dll 后第 "+myfun.Invoke(Parameters,ParameterTypes,themode,Type_Return).ToString()
+" 次点击此按钮。 "," 挑战杯 ");
[设计]”视图中双击按钮
”方法体内添加如下代码:
myfun.UnLoadDll();
”运行该程序,并先点击按钮
加载“Count.dll
,接着点击按钮
三次以调用
先后弹出的提示框如下:
这三个提示框所得出的结果说明了静态变量
经初始化后,再传入实参“
也不会改变其值为“
”,再点击按钮
进行装载“
”,再点击按钮
查看调用了“
”的结果:
从弹出的提示框所显示的结果可以看到又开始重新计数了,也就是实现了DLL
的动态装载与卸载了。
是很简单的,只要在“解决方案资源管理器”中的需要调用
的项目下用鼠标右击“引用”,并选择“添加引用”,然后选择已列出的
或通过浏览来选择
文件,最后需要用
导入相关的命名空间。
动态调用托管
动态调用托管
也需要借助
System.Reflection.Assembly
里的类和方法,主要使用了
Assembly.LoadFrom
。现在,用例子说明:
首先,启动
,新建一个
项目,使用的模板为“类库”,名称为“
”,并在类“
添加静态整型变量
// 由于 static 不能修饰方法体内的变量,所以需放在这里,且初始化值为 int.MinValue
static int S=int.MinV
public int count(int init)
{// 判断 S 是否等于 int.MinValue ,是的话把 init 赋值给 S
if(S==int.MinValue) S=
S++; //S 自增 1
return S; // 返回 S
然后,打开项目“
窗体中添加
一个按钮,
属性为“用
类来动态调用托管
”,调整到适当大小和位置,双击按钮
,转入代码视图,先导入命名空间:
using System.R
方法代码:
private object Invoke(string lpFileName,string Namespace,string ClassName,string lpProcName,object[] ObjArray_Parameter)
Try { // 载入程序集
Assembly MyAssembly=Assembly.LoadFrom(lpFileName);
Type[] type=MyAssembly.GetTypes();
foreach(Type t in type)
{// 查找要调用的命名空间及类
if(t.Namespace==Namespace&&t.Name==ClassName)
{// 查找要调用的方法并进行调用
MethodInfo m=t.GetMethod(lpProcName);
if(m!=null)
object o=Activator.CreateInstance(t);
return m.Invoke(o,ObjArray_Parameter);
else MessageBox.Show(" 装载出错 !");
catch(System.NullReferenceException e)
MessageBox.Show(e.Message);
return (object)0;
}// Invoke
”方法体内代码如下:
// 显示 count(0) 返回的值
MessageBox.Show(" 这是您第 "+Invoke("CsCount.dll","CsCount","Class1","count",new object[]{(int)0}).ToString()+" 次点击此按钮。 "," 挑战杯 ");
最后,把项目“
文件夹中的
CsCount.dll
复制到项目“
文件夹中,按“
运行该程序,并点击按钮
三次,将会弹出
个提示框,内容分别是“这是您第
次点击此按钮。”、“这是您第
次点击此按钮。”、“这是您第
次点击此按钮。”,由此知道了静态变量
在这里的作用。
文件作为资源嵌入在
中,我们只要读取该资源文件并以“
”返回,然后就用“Assembly Load(byte[]);
程序集,最后就可以像上面的
方法那样对
中的方法进行调用。当然不用上面方法也可以,如用接口实现动态调用,但
中必须有该接口的定义并且程序
中也要有该接口的定义;也可用反射发送实现动态调用
。现在我只对
方法那样对
中的方法进行调用进行讨论,为了以后使用方便及实现代码的复用,我们可以结合上一个编写一个类。
类的编写:
”中新建一个名为
的类,意为“
load dll from resource
”,请注意,在这个类中“
”不只是嵌入在
程序中的资源,它也可以是硬盘上任意一个
文件,这是因为
的类中的方法
有些特别,就是先从程序的内嵌的资源中查找需加载的
,如果找不到,就查找硬盘上的。
首先导入所需的命名空间:
using System.IO; // 对文件的读写需要用到此命名空间
using System.R // 使用 Assembly 类需用此命名空间
using System.Reflection.E // 使用 ILGenerator 需用此命名空间
声明一静态变量
MyAssembly
// 记录要导入的程序集
static Assembly MyA
private byte[] LoadDll(string lpFileName)
Assembly NowAssembly = Assembly.GetEntryAssembly();
Stream fs=
{// 尝试读取资源中的 DLL
fs = NowAssembly.GetManifestResourceStream(NowAssembly.GetName().Name+"."+lpFileName);
{// 如果资源没有所需的 DLL ,就查看硬盘上有没有,有的话就读取
if (fs==null&&!File.Exists(lpFileName)) throw(new Exception(" 找不到文件 :"+lpFileName));
else if(fs==null&&File.Exists(lpFileName))
FileStream Fs = new FileStream(lpFileName, FileMode.Open);
fs=(Stream)Fs;
byte[] buffer = new byte[(int) fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
// 以 byte[] 返回读到的 DLL
方法来卸载
public void UnLoadDll()
{// 使 MyAssembly 指空
MyAssembly=
方法来进行对
中方法的调用,其原理大体上和“
”中的方法
相同,不过这里用的是
Assembly.Load
”,而且用了
MyAssembly
来保存已加载的
,如果已加载的话就不再加载,如果还没加载或者已加载的不同现在要加载的
就进行加载,其代码如下所示:
public object Invoke(string lpFileName,string Namespace,string ClassName,string lpProcName,object[] ObjArray_Parameter)
{// 判断 MyAssembly 是否为空或 MyAssembly 的命名空间不等于要调用方法的命名空间,如果条件为真,就用 Assembly.Load 加载所需 DLL 作为程序集
if(MyAssembly==null||MyAssembly.GetName().Name!=Namespace)
MyAssembly=Assembly.Load(LoadDll(lpFileName));
Type[] type=MyAssembly.GetTypes();
foreach(Type t in type)
if(t.Namespace==Namespace&&t.Name==ClassName)
MethodInfo m=t.GetMethod(lpProcName);
if(m!=null)
{// 调用并返回
object o=Activator.CreateInstance(t);
return m.Invoke(o,ObjArray_Parameter);
System.Windows.Forms.MessageBox.Show(" 装载出错 !");
catch(System.NullReferenceException e)
System.Windows.Forms.MessageBox.Show(e.Message);
return (object)0;
类的使用:
CsCount.dll
作为“嵌入的资源”添加到项目“
”窗体中添加两个按钮,
属性分别为“
ldfs.Invoke
”,并将它们调整到适当大小和位置。
”代码视图,添加一个
// 添加一个 ldfs 实例 tmp
private ldfs tmp=new ldfs();
[设计]”视图中双击按钮
”方法体内添加如下代码:
// 调用 count(0), 并使用期提示框显示其返回值
MessageBox.Show("
这是您第 "+tmp.Invoke("CsCount.dll","CsCount","Class1","count",new
object[]{(int)0}).ToString()+" 次点击此按钮。 "," 挑战杯 ");
[设计]”视图中双击按钮
”方法体内添加如下代码:
// 卸载 DLL
tmp.UnLoadDll();
”运行该程序,并先点击按钮
三次,接着点击按钮
,最后再点击按钮
,此时发现又开始重新计数了,情况和“
类的使用”类似,
也就是也实现了DLL
的动态装载与卸载了
说明:以上所用到的所有源代码详见附件
1:Form1.cs
4:Count.cpp
5:Class1.cs
有很多优点,如:节省内存和减少交换操作;开发大型程序时可以把某些模块分配给程序员,程序员可以用任何一门他所熟悉的语言把该模块编译成
文件,这样可以提高代码的复用,大大减轻程序员的工作量。当然
也有一些不足,如在提要中提及的问题。所以,如何灵活地调用
应该是每位程序员所熟知的。
语言有很多优点,越来越多的人开始使用它来编程。但是,
还有一些不足,如对不少的底层操作是无能为力的,只能通过调用
;另外,一般认为
程序的保密性不够强,因为它容易被
反编译而得到部分源码,所以需要使用混合编程加强
程序的保密性,而把
程序并实现动态调用的方法是比较理想的方法,因为可以把
文件先用某一进行加密甚至压缩后再作为资源文件添加到
程序中,在程序运行时才用某一算法进行解压解密后才进行加载,所以即使用反编译软件,也只能得到一个资源文件,且这个资源文件是用一个复杂算法进行加密过的,不可能再次对资源文件中的内容进行反编译,从而大大加强了代码的保密性。
参考文献:
》,网址:
Jason Clark
《深入分析
动态库应用异同》刘世栋
程序设计》
Jesse Liberty
中国电力出版社
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=568096
【上篇】【下篇】

我要回帖

更多关于 程序进行编译时,不为形式参数 的文章

 

随机推荐