1.1、解决方案的结构简介
在.NET中,解决方案是管理各个项目,每一个非网站项目都对应着一个命名空间,网站则是没有命名空间的,里面全是类。项目是管理各种类的,命名空间里面就是各种类。在VS.NET2003中,新建一个空的解决方案,然后在解决方案里面添加项目,一般添加的项目类型主要有以下几种:windows应用程序、控制台应用程序、类库、Web应用程序、网站等。
1.1.1、windows应用程序
Windows应用程序即可执行程序,在新建了windows应用程序后,它编译会生成exe,因为exe文件是可执行的,那么在这个windows应用程序中就会有一个入口的main函数。在VS.NET2003的windows应用程序中,会有一个默认的窗体,默认窗体中有一个main函数,这个函数是启动函数,这个函数可以放在该项目的别的类里面去,系统会自动搜索这个main函数作为启动的函数。注意:一个项目里面不允许有多个main函数,只能有一个,即一个项目中只能有一个程序入口,一般main函数的位置是不会变的,在默认的窗体中的。
在一个解决方案里面,可以添加任意多个windows应用程序。可以单个编译调试windows应用程序,这个windows应用程序可能已经添加了某个自定义的类库,那么在编译或者调试的某个windows应用程序的过程中,还会编译与该windows应用程序相关的类库,其它的都不会编译;也可以单独编译一个类库,如果在该类库里面添加了别的类库,也会连同别的类库一起编译,那么其他的类库和windows应用程序就不会编译。
一个解决方案里面如果有多个windows应用程序,那么解决方案经过编译之后,就会生成多个exe文件,每个项目里面都会有main函数的,都可以单独的执行,在一个解决方案里面会选择一个windows应用程序作为启动的项目。一般在一个解决方案里面只有一个windows应用程序,而且这个就是启动项目。如果有多个,那么也只能设置一个启动项目,其他的项目可以编译成exe,可以单独的执行。
1.1.2、控制台应用程序
控制台程序跟windows应用程序基本是一样的,是同一个级别,都是可执行文件,可以在一个解决方案里面与windows应用、类库共存。唯一不同的是,main函数默认在一个非窗体的的类里面的,而且是空的,什么也不做。
1.1.3、类库
类库是非可执行的,它主要是包含公用的一些类,被其他的项目调用的。类库编译后生成的是不可执行的dll文件,而非可执行的exe文件,是不能作为启动项的,可以添加任意多个类库。既然不可执行,那么也就没有main函数的概念了,因为main函数是可执行程序的入口。类库中是可以包括窗体的,因为.Net中的窗体也是一个类。
1.1.4、Web应用程序
Web应用程序与网站是差不多的,里面主要是可以添加一些WEB页面,可以将其设置成为一个启动项目,然后将其中的一个Web页面设为启动页面,那么就可以运行网站了,但是它与网站不同,它编译之后可以生成一个dll文件,这个有利于网站程序的发布。它是有命名空间的。
1.1.5、网站
网站也是由解决方案添加和管理的,但是它没有命名空间的,里面全是类,可以将其设置为启动项,然后设置一个其内的页面为启动页面,那么一样可以运行网站了,但是它编译之后既不会生成exe文件也不会生成dll文件,所以发布的时候要发布整体的文件,没有Web应用程序方便。
1.2、与项目相关的操作
1.2.1、项目输出类型
每一个项目都有一个重要属性,即输出类型,可选择windows应用程序输出类型、类库输出类型、控制台应用程序输出类型:
windows应用程序的默认值就是windows应用程序输出类型,这个会生成exe文件;类库的默认值就是类库输出类型,这个会生成dll;控制台应用程序默认就是控制台应用程序输出类型,这个会生成exe文件。
Windows应用程序也可以选择类库输出类型,可以保留mian也可以不保留,编译会成为dll,但是编译成为dll之后不能像exe一样的执行,只能被调用,而且不能再被设为启动项了。一般都会舍去main函数,因为作为dll,被别的项目调用的时候,别的项目是不会使用其中的main函数的。
类库是不能选择windows应用程序输出类型,因为如果选择windows应用程序输出类型,就会被编译成为exe,那么就要有入口main函数,可是类库是没有的,所以不能选择windows应用程序输出类型,否则编译会报错。但是如果在类库里面添加了main函数,那么就可以选择选择windows应用程序输出类型编译生成exe文件了。
注意:虽然exe项目与dll项目可以通过添加main函数以及修改输出类型来相互转换,但是一般都不会去这么做,全部按照默认的来操作。
1.2.2.、在解决方案中添加项目
在解决方案中可以新建类库、windows应用程序、控制台应用程序、web应用程序、网站,也可以添加现有的项目。有时候,有些项目是已经写好了的存在的,那么一般都会把这个项目直接拷贝到本解决方案里面,然后去添加项目,那么就可以在程序中看到这些项目了。
在某一个具体的项目中,可以有很多的文件夹,文件夹里面还可以有文件夹,那么在别的项目中要引用这个项目下面的某个文件夹下面的类,就先要添加引用,然后:
A、using项目的命名空间.文件夹名1.文件夹名11
B、项目的命名空间.文件夹名1.文件夹名11.类名
1.2.3.、在项目中添加引用
添加引用一般是指的dll,主要有两种方式:
1、添加本解决方案中的dll项目,然后编译之后,dll项目会生成dll文件,这个dll文件最终会被拷贝到引用这个dll项目的项目的exe文件同一目录下面,在引用这个dll项目的项目中,在程序里面,要using那个dll项目的命名空间。
编译引用项目的时候,被引用的dll项目变化了,被引用的dll项目的obj\debug下面的dll会被更新,引用这个dll项目的项目中bin\debug下面的dll文件也会被更新。
2、添加本解决方案以外的dll文件,但是仍然是.NET编译的dll,直接引用,然后编译本项目之后,dll文件也会拷贝到本项目的exe文件同一目录下面,在引用这个dll文件的项目中,在程序里面,要using那个dll的命名空间。
固定地方的dll项目变化了,编译本项目之后,本地的dll一样会被更新。
注意:在编译的时候,要保证引用的源dll项目或者dll文件是存在的。最后执行exe的时候,只需要保证拷贝过来的与exe同目录的dll文件是存在的。
1.3、详细讲解关于dll的引用
每一个exe的项目在解决方案的文件夹下面都会对应着一个该项目的文件夹,然后该文件夹下面有bin\debug和obj\debug,第一次debug这个项目的时候,在这两个文件夹下面会同时生成exe文件,最终执行的是bin\debug下面的exe,如果要添加配置文件,比如ini、config等要考虑相对路径的时候,就要考虑到bin\debug文件夹,因为运行的就是这个文件夹下面的exe。对于dll项目也是如此,只不过最后生成的是dll文件。以后再次debug这个项目的时候,如果项目发生了变更,就会更新bin\debug和obj\debug里面的文件,否则不会更新。
当一个项目要使用另外一个项目的类的时候,一般就是主项目要使用子项目中的类的时候,就要在主项目中添加引用,引用别的项目,其实添加引用的实质是添加了obj\debug下面的dll。有以下几种方式:
(1)、先创建那些没有调用别的项目的dll,在bin\debug和obj\debug中如果存在而且源代码更新了就更新,源代码没更新就不会去更新;不存在就会创建。很显然,dll文件是已经编译好了的文件。然后开始编译exe文件,首先会根据引用的项目到原项目的obj\debug中搜索出dll来,如果是在原dll是第一次创建的时候,在本地引用了原项目,那么在本地的bin\debug中也会第一次复制原obj\debug下的dll;如果原项目创建dll的时候,本地还没引用原项目,那就要到以后引用了才复制到本地;在本地复制了dll之后,如果所有项目重新编译,原来的dll如果没有更新,本地也不会更新,如果原来的dll更新了,那么本地的也会更新的;如果原来的dll项目不编译了,而且原项目中obj\debug下面没有了dll文件,本exe项目又引用了原项目(实质是引用了原dll项目中的obj\debug下面的dll文件,而不是原项目中bin\debug下面的),那么就会引用失败,而且如果本地有这个dll,引用失败之后还会将本地的bin\debug下面原项目中的dll删除,然后报错,如果本地本来就没有更会直接报错;如果原来的dll项目不编译了,如果原项目的obj\debug下面的dll文件存在,那么本地的会去判断,如果本地不存在,那么就复制到本地,如果本地存在,就会判断原dll与本地dll是否为同一个dll,不同才去更新本地的dll。根据using命名空间,如果命名空间下有文件夹,那么就是using 命名空间.文件夹名,这个就是引用当前目录下的dll,然后调用dll中的函数,这样编译就可以通过了,然后创建或者更新exe在本地的bin\debug和obj\debug里面。
(2)、有一个dll1项目中引用了别的dll2项目,先编译dll2项目,然后生成了dll2项目中的bin\debug和obj\debug文件夹下的dll2文件,然后dll1编译的时候,首先根据dll1中的引用来调用dll2的obj\debug文件夹下的dll2文件来更新本地的bin\debug对应的dll2文件,然后using命名空间,就以本地的来编译了,然后生成本地的bin\debug中的dll1;那么该dll1项目再次被exe引用的时候,exe编译的时候首先会让dll1、dll2来更新本地的bin\debug下面的对应dll或者创建,编译生成exe的时候就是以本地的dll为主了。
(3)、如果指定的是别的解决方案里面的项目的dll,只有dll没有源代码,那么只需要把这个dll放在一个公共的dll文件夹下面,然后在本exe项目中引用,编译时还是先把该dll在本地的bin\debug下面更新或者复制,然后using命名空间,然后以本地的dll来编译生成exe文件。如果原来的dll更新了,本地也一定会更新,因为本地的exe编译的时候先编译添加引用,把原来的dll添加到本地,如果原来的dll存在,那么就在本地创建或者更新,如果原来的dll不存在,如果本地没有就报错,如果本地有dll,也会删除这个dll,仍然报错。
(4)、各个项目之间都是相互独立的,如果只编译exe,不编译其他的dll,那么如果在编译exe的时候,要去引用别的dll到本地,如果原dll能找到,那么就创建本地或者更新本地的dll,如果别处的dll找不到,本地有dll,也会删除这个dll,仍然报错,如果别的找不到,本地没有,那么编译exe就会报错;如果编译其他的dll就是会更新他们的dll文件从而使exe文件编译的时候能够引用到最新的dll。
(5)、一旦exe编译完成,生成了exe文件,那么就会以本地的dll为主了,使用的就是本地的dll了。发布的时候exe就和dll一起发布,以后修改只会修改某个dll或者exe了。.Net生成exe的时候先会在本地生成引用的各种dll,再把本项目中的文件打包生成exe。整个程序的运行还需要。Net框架的支持,因为系统函数全部在。Net框架中。以后exe在执行的时候就直接调用本地的dll即可。所以在安装环境中,只看到本地的exe和本地的dll,其他都不需要。
(6)、编译项目的时候,第一次编译会在bin\debug和obj\debug里面生成文件dll或者exe,第二次编译会更新这两个文件,但是如果将obj\debug里面的文件删除,保留bin\debug里面的文件,编译将失败,如果删除bin\debug里面的文件而不删除obj\debug里面的就会成功。两个都删除也会成功。
(7)、其实在引用别的dll项目的时候,可以选择引用的属性为复制到本为true或者false,一般为true,那么所有的dll将会赋值到本地的exe同目录下,以后就以本目录下的dll为主。也可以为false,那么引用的就是原来位置的dll,不会复制到本地,如果在本地的exe项目中只是using了,但是没有调用原dll中的函数,编译还是可以通过的,但是如果调用了函数,编译就会失败。所以一般还是默认为true比较好。
小结:当项目引用源dll的时候,编译的时候,源dll一定要存在,否则会报错,编译的时候,会用源dll来更新本地的dll,执行的时候,源dll存在与否不关心,因为源dll在项目编译之后已经拷贝到本项目的exe同一级目录下面,执行的时候只用考虑本地的dll即可。
1.4、托管的dll和非托管的dll
系统在编译的时候,只关心源头的dll文件,所以编译的时候源头的dll一定要存在,编译好了之后,所有的dll都会拷贝到与可执行文件exe同一级的目录下面。执行exe的时候,调用的是本地的dll。
在.NET平台下面,不管是在哪个机器上编写的dll,也不管是用.NET 2003还是2005、2008平台,也不管是用.NET中的哪一种语言,VB或者C#,只要编写出了dll,那么对于.NET平台来说就是托管的dll,某个项目要使用这个dll,只需要添加引用,然后using,即可使用了。
但是如果在别的平台下面用某种语言编译生成的dll,比如用DELPHI、在VS6.0下面用C++编译生成的dll,对于.NET平台来说就是非托管的dll了。非托管的dll是无法被添加引用的,添加引用的时候会出错,它可以拿过来,直接执行就可以了,所以它必须被拷贝到与exe同一级目录下面。然后:
using System.Runtime.InteropServices;
[DllImport("IT3CW32D.Dll")]
public static extern int AbortIt3c();
注意:
(1)、非托管dll不像托管的dll,开发人员自己就知道托管的dll中的函数和属性,但是一般不知道非托管dll中的函数和属性,所以在使用的时候,一定都是有接口文档的,在文档中会对函数进行详细的说明。
(2)、在一个类里面,首先要using System.Runtime.InteropServices,然后再来引用函数:
在每一个函数引用的时候,都要写一次[DllImport("IT3CW32D.Dll")],然后下面声明的函数就是出自这个dll。因为有可能一个项目同时引用了多个dll,而且有某个同名、同参、同返回值的函数分别出现在了这几个dll中,但是函数功能各不相同,那么如果没有上面那句话,就没法确定声明的是哪个dll中的函数。所以一定要加上DllImport,来确定每一个函数的出处。另外,也不能一个[DllImport("IT3CW32D.Dll")]下面多个函数。
(3)、在一个类里面一个函数引用一次就可以了,重复的引用会出错。
(4)、IT3CW32D.Dll表示的是dll文件的名字和后缀。
(5)、extern修饰符用于声明在外部实现的方法。extern 修饰符的常见用法是在使用 Interop服务调入非托管代码时与 DllImport 属性一起使用;在这种情况下,该方法还必须声明为 static;修饰符public表示不仅可以在本类中使用,也可以在别的类中通过类名来调用。也可以private,那么只能在本类中使用了。
1.5、关于解决方案、项目、类的名字
(1)、解决方案
解决方案是管理各个项目的,一个解决方案里面可以包含一个或者多个不同类型的项目。解决方案名是sln名,解决方案会有一个文件夹,默认情况下解决方案名字与解决方案的文件夹名字相同。注意:解决方案名字可以任意的修改,这个会导致sln名字修改,解决方案文件夹名字亦可任意的修改,两者相互独立,但是一般不要去修改,默认相同。
(2)、项目
项目是解决方案的下一级,项目是以命名空间为单位的。在默认情况下,项目名、命名空间名、项目文件夹名、程序集名都是相同的。
项目名称是可以修改的,是csproj名,如果是类库,那么被别的项目添加引用的时候,选择【项目】,引用【项目名称】就是这个项目名。
命名空间名称在类文件中,是可以任意修改的,如果是类库,别的项目using的时候,就是这个类文件中的命名空间的名字。在项目的属性中,有一个【默认命名空间】,在这里设置之后,如果在这个项目中新增类文件,那么类文件的命名空间就是这个【默认命名空间】,当然可以自由的在类文件中修改。所以一个项目中,各个类文件的命名空间是可以不一样的,最终不会以默认的命名空间为准,而是以类文件中最终命名空间为准。但是一般都不会去修改命名空间,默认都是相同的即可。
程序集名称也是可以任意修改的,这个名字是项目最终编译输出的exe或者dll文件名,如果是dll,那么别的项目添加引用的时候,点击【浏览】就可以选择这个dll文件。
项目文件夹名称不可以修改,一旦修改,解决方案就无法无法访问该项目了。
注意:以上这几个名称都是相互独立没有任何关系的,但是一般都不会去修改名字,默认所有的都相同。
(3)、类名
类是项目的下一级,资源管理器里面的类名是cs文件名,可以任意的修改。程序里面的类名才是真正的类名,是程序中定义对象的名字。
总结:以上就是.NET 2003解决方案的框架,其实在.NET 2005中也是如此,基本一样,唯一不一样的是项目里面的main函数在默认的program类里面,其实可以删除这个类,把main函数放到窗体中去,也一样,项目会自动搜索这个启动函数,只要一个exe项目中只有一个main就可以。
2009-2-6---2009-2-10