雷's profileAbbeyGong's SpacesPhotosBlog Tools Help

Blog


    9/24/2008

    什么是美国次贷危机,看后你就明了

    在美国,贷款是非常普遍的现象,从房子到汽车,从信用卡到电话账单,贷款无处不在。当地人很少全款买房,通常都是长时间贷款。可是我们也知道,在这里失业和再就业是很常见的现象。这些收入并不稳定甚至根本没有收入的人,他们怎么买房呢?因为信用等级达不到标准,他们就被定义为次级贷款者。
    大约从10年
    前开始,那个时候贷款公司漫天的广告就出现在电视上、报纸上、街头,抑或在你的信箱里塞满诱人的传单:
    “你想过中产阶级的生活吗?买房吧!”
    “积蓄不够吗?贷款吧!”
    “没有收入吗?找阿牛贷款公司吧!”
    “首付也付不起?我们提供零首付!”
    “担心利息太高?头两年我们提供3%的优惠利率!”
    “每个月还是付不起?没关系,头24个月你只需要支付利息,贷款的本金可以两年后再付!想想看,两年后你肯定已经找到工作或者被提升为经理了,到时候还怕付不起!”
    “担心两年后还是还不起?哎呀,你也真是太小心了,看看现在的房子比两年前涨了多少,到时候你转手卖给别人啊,不仅白住两年,还可能赚一笔呢!再说了,又不用你出钱,我都相信你一定行的,难道我敢贷,你还不敢借?”
    在这样的诱惑下,无数美国市民毫不犹豫地选择了贷款买房。(你替他们担心两年后的债务?向来自我感觉良好的美国市民会告诉你,演电影的都能当上州长,两年后说不定我还能竞选总统呢。)
    阿牛贷款公司短短几个月就取得了惊人的业绩,可是钱都贷出去了,能不能收回来呢?公司的董事长——阿牛先生,那也是熟读美国经济史的人物,不可能不知道房地产市场也是有风险的,所以这笔收益看来不能独吞,要找个合伙人分担风险才行。于是阿牛找到美国经济界的带头大哥——投行。这些家伙可都是名字响当当的主儿(美林、高盛、摩根),他们每天做什么呢?就是吃饱了闲着也是闲着,于是找来诺贝尔经济学家,找来哈佛教授,用上最新的经济数据模型,一番鼓捣之后,弄出几份分析报告,从而评价一下某某股票是否值得买进,某某国家的股市已经有泡沫了,一群在风险评估市场里面骗吃骗喝的主儿,你说他们看到这里面有风险没?用脚都看得到!可是有利润啊,那还犹豫什么,接手搞吧!于是经济学家、大学教授以数据模型、老三样评估之后,重新包装一下,就弄出了新产品——CDO(注: Collateralized Debt Obligation,债务抵押债券),说穿了就是债券,通过发行和销售这个CDO债券,让债券的持有人来分担房屋贷款的风险。
    光这样卖,风险太高还是没人买啊,假设原来的债券风险等级是6,属于中等偏高。于是投行把它分成高级和普通CDO两个部分,发生债务危机时,高级CDO享有优先赔付的权利。这样两部分的风险等级分别变成了4和8,总风险不变,但是前者就属于中低风险债券了,凭投行三寸不烂“金”舌,当然卖了个满堂彩!可是剩下的风险等级8的高风险债券怎么办呢?
    于是投行找到了对冲基金,对冲基金又是什么人,那可是在全世界金融界买空卖多、呼风唤雨的角色,过的就是刀口舔血的日子,这点风险小意思!于是凭借着老关系,在世界范围内找利率最低的银行借来钱,然后大举买入这部分普通CDO债券,2006年以前,日本央行贷款利率仅为1.5%;普通CDO利率可能达到12%,所以光靠利息差对冲基金就赚得盆满钵满了。
    这样一来,奇妙的事情发生了,2001年末,美国的房地产一路飙升,短短几年就翻了一倍多,这样一来就如同阿牛贷款公司开头的广告一样,根本不会出现还不起房款的事情,就算没钱还,把房子一卖还可以赚一笔钱。结果是从贷款买房的人,到阿牛贷款公司,到各大投行,到各个银行,到对冲基金人人都赚钱,但是投行却不太高兴了!当初是觉得普通CDO风险太高,才扔给对冲基金的,没想到这帮家伙比自己赚的还多,净值一个劲地涨,早知道自己留着玩了,于是投行也开始买入对冲基金,打算分一杯羹了。这就好像“老黑”家里有馊了的饭菜,正巧看见隔壁邻居那只讨厌的小花狗,本来打算毒它一把,没想到小花狗吃了不但没事,反而还越长越壮了,“老黑”这下可蒙了,难道馊了的饭菜营养更好,于是自己也开始吃了!
    这下又把对冲基金乐坏了,他们是什么人,手里有1块钱,就能想办法借10块钱来玩的土匪啊,现在拿着抢手的CDO还能老实?于是他们又把手里的CDO债券抵押给银行,换得10倍的贷款,然后继续追着投行买普通CDO。嘿,当初可是签了协议,这些CDO都归我们的!!!投行心里那个不爽啊,除了继续闷声买对冲基金之外,他们又想出了一个新产品,就叫CDS

    (注:Credit Default Swap,信用违约交换)好了,华尔街就是这些天才产品的床:不是都觉得原来的CDO风险高吗,那我投保好了,每年从CDO里面拿出一部分钱作为保金,白送给保险公司,但是将来出了风险,大家一起承担。
    保险公司想,不错啊,眼下CDO这么赚钱,1分钱都不用出就分利润,这不是每年白送钱给我们吗?干了!
    对冲基金想,不错啊,已经赚了几年了,以后风险越来越大,光是分一部分利润出去,就有保险公司承担一半风险,干了!
    于是再次皆大欢喜,CDS也卖火了!但是事情到这里还没有结束:因为“聪明”的华尔街人又想出了基于CDS的创新产品!我们假设CDS已经为我们带来了50 亿元的收益,现在我新发行一个“三毛”基金,这个基金是专门投资买入CDS的,显然这个建立在之前一系列产品之上的基金的风险是很高的,但是我把之前已经赚的50亿元投入作为保证金,如果这个基金发生亏损,那么先用这50亿元垫付,只有这50亿元亏完了,你投资的本金才会开始亏损,而在这之前你是可以提前赎回的,首发规模500亿元。天哪,还有比这个还爽的基金吗?1元面值买入的基金,亏到0.90元都不会亏自己的钱,赚了却每分钱都是自己的!评级机构看到这个天才设想,简直是毫不犹豫:给予AAA评级!
    结果这个“三毛”可卖疯了,各种养老基金、教育基金、理财产品,甚至其他国家的银行也纷纷买入。虽然首发规模是原定的500亿元,可是后续发行了多少亿,简直已经无法估算了,但是保证金50亿元却没有变。如果现有规模5000 亿元,那保证金就只能保证在基金净值不低于0.99元时,你不会亏钱了。
    当时间走到了2006年年底,风光了整整5年的美国房地产终于从顶峰重重摔了下来,这条食物链也终于开始断裂。因为房价下跌,优惠贷款利率的时限到了之后,先是普通民众无法偿还贷款,然后阿牛贷款公司倒闭,对冲基金大幅亏损,继而连累保险公司和贷款的银行,花旗、摩根相继发布巨额亏损报告,同时投资对冲基金的各大投行也纷纷亏损,然后股市大跌,民众普遍亏钱,无法偿还房贷的民众继续增多……最终,美国次贷危机爆发。

    9/19/2008

    “老头子,我要走了,抱抱我吧。”

    老太太醒过来了,心脏跳的忽快忽慢的,让她有些吃不消了。
    老太太就想:差不多喽,自己要走,也就在这一两天喽。
    老太太已经76岁了,身体倒还好,只是今年,大冷大热,对他们这些老年人,是很致命的伤害呢。这不,自己就觉得从春节后,身体一天不如一天了。
    老太太转头,看见旁边的暖椅上,躺着自己78岁的老头子,心里,稍稍安慰了些。
    太阳暖暖的,正在向天边垂落,老太太就想起了和老头子,这一辈子的时光。
    年轻时候,老太太是四邻八乡有名的美人儿。说媒的人,踏破了她家好几块门槛。可是,可是她早就心有所属。她,看中了村中那个小学校里,唯一的教书先生。
    那是个斯斯文文的年轻人,长着很好看的一双眼睛,看着你的时候,满满的笑,让人就心醉的不行。
    两个人曾经多次在村中的小道上迎面走过,都只是短短的对视一眼,然后双双红了脸,低了头,匆匆的擦肩而过。短短的相遇,却是两个人,最幸福的期待。
    谁知那一年,她的父亲去外面采办年货,回来时遇到了土匪,危急关头,被一个五大三粗的过路客,舍命救了下来,还替父亲挨了深深的一刀。
    在她家里养伤的时候,她在床前端茶递饭,完全是出于报答这个陌生男人,对父亲的救命之恩。
    等到这个汉子伤势渐好的时候,这个汉子就开始忙里忙外的,几乎包揽了所有的农活和家务活。别看他粗枝大叶的样子,竟是个全能手,洗衣做饭,田间地头,春耕夏种,修修弄弄,竟没有他不会的活计,把她的父母给欢喜的不行,就经常陶醉在四邻的夸奖和羡慕声中。
    这让她非常心焦,因为她在一个晚上,偶然在父母的门外,听到了父母亲,有意要招这个汉子入赘。她就软软的靠在门边,没了主意。
    第二天,故意去那条和教书先生经常偶遇的巷子,徘徊了很久,都没有见到。后来问了村里的一个孩子,才知道那个教书先生,已经回城多日,说是家中有事,要三个月后,才能回来。
    那个教书先生再回来的时候,匆匆的跑到她家门口,就看到了她家门上,醒目而刺眼的大红喜字,看见了院子里,一身红衣,满眼幽怨的她。
    从那天起,那个教书先生,就彻底的消失在她的生活中。
    后来,后来就跟着那个汉子,安安心心的过起了日子。
    新中国成立,三年自然灾害,十年文革,改革开放,风风雨雨,雨雨风风。两个人从农村来到城市,相依为命,相互扶持,生儿育女,开枝散叶,就到了现在,老态龙钟的样子。
    不容易,实在不容易啊!
    老太太这样想着,胸中有些发闷,就咳嗽起来,惊醒了一旁午睡的老头子。
    那老头子赶紧起身,关切的看着老太太,就手到了一杯水。老太太就捧了暖暖的水杯,看着自己的男人,想自己,和这个男人过了这一辈子,还有什么遗憾吗?好象没有吧?
    这个男人,心思实在细腻的可以。对这个家,也实在没话可说。再苦再难,都把她们娘几个,照顾的妥妥当当的。两个人虽然在一起,极少有什么话,却有着多年培养出来的默契。有时候,就默默的坐在一起,手握着手,什么也不说,都能静静的,坐上那么一天。
    老太太就想起老头子为了这个家,付出的一切。
    还记得一年秋天,二小子要上学,学费成了问题,家里也好久没有见到荤腥了。老头子就在屋子里坐了很久,然后起身说,去找人借。找谁借?其时他们在那个城市,一个亲戚也没有。寥寥的几家朋友,也都是一穷二白。谁知到了傍晚,老头子果然就带回来了儿子的学费,手里还破天荒的拎了一只活鸡!
    那个晚上,一家人,暖暖活活的在一起,好象过年一样的快乐。
    可是,可是她却在晚上给老头子换衣服时,发现了袖弯里,有淡淡的一点血迹。就赶紧去看熟睡中老头子的胳膊,就看见了他肘弯处,一个醒目的针眼,还有好大一片淤青。
    啊!这个汉子!这个男人!这个老头子!!
    为了这个家,也是一身的病了。快八十岁的人了,却每天依旧忙忙碌碌的,仿佛是一台不知疲倦为何物的机器。
    而自己,自己当初嫁给他的时候,是多么多么的伤心,多么多么的不情愿啊。现在牵手走了这么多年,却只有他一直陪在自己身边,不离不弃,始终如一。
    老太太这样想着,眼睛里就渐渐的潮湿起来。忽然,就有些孩子气,就轻声的问眼前这个男人:“老头子,说说看,如果有下辈子,还愿意和我做夫妻吗?”
    老头子被老太太这个突兀的问题,弄的愣了一下,就展开满脸的核桃纹,笑的很神秘:“不一定喽,如果下辈子,我托生成了大官财主,就去找你,让你好好的跟我享享福。如果,如果还是这么穷,就不喽,就帮着你,帮着你找一个有钱的人家。我呢,我就在你家附近,远远的看着你,只要你能过得好,就成了。”
    老太太很感动,就幸福的笑着说:“你个臭老头子,还在我家附近,在我家附近干什么?”
    老头子就转头,认认真真的看着心爱的女人,认认真真的说:“不干什么,就,就做个教书先生吧。”
    老太太就突然愣住了,哀伤地看着这个和自己共渡了一生的男人。想说什么,却什么也说不出来,眼里的泪,却无休无止的流了下来。
    过了很久,老太太深情的说:“老头子,我要走了,抱抱我吧。”
    老头子就慢慢的起了身,轻轻的,轻轻的把老太太搂在怀里。老太太就在老头子耳边,呢喃着说:“老头子,下辈子,咱,还做夫妻啊……
    老太太和老头子的小孙女儿,放学回家的时候,看到夕阳西下,火红的霞光,将老头子和老太太满满的笼罩在一起。就说:“羞羞,爷爷,奶奶,看不出你们还这么浪漫啊。”于是惊讶地发现,老太太和老头子,幸福的相拥着,已经双双去了。
    相濡以沫,相爱一生,平淡却不平凡,爱不一定要轰轰烈烈,一样可以感人至深荡气回肠,希望许多年以后,我也能这样离开这个世界

    9/17/2008

    sqlserver 提取一定范围记录

    select top N * from table
    前N条数据
    select top N * from table where id in
    (select top M id from table order by id desc)
    第M-N条 到第M条数据

    9/13/2008

    MCP68 主板开启AHCI模式攻略

    1、在SATA普通状态安装Windows XP(或Windows 2003)。
    2、安装成功后在设备管理器中做如下操作:
       1)在磁盘驱动器中找到“硬盘”,双击显示属性框->进入详细信息选项卡->记下“设备范例ID“
       2)在IDE ATA/ATAPI控制器中的四“主要IDE通道“与”次要IDE通道“中的->属性->详细信息->总线关系 中找到与刚才对应的设备范例ID,然后记下这个IDE通道的设备范例ID.
       3)在IDE ATA/ATAPI控制器中有两个“标准双通道PCI IDE控制器“,在其中找到->详细信息->总线关系,其中会有一个与上面记下的"IDE通道的设备范例ID"相同。
    3、通过上面第2大步的三个步骤已经找到了SATA控制器,它的设备范例ID可能会是“PCIVEN_10DE&DEV_0550&SUBSYS_CB8410DE&REV_A2..."
    4、将七彩虹安装盘放入光驱,在上面找的“标准双通道PCI IDE控制器“上按鼠标右键-->更新驱动程序-->从列表或指定位置安装-->浏览到:光驱: vbitmcp68XP IDEWinXPsata_ide ,更新驱动程序为:NVIDIA nForce...。完成后重启。
    5、进入BIOS,把STATA改成AHCI模式,启动系统。
    6、系统启动后会提示找到新硬件,等待它完成即可。

    9/6/2008

    英文地址翻译

    翻译原则:先小后大。
    中国人喜欢先说大的后说小的,如 ** 区 ** 路 ** 号
    而外国人喜欢先说小的后说大的,如 ** 号 ** 路 ** 区,因此您在翻译时就应该先写小的后写大的。
    例如:中国广东深圳市华中路 1023 号 5 栋 401 房,您就要从房开始写起, Room 401, Buliding 5, No.1023, HuaZhong Road , ShenZhen , GuangDong Prov., China (逗号后面有空格)。注意其中路名、公司名、村名等均不用翻译成同意的英文,只要照写拼音就行了。因为您的支票是中国的邮递员送过来,关键是要他们明白。技术大厦您写成 Technology Building ,他们可能更迷糊呢。
    现在每个城市的中国邮政信件分拣中心都有专人负责将外国来信地址翻译成中文地址 , 并写在信封上交下面 邮递员 送过来 .
    重要 : 你的邮政编码一定要写正确 , 因为外国信件中间的几道邮政环节都是靠邮政编码区域投递的。
    常见中英文对照:
    *** 室 / 房
    Room ***
    *** 村
    *** Vallage
    *** 号
    No. ***
    *** 号宿舍
    *** Dormitory
    *** 楼 / 层
    *** /F
    *** 住宅区 / 小区
    *** Residential Quater
    甲 / 乙 / 丙 / 丁
    A / B / C / D
    *** 巷 / 弄
    Lane ***
    *** 单元
    Unit ***
    *** 号楼 / 栋
    *** Building
    *** 公司
    ***Com./*** Crop/***CO.LTD
    *** 厂
    *** Factory
    *** 酒楼 / 酒店
    *** Hotel
    *** 路
    *** Road
    *** 花园
    *** Garden
    *** 街
    *** Stree
    *** 信箱
    Mailbox ***
    *** 区
    *** District
    *** 县
    *** County
    *** 镇
    *** Town
    *** 市
    *** City
    *** 省
    *** Prov.
    *** 院
    ***Yard
    *** 大学
    ***College
    ** 表示序数词,比如 1st 、 2nd 、 3rd 、 4th ……如果不会,就用 No.*** 代替,或者直接填数字吧!
    另外有一些 *** 里之类难翻译的东西,就直接写拼音 *** Li 。而 *** 东(南、西、北)路,直接用拼音也行,写 *** East ( South 、 West 、 North ) Road 也行。还有,如果地方不够可以将 7 栋 3012 室写成: 7-3012 。
    201 室 : Room 201
    12 号 : No.12
    2 单元 : Unit 2
    3 号楼 : Building No.3
    长安街 : Chang An street
    南京路 : Nanjing road
    长安公司 : Chang An Company
    宝山区 : BaoShan District
    赵家酒店 : ZhaoJia hotel
    钱家花园 : Qianjia garden
    孙家县 : Sunjia county
    李家镇 : Lijia town
    广州市 : Guangzhou city
    广东省 : Guangdong province
    中国 : China
    实例:
    宝山区南京路 12 号 3 号楼 201 室
    room 201 , building No.3,No.12 , nan jing road , BaoShan District
    如果地方不够可以将 3 号楼 201 室写成: 3-201
    宝山区示范新村 37 号 403 室
    Room 403,No.37,SiFan Residential Quarter,BaoShan District
    中华人民共和国民政部政策研究中心北京河沿大街 147 号
    No.147# HeiYian Street the policy center of civil administration department the People'Republic of China
    虹口区西康南路 125 弄 34 号 201 室
    Room 201,No.34,Lane 125, XiKang Road (South),HongKou District
    北京市崇文区天坛南里西区 20 楼 3 单元 101
    Room 3-101 building No.20,TianTan-NanXiLi Residential ChongWen District BeiJing City
    中国新疆喀什市东苑小区3号楼1单元501室
    Room 1-501,Buliding No.3,Dongyuan Residential,Kashi city,Xinjiang ,China
    江苏省扬州市宝应县泰山东村 102 栋 204 室
    Room 204 building No.102, East TaiShan Residential BaoYin County JiangSu Province
    473004 河南省南阳市中州路 42 号 周旺财
    Zhou Wangcai
    Room 42 ,
    Zhongzhou Road , Nanyang City ,
    Henan Prov.China 473004
    中国四川省江油市川西北矿区采气一队
    1 Team CaiQi ChuanXiBei Mining Area JiangYou City SiChuan Province China
    中国河北省邢台市群众艺术馆
    The Masses Art Centre XinTai City HeBei Prov. China
    江苏省吴江市平望镇联北村七组
    7 Group LiBei Village PingWang Town WeJiang City JiangSu Province
    434000 湖北省荆州市红苑大酒店 周旺财
    Zhou Wangcai
    Hongyuan Hotel,
    Jingzhou city,
    Hubei Prov. China 434000
    473000 河南南阳市八一路 272 号特钢公司 周旺财
    Zhou Wangcai
    Special Steel Corp , No.272 ,
    Bayi Road , Nanyang City ,
    Henan Prov. China 473000
    528400 广东中山市东区亨达花园 7 栋 702 周旺财
    Zhou Wangcai
    Room 702, 7th Building,
    Hengda Garden, East District,
    Zhongshan, China 528400
    361012 福建省厦门市莲花五村龙昌里 34 号 601 室 周旺财
    Zhou Wangcai
    Room 601, No.34 Long Chang Li,
    Xiamen, Fujian, China 361012
    361004 厦门公交总公司承诺办 周旺财
    Mr. Zhou Wangcai
    Cheng Nuo Ban, Gong Jiao Zong Gong Si
    Xiamen, Fujian, China 361004
    266042 山东省青岛市开平路 53 号国棉四厂二宿舍 1 号楼 2 单元 204 户甲 周旺财
    Mr. Zhou Wangcai
    NO. 204, A, Building NO. 1,
    The 2nd Dormitory of the NO. 4 State-owned Textile Factory,
    53 Kaiping Road, Qingdao ,
    Shandong , China 266042

    9/3/2008

    动态链接库

    什么是动态链接库?DLL三个字母对于你来说一定很熟悉吧,它是Dynamic Link Library 的缩写形式,动态链接库 (DLL) 是作为共享函数库的可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个应用程序可同时访问内存中单个 DLL 副本的内容。

    和大多数程序员一样,你一定很使用过DLL吧。也曾感受到它的带给你程序设计和编码上的好错吧今天我想和大家探讨一个主题:如何在C#创建和调用DLL(动态链接库), 其实在很大意义上而讲,DLL让我更灵活的组织编写我们的应用程序,作为软件设计者,可一个根据它来达到很高的代码重用效果。下面我来介绍一下在C#中如何创建和调用DLL。

    二、准备工作

    我们需要对我们接下来要做的事情做个简单的介绍,在本文我们将利用C#语言创建一个名为 MyDLL.DLL的动态链接库,在这个动态链接库文件中我们将提供两个功能一个是对两个参数交换他们的值,另一个功能是求两个参数的最大公约数。然后创建一个应用程序使用这个DLL。运行并输出结果。

    三、创建DLL

    让我们创建以下三个C#代码文件:

    1、  MySwap.cs

    using System;

    namespace MyMethods

    {

         public class SwapClass

         {

              public static bool Swap(ref long i,ref long j)

              {

      i = i+j;

      j = i-j;

      i = i-j;

      return true;

              }

         }

    }

    2、  MyMaxCD.cs

    using System;

    namespace MyMethods

    {

         public class MaxCDClass

         {

              public static long MaxCD(long i, long j)

              {

      long a,b,temp;

      if(i>j)

      {

           a = i;

           b = j;

      }

      else

      {

           b = i;

           a = j;

      }

      temp = a % b;

      while(temp!=0)

      {

           a = b;

           b = temp;

           temp = a % b;

      }

      return b;

              }

         }

    }

    }需要注意的是:我们在制作这两个文件的时候可以用Visual Studio.NET或者其他的文本编辑器,就算是记事本也可以。这两个文件虽然不在同一个文件里面,但是他们是属于同一个namespace(名称空间)这对以后我们使用这两个方法提供了方便。当然他们也可以属于不同的名称空间,这是完全可以的,但只是在我们应用他们的时候就需要引用两个不同的名称空间,所以作者建议还是写在一个名称空间下面比较好。
    接下来的任务是把这两个cs文件变成我们需要的DLL文件。方法是这样的:
    在安装了Microsoft.NET Framework的操作系统上,我们可以在Windows所在目录下找到Microsoft.NET目录。在这个目录下面提供了C#的编译器,CSC.EXE
    运行:csc /target:library /out:MyDLL.DLL MySwap.cs MyMaxCD.cs
    完成后可在本目录下面找到我们刚才生成的MyDLL.DLL文件
    /target:library 编译器选项通知编译器输出 DLL 文件而不是 EXE 文件。后跟文件名的 /out 编译器选项用于指定 DLL 文件名。
    如果/out后面不跟文件名编译器使用第一个文件 (MySwap.cs) 作为 DLL 文件名。生成的文件为MySwap.DLL文件
    OK!我们创建动态链接库文件的任务完成了,现在是我们享受劳动成果的时候了,下面我将介绍如何使用我们所创建的动态链接库文件。
    四、   使用DLL
    我们简单写一个小程序来测试一下我们刚才写的两个方法是否正确,好吧,跟我来:
    MyClient.cs
    using System;

    using MyMethods;

    //这里我们引用刚才定义的名称空间,如果刚才的两个文件我们写在两个不同的名称空间
    class MyClient

    {

         public static void Main(string[] args)

         {

             if (args.Length != 2)

             {

    Console.WriteLine("Usage: MyClient <num1> <num2>");

    return;

             }

              long num1 = long.Parse(args[0]);

              long num2 = long.Parse(args[1]);

              SwapClass.Swap(ref num1,ref num2);

    // 请注意,文件开头的 using 指令使您得以在编译时使用未限定的类名来引用 DLL 方法

              Console.WriteLine("The result of swap is num1 = {0} and num2 ={1}",num1, num2);

              long maxcd = MaxCDClass.MaxCD(num1,num2);

              Console.WriteLine("The MaxCD of {0} and {1} is {2}",num1, num2, maxcd);

         }

    }

    若要生成可执行文件 MyClient.exe,请使用以下命令行:

    csc /out:MyClient.exe /reference:MyLibrary.DLL MyClient.cs

    /out 编译器选项通知编译器输出 EXE 文件并且指定输出文件名 (MyClient.exe)。/reference 编译器选项指定该程序所引用的 DLL 文件。

    五、执行

    若要运行程序,请输入 EXE 文件的名称,文件名的后面跟两个数字,例如:

    MyClient 123 456

    六、输出

    The result of s

    wap is num1 = 456 and num2 = 123

    The MaxCD of 456 and 123 is 3

    七、小结

    动态链接具有下列优点:

    节省内存和减少交换操作。很多进程可以同时使用一个 DLL,在内存中共享该 DLL 的一个副本。相反,对于每个用静态链接库生成的应用程序,Windows 必须在内存中加载库代码的一个副本。
    节省磁盘空间。许多应用程序可在磁盘上共享 DLL 的一个副本。相反,每个用静态链接库生成的应用程序均具有作为单独的副本链接到其可执行图像中的库代码。
    升级到 DLL 更为容易。DLL 中的函数更改时,只要函数的参数和返回值没有更改,就不需重新编译或重新链接使用它们的应用程序。相反,静态链接的对象代码要求在函数更改时重新链接应用程序。
    提供售后支持。例如,可修改显示器驱动程序 DLL 以支持当初交付应用程序时不可用的显示器。
    支持多语言程序。只要程序遵循函数的调用约定,用不同编程语言编写的程序就可以调用相同的 DLL 函数。程序与 DLL 函数在下列方面必须是兼容的:函数期望其参数被推送到堆栈上的顺序,是函数还是应用程序负责清理堆栈,以及寄存器中是否传递了任何参数。
    提供了扩展 MFC 库类的机制。可以从现有 MFC 类派生类,并将它们放到 MFC 扩展 DLL 中供 MFC 应用程序使用。
    使国际版本的创建轻松完成。通过将资源放到 DLL 中,创建应用程序的国际版本变得容易得多。可将用于应用程序的每个语言版本的字符串放到单独的 DLL 资源文件中,并使不同的语言版本加载合适的资源。
    使用 DLL 的一个潜在缺点是应用程序不是独立的;它取决于是否存在单独的 DLL 模块

    文章引用自:

    c#调用DLL

    每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍。首先,您需要了解什么是托管,什么是非托管。一般可以认为:非托管代码主要是基于win 32平台开发的DLL,activeX的组件,托管代码是基于.net平台开发的。如果您想深入了解托管与非托管的关系与区别,及它们的运行机制,请您自行查找资料,本文件在此不作讨论。

    (一) 调用DLL中的非托管函数一般方法

    首先,应该在C#语言源程序中声明外部方法,其基本形式是:

    [DLLImport(“DLL文件”)]

    修饰符 extern 返回变量类型 方法名称 (参数列表)

    其中

    DLL文件:包含定义外部方法的库文件。

    修饰符: 访问修饰符,除了abstract以外在声明方法时可以使用的修饰符。

    返回变量类型:在DLL文件中你需调用方法的返回变量类型。

    方法名称:在DLL文件中你需调用方法的名称。

    参数列表:在DLL文件中你需调用方法的列表。

    注意:需要在程序声明中使用System.Runtime.InteropServices命名空间。

          DllImport只能放置在方法声明上。

    DLL文件必须位于程序当前目录或系统定义的查询路径中(即:系统环境变量中Path所设置的路径)。

    返回变量类型、方法名称、参数列表一定要与DLL文件中的定义相一致。

    若要使用其它函数名,可以使用EntryPoint属性设置,如:

    [DllImport("user32.dll", EntryPoint="MessageBoxA")]

    static extern int MsgBox(int hWnd, string msg, string caption, int type);

    其它可选的 DllImportAttribute 属性:

    CharSet 指示用在入口点中的字符集,如:CharSet=CharSet.Ansi;

    SetLastError 指示方法是否保留 Win32"上一错误",如:SetLastError=true;

    ExactSpelling 指示 EntryPoint 是否必须与指示的入口点的拼写完全匹配,如:ExactSpelling=false;

    PreserveSig指示方法的签名应当被保留还是被转换, 如:PreserveSig=true;

    CallingConvention指示入口点的调用约定, 如:CallingConvention=CallingConvention.Winapi;

    此外,关于“数据封送处理”及“封送数字和逻辑标量”请参阅其它一些文章[2]。

    C#例子:

    1. 启动VS.NET,新建一个项目,项目名称为“Tzb”,模板为“Windows 应用程序”。

    2. 在“工具箱”的“ Windows 窗体”项中双击“Button”项,向“Form1”窗体中添加一个按钮。

    3. 改变按钮的属性:Name为 “B1”,Text为 “用DllImport调用DLL弹出提示框”,并将按钮B1调整到适当大小,移到适当位置。

    4. 在类视图中双击“Form1”,打开“Form1.cs”代码视图,在“namespace Tzb”上面输入“using System.Runtime.InteropServices;”,以导入该命名空间。

    5. 在“Form1.cs[设计]”视图中双击按钮B1,在“B1_Click”方法上面使用关键字 static 和 extern 声明方法“MsgBox”,将 DllImport 属性附加到该方法,这里我们要使用的是“user32.dll”中的“MessageBoxA”函数,具体代码如下:

    [DllImport("user32.dll", EntryPoint="MessageBoxA")]

    static extern int MsgBox(int hWnd, string msg, string caption, int type);

    然后在“B1_Click”方法体内添加如下代码,以调用方法“MsgBox”:

    MsgBox(0," 这就是用 DllImport 调用 DLL 弹出的提示框哦! "," 挑战杯 ",0x30);

    6. 按“F5”运行该程序,并点击按钮B1,便弹出如下提示框:

    (二) 动态装载、调用DLL中的非托管函数

    在上面已经说明了如何用DllImport调用DLL中的非托管函数,但是这个是全局的函数,假若DLL中的非托管函数有一个静态变量S,每次调用这个函数的时候,静态变量S就自动加1。结果,当需要重新计数时,就不能得出想要的结果。下面将用例子说明:

    1. DLL的创建

    1) 启动Visual C++ 6.0;

    2) 新建一个“Win32 Dynamic-Link Library”工程,工程名称为“Count”;

    3) 在“Dll kind”选择界面中选择“A simple dll project”;

    4) 打开Count.cpp,添加如下代码:

    // 导出函数,使用“ _stdcall ” 标准调用

    extern "C" _declspec(dllexport)int _stdcall count(int init);

    int _stdcall count(int init)

    {//count 函数,使用参数 init 初始化静态的整形变量 S ,并使 S 自加 1 后返回该值

    static int S=init;

    S++;

    return S;

    }

    5) 按“F7”进行编译,得到Count.dll(在工程目录下的Debug文件夹中)。

    2. 用DllImport调用DLL中的count函数

    1) 打开项目“Tzb”,向“Form1”窗体中添加一个按钮。

    2) 改变按钮的属性:Name为 “B2”,Text为 “用DllImport调用DLL中count函数”,并将按钮B1调整到适当大小,移到适当位置。

    3) 打开“Form1.cs”代码视图,使用关键字 static 和 extern 声明方法“count”,并使其具有来自 Count.dll 的导出函数count的实现,代码如下:

    [DllImport("Count.dll")]

    static extern int count(int init);

    4) 在“Form1.cs[设计]”视图中双击按钮B2,在“B2_Click”方法体内添加如下代码:

    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 函数是全局的、静态的函数!!! "," 挑战杯 ");

    5) 把Count.dll复制到项目“Tzb”的binDebug文件夹中,按“F5”运行该程序,并点击按钮B2,便弹出如下三个提示框:

    第1个提示框显示的是调用“count(0)”的结果,第2个提示框显示的是调用“count(10)”的结果,由所得结果可以证明“用DllImport调用DLL中的非托管函数是全局的、静态的函数”。所以,有时候并不能达到我们目的,因此我们需要使用下面所介绍的方法:C#动态调用DLL中的函数。

    3. C#动态调用DLL中的函数

    因为C#中使用DllImport是不能像动态load/unload assembly那样,所以只能借助API函数了。在kernel32.dll中,与动态库调用有关的函数包括[3]:

    ①LoadLibrary(或MFC 的AfxLoadLibrary),装载动态库。

    ②GetProcAddress,获取要引入的函数,将符号名或标识号转换为DLL内部地址。

    ③FreeLibrary(或MFC的AfxFreeLibrary),释放动态链接库。

    它们的原型分别是:

    HMODULE LoadLibrary(LPCTSTR lpFileName);

    FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName);

    BOOL FreeLibrary(HMODULE hModule);

    现在,我们可以用IntPtr hModule=LoadLibrary(“Count.dll”);来获得Dll的句柄,用IntPtr farProc=GetProcAddress(hModule,”_count@4”);来获得函数的入口地址。

    但是,知道函数的入口地址后,怎样调用这个函数呢?因为在C#中是没有函数指针的,没有像C++那样的函数指针调用方式来调用函数,所以我们得借助其它方法。经过研究,发现我们可以通过结合使用System.Reflection.Emit及System.Reflection.Assembly里的类和函数达到我们的目的。为了以后使用方便及实现代码的复用,我们可以编写一个类。

    1) dld类的编写:

    1. 打开项目“Tzb”,打开类视图,右击“Tzb”,选择“添加”-->“类”,类名设置为“dld”,即dynamic loading dll 的每个单词的开头字母。

    2. 添加所需的命名空间及声明参数传递方式枚举:

    using System.Runtime.InteropServices; // 用 DllImport 需用此命名空间

    using System.Reflection; // 使用 Assembly 类需用此 命名空间

    using System.Reflection.Emit; // 使用 ILGenerator 需用此命名空间

    在“public class dld”上面添加如下代码声明参数传递方式枚举:

    /// <summary>

    /// 参数传递方式枚举 ,ByValue 表示值传递 ,ByRef 表示址传递

    /// </summary>

    public enum ModePass

    {

    ByValue = 0x0001,

    ByRef = 0x0002

    }

    3. 声明LoadLibrary、GetProcAddress、FreeLibrary及私有变量hModule和farProc:

    /// <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.Zero;

    /// <summary>

    /// GetProcAddress 返回的函数指针

    /// </summary>

    private IntPtr farProc=IntPtr.Zero;

    4. 添加LoadDll方法,并为了调用时方便,重载了这个方法:

    /// <summary>

    /// 装载 Dll

    /// </summary>

    /// <param name="lpFileName">DLL 文件名 </param>

    public void LoadDll(string lpFileName)

    {

    hModule=LoadLibrary(lpFileName);

    if(hModule==IntPtr.Zero)

    throw(new Exception(" 没有找到 :"+lpFileName+"." ));

    }

    若已有已装载Dll的句柄,可以使用LoadDll方法的第二个版本:

    public void LoadDll(IntPtr HMODULE)

    {

    if(HMODULE==IntPtr.Zero)

    throw(new Exception(" 所传入的函数库模块的句柄 HMODULE 为空 ." ));

    hModule=HMODULE;

    }

    5. 添加LoadFun方法,并为了调用时方便,也重载了这个方法,方法的具体代码及注释如下:

    /// <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+" 这个函数的入口点 "));

    }

    6. 添加UnLoadDll及Invoke方法,Invoke方法也进行了重载:

    /// <summary>

    /// 卸载 Dll

    /// </summary>

    public void UnLoadDll()

    {

    FreeLibrary(hModule);

    hModule=IntPtr.Zero;

    farProc=IntPtr.Zero;

    }

    用Visual C#调用Windows API函数

    Api函数是构筑Windws应用程序的基石,每一种Windows应用程序开发工具,它提供的底层函数都间接或直接地调用了Windows API函数,同时为了实现功能扩展,一般也都提供了调用WindowsAPI函数的接口, 也就是说具备调用动态连接库的能力。Visual C#和其它开发工具一样也能够调用动态链接库的API函数。.NET框架本身提供了这样一种服务,允许受管辖的代码调用动态链接库中实现的非受管辖函数,包括操作系统提供的Windows API函数。它能够定位和调用输出函数,根据需要,组织其各个参数(整型、字符串类型、数组、和结构等等)跨越互操作边界。

    下面以C#为例简单介绍调用API的基本过程:

    动态链接库函数的声明

     动态链接库函数使用前必须声明,相对于VB,C#函数声明显得更加罗嗦,前者通过 Api Viewer粘贴以后,可以直接使用,而后者则需要对参数作些额外的变化工作。

     动态链接库函数声明部分一般由下列两部分组成,一是函数名或索引号,二是动态链接库的文件名。

      譬如,你想调用User32.DLL中的MessageBox函数,我们必须指明函数的名字MessageBoxA或MessageBoxW,以及库名字User32.dll,我们知道Win32 API对每一个涉及字符串和字符的函数一般都存在两个版本,单字节字符的ANSI版本和双字节字符的UNICODE版本。

     下面是一个调用API函数的例子:

    [DllImport("KERNEL32.DLL", EntryPoint="MoveFileW", SetLastError=true,

    CharSet=CharSet.Unicode, ExactSpelling=true,

    CallingConvention=CallingConvention.StdCall)]

    public static extern bool MoveFile(String src, String dst);

     其中入口点EntryPoint标识函数在动态链接库的入口位置,在一个受管辖的工程中,目标函数的原始名字和序号入口点不仅标识一个跨越互操作界限的函数。而且,你还可以把这个入口点映射为一个不同的名字,也就是对函数进行重命名。重命名可以给调用函数带来种种便利,通过重命名,一方面我们不用为函数的大小写伤透脑筋,同时它也可以保证与已有的命名规则保持一致,允许带有不同参数类型的函数共存,更重要的是它简化了对ANSI和Unicode版本的调用。CharSet用于标识函数调用所采用的是Unicode或是ANSI版本,ExactSpelling=false将告诉编译器,让编译器决定使用Unicode或者是Ansi版本。其它的参数请参考MSDN在线帮助.

     在C#中,你可以在EntryPoint域通过名字和序号声明一个动态链接库函数,如果在方法定义中使用的函数名与DLL入口点相同,你不需要在EntryPoint域显示声明函数。否则,你必须使用下列属性格式指示一个名字和序号。

    [DllImport("dllname", EntryPoint="Functionname")]

    [DllImport("dllname", EntryPoint="#123")]

    值得注意的是,你必须在数字序号前加“#”

    下面是一个用MsgBox替换MessageBox名字的例子:

    [C#]

    using System.Runtime.InteropServices;

    public class Win32 {

    [DllImport("user32.dll", EntryPoint="MessageBox")]

    public static extern int MsgBox(int hWnd, String text, String caption, uint type);

    }

    许多受管辖的动态链接库函数期望你能够传递一个复杂的参数类型给函数,譬如一个用户定义的结构类型成员或者受管辖代码定义的一个类成员,这时你必须提供额外的信息格式化这个类型,以保持参数原有的布局和对齐。

    C#提供了一个StructLayoutAttribute类,通过它你可以定义自己的格式化类型,在受管辖代码中,格式化类型是一个用StructLayoutAttribute说明的结构或类成员,通过它能够保证其内部成员预期的布局信息。布局的选项共有三种:

    布局选项

    描述

    LayoutKind.Automatic

    为了提高效率允许运行态对类型成员重新排序。

    注意:永远不要使用这个选项来调用不受管辖的动态链接库函数。

    LayoutKind.Explicit

    对每个域按照FieldOffset属性对类型成员排序

    LayoutKind.Sequential

    对出现在受管辖类型定义地方的不受管辖内存中的类型成员进行排序。

    传递结构成员

    下面的例子说明如何在受管辖代码中定义一个点和矩形类型,并作为一个参数传递给User32.dll库中的PtInRect函数,

    函数的不受管辖原型声明如下:

    BOOL PtInRect(const RECT *lprc, POINT pt);

    注意你必须通过引用传递Rect结构参数,因为函数需要一个Rect的结构指针。

    [C#]

    using System.Runtime.InteropServices;

    [StructLayout(LayoutKind.Sequential)]

    public struct Point {

    public int x;

    public int y;

    }

    [StructLayout(LayoutKind.Explicit]

    public struct Rect {

    [FieldOffset(0)] public int left;

    [FieldOffset(4)] public int top;

    [FieldOffset(8)] public int right;

    [FieldOffset(12)] public int bottom;

    }

    class Win32API {

    [DllImport("User32.dll")]

    public static extern Bool PtInRect(ref Rect r, Point p);

    }

    类似你可以调用GetSystemInfo函数获得系统信息:

    ? using System.Runtime.InteropServices;

    [StructLayout(LayoutKind.Sequential)]

    public struct SYSTEM_INFO {

    public uint dwOemId;

    public uint dwPageSize;

    public uint lpMinimumApplicationAddress;

    public uint lpMaximumApplicationAddress;

    public uint dwActiveProcessorMask;

    public uint dwNumberOfProcessors;

    public uint dwProcessorType;

    public uint dwAllocationGranularity;

    public uint dwProcessorLevel;

    public uint dwProcessorRevision;

    }

    [DllImport("kernel32")]

    static extern void GetSystemInfo(ref SYSTEM_INFO pSI);

    SYSTEM_INFO pSI = new SYSTEM_INFO();

    GetSystemInfo(ref pSI);

    类成员的传递

    同样只要类具有一个固定的类成员布局,你也可以传递一个类成员给一个不受管辖的动态链接库函数,下面的例子主要说明如何传递一个sequential顺序定义的MySystemTime类给User32.dll的GetSystemTime函数, 函数用C/C++调用规范如下:

    void GetSystemTime(SYSTEMTIME* SystemTime);

    不像传值类型,类总是通过引用传递参数.

    [C#]

    [StructLayout(LayoutKind.Sequential)]

    public class MySystemTime {

    public ushort wYear;

    public ushort wMonth;

    public ushort wDayOfWeek;

    public ushort wDay;

    public ushort wHour;

    public ushort wMinute;

    public ushort wSecond;

    public ushort wMilliseconds;

    }

    class Win32API {

    [DllImport("User32.dll")]

    public static extern void GetSystemTime(MySystemTime st);

    }

    回调函数的传递:

    从受管辖的代码中调用大多数动态链接库函数,你只需创建一个受管辖的函数定义,然后调用它即可,这个过程非常直接。

    如果一个动态链接库函数需要一个函数指针作为参数,你还需要做以下几步:

    首先,你必须参考有关这个函数的文档,确定这个函数是否需要一个回调;第二,你必须在受管辖代码中创建一个回调函数;最后,你可以把指向这个函数的指针作为一个参数创递给DLL函数,.

    回调函数及其实现:

    回调函数经常用在任务需要重复执行的场合,譬如用于枚举函数,譬如Win32 API 中的EnumFontFamilies(字体枚举), EnumPrinters(打印机), EnumWindows (窗口枚举)函数. 下面以窗口枚举为例,谈谈如何通过调用EnumWindow 函数遍历系统中存在的所有窗口

    分下面几个步骤:

    1. 在实现调用前先参考函数的声明

    BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam)

    显然这个函数需要一个回调函数地址作为参数.

    2. 创建一个受管辖的回调函数,这个例子声明为代表类型(delegate),也就是我们所说的回调,它带有两个参数hwnd和lparam,第一个参数是一个窗口句柄,第二个参数由应用程序定义,两个参数均为整形。

      当这个回调函数返回一个非零值时,标示执行成功,零则暗示失败,这个例子总是返回True值,以便持续枚举。

    3. 最后创建以代表对象(delegate),并把它作为一个参数传递给EnumWindows 函数,平台会自动地 把代表转化成函数能够识别的回调格式。

    [C#]

    using System;

    using System.Runtime.InteropServices;

    public delegate bool CallBack(int hwnd, int lParam);

    public class EnumReportApp {

    [DllImport("user32")]

    public static extern int EnumWindows(CallBack x, int y);

    public static void Main()

    {

    CallBack myCallBack = new CallBack(EnumReportApp.Report);

    EnumWindows(myCallBack, 0);

    }

    public static bool Report(int hwnd, int lParam) {

    Console.Write("窗口句柄为");

    Console.WriteLine(hwnd);

    return true;

    }

    }

    指针类型参数传递:

     在Windows API函数调用时,大部分函数采用指针传递参数,对一个结构变量指针,我们除了使用上面的类和结构方法传递参数之外,我们有时还可以采用数组传递参数。

     下面这个函数通过调用GetUserName获得用户名

    BOOL GetUserName(

    LPTSTR lpBuffer, // 用户名缓冲区

    LPDWORD nSize // 存放缓冲区大小的地址指针

    );

    [DllImport("Advapi32.dll",

    EntryPoint="GetComputerName",

    ExactSpelling=false,

    SetLastError=true)]

    static extern bool GetComputerName (

    [MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,

      [MarshalAs(UnmanagedType.LPArray)] Int32[] nSize );

     这个函数接受两个参数,char * 和int *,因为你必须分配一个字符串缓冲区以接受字符串指针,你可以使用String类代替这个参数类型,当然你还可以声明一个字节数组传递ANSI字符串,同样你也可以声明一个只有一个元素的长整型数组,使用数组名作为第二个参数。上面的函数可以调用如下:

    byte[] str=new byte[20];

    Int32[] len=new Int32[1];

    len[0]=20;

    GetComputerName (str,len);

    MessageBox.Show(System.Text.Encoding.ASCII.GetString(str));

     最后需要提醒的是,每一种方法使用前必须在文件头加上:

     using System.Runtime.InteropServices;