梦寐以求,太强了,自己看,不爽砍我

1:拼写检查
Spell Check

2:智能探测Filter
Filter

3:色彩
Color

4:无前缀、非前缀智能补写+“结构体”自动补全
Acronym

还不满足,还有功能不爽,看看附件里面的图,再对照自己的比较一下,差距啊:)

厚道的放出链接:

http://www.wholetomato.com/index.html

破解都找到了,我太好了T T

Crack for VA_X_Setup1438.exe

Browser Helper Object

那天有个信息安全实验室的博士生叫我们做IE插件,主要用于拦截IE浏览器的各项消息,从而做出我们自己的处理。

实现方法,当然千奇百怪,全局钩子函数,进程注入,消息拦截等等都可以,不过我最终选择了全局钩子函数,感觉应该好做一点。

由于最近比较多接触C#,第一反映当然还是.net framework。
MSDN->Filter:c#->“全局钩子”一找,郁闷死人:

在 .NET 框架中不支持全局挂钩

您无法在 Microsoft .NET 框架中实现全局挂钩。若要安装全局挂钩,挂钩必须有一个本机动态链接库 (DLL) 导出以便将其本身插入到另一个需要调入一个有效而且一致的函数的进程中。这需要一个 DLL 导出,而 .NET 框架不支持这一点。托管代码没有让函数指针具有统一的值这一概念,因为这些函数是动态构建的代理。
[http://support.microsoft.com/kb/318804]

当然,其实这段文字本身有点问题,先不说网上有人说好像并不是所有的全局挂钩都不能加载,但就.net支持对非受控代码的访问这点,就可以通过在中间增加一层Managed Code->UnManaged Code->Managed Code实现调用。

但是这样的实现难度陡增,很是麻烦。不过在搜索结果中看到一个比较乖的咚咚:Browser Helper Object,看起来感觉比较象。

记得以前有篇写Office Doc Object架构的文章很有名(好像小排发过吧),不过记忆中,当时看了一点就放弃了,感觉挺麻烦的。特别是针对COM组件的调用部分,看着就头大。

话说回来,调用COM组件在.net环境下,还是C++来得方便快捷,毕竟都是非受控的代码,不用处理太多受控、非受控对象之间的交互问题。不过C++.net依然还是VC++6.0的模样,看着都恶心,于是乎放弃:P

粗粗看了看Browser Helper Object(BHO)的机理,这个咚咚主要用于下面的场景:

想实现一个浏览器,提供一些自己的功能,但是如果用IE Control来写的话,还要自己写很多很多很多诸如:前进、后退、历史记录等等IE已经提供的功能,所以更简便的方法是给扩展、修改IE的功能,直接使用IE来实现。这方面,当然不能不提很多人深恶痛绝的3721,他提供了一个个性化的浏览器,却只写了很少的代码,其余的都是IE自带的内容。

打开一个IE,打开Spy++,再结合Internet Explorer 4.0 Architecture的图看看

Internet Explorer 4.0 Architecture

举个例子,如果想要获取IE地址栏的内容:
IEExplorer->IEFrame->WorkerW->ReBarWindow32->ComboBoxEx32->ComboBox->Edit
这就是他的层次结构

IE和Explorer在启动新线程的时候(Explorer要看看选项里面是否选择了在新的线程中打开窗口),会检查注册表项:

[HKLMSOFTWAREMicrosoftWindowsCurrentVersionExplorerBrowser Helper Objects]

他下面有很多子项,每一个子项就是一个GUID
例如:
{06849E9F-C8D7-4D59-B87D-784B7D6BE0B3}

这个GUID对应一个BHO组件(其实就是一个COM组件)
这个组件在[HKLMSOFTWAREClassesCLSID]下面进行描述,包括dll路径,签名等等信息

IE或Explorer在每个线程里会依次实例化每个在上述路径注册的BHO对象,从而BHO对象实际上运行在IE或Explorer的地址空间内,并且能够访问IE的几乎所有资源,拦截几乎所有事件,挂载自己的事件处理函数,而不需要跨进程调用和传消息。

看来完全符合最初的应用要求并且是最简单的方法了。

接下来的问题,就只剩下COM组件的使用了。.NET对COM组件的调用提供了很方便的100% pure 受控的访问方式 – Interop, Marshal。查错过程比较艰辛,因为每次重新生成都要重启Explorer.exe T T并且调试的时候,要先开IE,再下断点,再将VS .NET附加到IE进程,再点新建IE窗口…

COM组件的使用:
首先,当然是用Create GUID工具生成我们自己BHO组件的GUID({B29E305D-BC4D-4a80-B522-B0ABC9EBDFFC}),然后添加对 Microsoft Internet Controls COM组件(%systemroot%ShDocVw.dll)的引用。VS.NET会自动Wrapper之为 Interop.SHDocVw.dll,值得注意的是,这里由于最后要使用BHO注册,所以要求这个COM组件必须用私钥签名,从而使用强名机制。具体步骤如下:
1、sn -k BHO.key生成Bho.key文件,也就是我们的私钥。
2、设置项目属性,ActiveX/COM对象的包装程序集->包装程序集密钥文件,指向Bho.key
3、添加COM引用(此时查看相关信息就可以看到强名选项已设为true)
4、设置AssemblyInfo.cs填写key字段,指向Bho.key

然后,就可以声明我们要继承的各项接口:

using SHDocVw;
#region 引入IObjectWithSite接口
[ComImport(), Guid("fc4801a3-2ba9-11cf-a229-00aa003d7352")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IObjectWithSite
{
void SetSite([In ,MarshalAs(UnmanagedType.IUnknown)] object site);
void GetSite(ref Guid guid, [MarshalAs(UnmanagedType.IUnknown)] out object site);
}
#endregion
#region 定义默认BHO COM接口
[GuidAttribute("181C179B-7CC9-4457-8C1D-4B45E7C8589D")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsDual)]
public interface IObserver
{
}
#endregion

然后定义我们自己的BHO类:

/// <summary>
///定义BHO类,此类由浏览器实例化
/// </summary>
[ClassInterfaceAttribute(ClassInterfaceType.None)]
[GuidAttribute("B29E305D-BC4D-4a80-B522-B0ABC9EBDFFC")]//由CreateGUID程序生成
[ProgIdAttribute("Observer.BrowserMonitor")]
public class BrowserMonitor : IObserver, IObjectWithSite
{}

这个类里面如下引入定义:

protected IWebBrowser2 browser; //浏览器对象
protected DWebBrowserEvents2_Event browserEvents;//浏览器事件

并且实现IObjectWithSite接口:

#region IObjectWithSite 成员
/// <summary>
/// IE调用此方法,并传递指向容器Site的IUnknown指针,由此我们可以获得IWebBrowser2接口
/// 并挂载DWebBrowserEvents2事件
/// </summary>
/// <param name="site">容器Site的IUnknown指针</param>
public void SetSite(object site)
{
// TODO: 添加 BrowserMonitor.SetSite 实现
#region 取得 IWebBrowser2 引用
if (browser != null)
Release();
if (site == null)
return;
browser = site as IWebBrowser2;
#endregion
#region 检查名称,当且仅当为IEXPLORE.EXE时加载
string hostName = browser.FullName;
if (!(hostName.ToUpper().EndsWith("IEXPLORE.EXE")))
{
Release();
return;
}
#endregion
#region 挂载浏览器事件
browserEvents = browser as DWebBrowserEvents2_Event ;
if (browserEvents != null)
{
browserEvents.DocumentComplete += new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete);
}
else
{
Release();
return;
}
#endregion
}
/// <summary>
/// 调用者调用此方法以获得前面浏览器发送给SetSite()方法的浏览器对象
/// </summary>
/// <param name="guid">请求Site接口对象的GUID</param>
/// <param name="site">返回的Site接口对象</param>
public void GetSite(ref System.Guid guid, out object site)
{
// TODO: 添加 BrowserMonitor.GetSite 实现
site = null;
if (browser != null)
{
IntPtr pSite = IntPtr.Zero;
IntPtr pUnk = Marshal.GetIUnknownForObject(browser); //引用计数增加
Marshal.QueryInterface(pUnk, ref guid, out pSite); //引用计数增加
Marshal.Release(pUnk); //引用计数减少
Marshal.Release(pUnk); //引用计数减少
if (!pSite.Equals(IntPtr.Zero))
{
site = pSite;
}
else
{
// 若找不到请求的接口,将返回E_NOINTERFACE
Release();
Marshal.ThrowExceptionForHR(E_NOINTERFACE);
}
}
else
{
// 若找不到请求的接口对象,将返回E_FAIL
Release();
Marshal.ThrowExceptionForHR(E_FAIL);
}
}
#endregion

这里挂载了我们自己的事件处理函数,处理DocumentComplete事件,此外还有很多事件可以使用,例如:

DownloadBegin
DownloadComplete
BeforeNavigate2
CommandStateChange
FileDownload
NavigateComplete
NewWindow
FullScreen
MenuBar
Quit
FrameBeforeNavigate
FrameNavigateComplete
FrameNewWindow
ProgressChange
PropertyChange
StatusTextChange
TitleChange
WindowActivate
WindowMove
WindowResize

using System.IO;
#region 用于挂载的自定义函数
protected void OnDocumentComplete(object display, ref object url)
{
try
{
if (Marshal.Equals(browser, display))
{
StreamWriter sw = new StreamWriter(@"C:Test.txt", true);
sw.WriteLine(url.ToString());
sw.Close();
}
}
catch
{
Release();
Marshal.ThrowExceptionForHR(E_FAIL);
}
}
#endregion

此外,关于错误处理,由于涉及包裹为受控组件的非受控组件,所以需要使用以下方式抛出异常:

#region 定义HRESULT值 : 预定义的COMException值
//在未检查的上下文中,如果表达式产生目标类型范围之外的值,则结果被截断
const int E_FAIL = unchecked((int)0x80004005);//失败
const int E_NOINTERFACE = unchecked((int)0x80004002);//QueryInterface时,接口不存在
#endregion
Marshal.ThrowExceptionForHR(E_FAIL);
Marshal.ThrowExceptionForHR(E_NOINTERFACE);

使用一下方式,释放对象:

#region Release操作
protected void Release()
{
if (browserEvents != null)
{
Marshal.ReleaseComObject(browserEvents);
browserEvents = null;
}
if (browser != null)
{
Marshal.ReleaseComObject(browser);
browser = null;
}
}
#endregion

至此已基本完成,但是要想自动注册BHO组件,还需要做几件事情:

#region 挂载注册/注销操作
/// <summary>
/// 在注册COM组件时,由运行时调用
/// </summary>
[ComRegisterFunctionAttribute]
public static void Register(Type type)
{
// 注册BHO组件
string guid = type.GUID.ToString("B");
RegistryKey rkey =
Registry.LocalMachine.CreateSubKey(@"SOFTWAREMicrosoftWindowsCurrentVersionExplorerBrowser Helper Objects");
RegistryKey rkeyBHO = rkey.CreateSubKey(guid);
}
/// <summary>
/// 在注销COM组件时,由运行时调用
/// </summary>
[ComUnregisterFunctionAttribute]
public static void Unregister(Type type)
{
// 注销BHO组件
string guid = type.GUID.ToString("B");
RegistryKey rkey =
Registry.LocalMachine.CreateSubKey(@"SOFTWAREMicrosoftWindowsCurrentVersionExplorerBrowser Helper Objects");
rkey.DeleteSubKey(guid,false);
}
#endregion

由这些可见.NET平台对于COM组件的调用支持还是相当强大的,可以在受控环境下实现100% pure的COM访问能力。

PS:
结尾怎么写成这样了,写一写的就有点偏题了- –

BHO Project

Cached FlyWeight

其实因为C#语言自身的一些特点,和FlyWeight的实现方式不太一样- –

初学,不足之处见谅:)

详细叙述见附件

======2005-12-13======

作了一些修改,使用方式不变,不过加入了对任何单参数构造函数的支持。

有人问,为什么不支持任何多参数构造函数, 因为实际上参数就是缓存时用于查找的Key,多参数生成Key的时候较麻烦而已:)

最后,说明一点,本FlyWeight在Scavenging的时候,会根据使用的情况,删除最不常使用的Key

======2006-1-19=======

VS2005发布后,更新版本

Download Fly Weight

人之初,性本善

人之初,性本善,世间本没恶魔,有的只是迷失的心智,爵爷有罪,按罪当诛,但其并非恶魔,他只是一个普普通通的学生。他坚强,他坚持,套用《朋友》的歌词,他能够划船不用桨,他能够扬帆没有风向, 但任其三头六臂,也不能能够离开“好朋友”却不感伤。

然而正是这些所谓的好朋友,给了他最后的一击,当然他的选择是极其错误的,但是他的选择以他的背景来看,却并非完全不能理解。

总之,黑夜给了我黑色的眼睛,我却用它来寻找光明;命运给了我们苦难,我们不应该去苦难别人。

希望他们一个寝室的5兄弟,能够再次团聚, 一起开心的玩牌,一起开心的歌唱。

附:
遗书
春城的春天下着雨
有着一丝凄寒的风
我望着生锈的铁窗
我想起了我可怜的父母
为了供子女读书
他二老起早摸黑在田里干活
还点着蜡烛为人烫衣服
5毛钱一件
那次我母亲掉了一百块钱
她心疼的说那是烫了两百件衣服赚来的钱呀
我看着母亲伤心的样子
就把自己做苦力赚来的一百块钱丢到地上
对母亲说:妈妈你的一百块钱在这里!
妈妈露出了一丝苦笑
其实妈妈知道是我丢的
我不怕一个人独自吃苦
我不忍心父母看到我吃苦
读大学几年我没问家里要一分钱
我总希望父母不要为我操劳
他们年纪大了
辛苦了一辈子
怎么忍心增添他们的负担呢
但学费是高昂的
我必须自己去卖苦力
耽误学习也是没办法的事情
我一个人默默的做苦工
我一个人偷偷一天只吃两个馒头
冬天其实我更怕冷
因为我是南方人
但是为了节省洗热水澡的几块钱
我整个冬天坚持洗冷水澡
我冷的直打哆嗦
我微笑着对同学说
我们年轻人需要锻炼身体
那天我没鞋子穿
我不好意思去上课
直到学校发了点救济
我才买了双便宜的拖鞋走进了课堂
我家一直很穷苦
我在穷苦中长大
我从小就体味到家庭的艰辛
幼小的我便心疼父母的辛苦
只想通过小手减轻父母一点点负担
我说:爸妈你们辛苦了,我做好了饭,你们快吃吧!
我一直努力读书
村里的邻居以及中学老师
都知道我是个吃苦好学、斯文老实的学生
我中学拿过全国奥林匹克物理大赛二等奖
我上了高中受过歧视而闷闷不乐
可是在接近高考的那几个月
我顶住各方面的压力奋发苦读
就这样我一个穷困的学生考出了优异的成绩
我高考的成绩超过我们广西省当年重点线50多分
完全可以上名牌大学武汉大学 、哈工大之类
可是我考虑到那离家远费用更大
所以选择地域较近并且消费水平比较低的云南大学
我充盈着希冀
一个农家的孩子
蕴涵着淳朴老实本分
来到了云南大学
当我看到毛主席书写的四个大字
“云南大学”
我的心激起一阵阳光的涟漪
我立志一定好好继续努力
学好专业找个好工作
可以好好报答父母,
改变穷苦的命运也好好用自己的知识
来为社会为国家努力工作
认真做个受人尊重的人
做个对社会有贡献的人
进入大学以后我怎么发现
大部分人不爱读书
每天晚上谈女孩子
哪个女孩子性感漂亮
和哪个女孩子做爱更爽
有钱的同学则大胆的找起女朋友来
大摇大摆的在学校旁边租房子同居
大家都爱玩电脑游戏
大家都嘲笑我是个土包子
这个不会那个也不会
于是我为了和同学打好关系
我也学会了玩电脑游戏
并且由于我的天生智商还可以
玩游戏比他们更厉害
我以后更乐衷于玩电脑了
我还自己用打工的钱以及借了部分钱
买了台旧电脑
我很大方
我的电脑同学们随时都可以玩
我很希望和同学们和睦相处
时间很快
大学过了几年
我暑假寒假基本都不回家
都在昆明做苦工赚钱
我还慰藉父母
爸爸妈妈我在云大过的很好
老师还经常约请我到他们家去做客呢
其实我每次说这样的话心里都是虚的
我有时候没钱就打一份饭吃上两天
经常一天吃两个馒头就过去了
我从不怕苦也不怨恨谁
我更没因为没钱而想到去偷去抢
我很坚强
我为自己骄傲
我对的起父母
我对的起自己
我对的起同学
我对的起社会
可是总有那么些同学总有意无意的歧视我
有时候说些话很伤我的心
他们觉得我的穿着打扮很怪
他们觉得我的举止很怪
我开始悄悄的打工
我不想被人家看成异类
好在我自以为有几个好老乡、好同学
这样我才能不去理会那些同学的歧视与人格蔑视
大学很多男生都在大胆的追求自己喜欢的女孩子
很多男生都谈恋爱了
我在这种氛围下加上几个同学的怂恿
也大胆的写了一封情书
交给了我暗恋许久的一个女孩子
由于我的模样不好看
加上又没钱
人显的很土气内向
那女生毫不留情的当着许多人的面
把我那封用真心诚挚锩刻成的信
撕个粉碎
我只是内心痛苦了下
我也并没有怨恨谁
我只觉得自己确实条件不行配不上她
我对父母也是这么说的
我有自知之明,我不谈恋爱
况且大学生应该以学业为重
时间过了很快
快到大学毕业了只剩一个学期就毕业了
最后一个寒假
我依旧没有回家
依旧在昆明做苦力
离开学还有几天
有些同学提前来学校了
大家可能都是为了找工作所以提前回学校
我很开心
因为整个寒假我一个人多么孤寂
我不怕吃苦
但是人是很怕寂寞的
当我看到同学们时我很热情
他们为了打发时间约我打牌
我很乐意的接受了
其实我们原来也经常玩牌的
其实无须掩饰
我智商真的比较高
所以打牌经常赢
几个同学都怀疑我作弊
我坚持说没有
谁知道那三个我自以为平时没有歧视过我的同学
以为一直平等对我的同学
竟然恶语伤我,蹂躏我的人格,
还揭露了我以前的许多伤疤,
包括那女生撕毁我情书的事情
什么苦楚什么贫苦什么艰辛的生活
我可以忍受
其他人歧视蔑视我
我也可以忍受
可是我这几个平时稍微好点的同学竟然
这样残酷无情的践踏、蹂躏我的人格尊严
原来每个人长期以来一直这样
凶悍的歧视我
残忍的嘲笑我
我的心很痛
我的泪悄悄的落下了
我是一个坚强的人
我不曾被艰辛贫苦生活打败
可是当我的人格尊严被人糟蹋的不成样子的时候
当我的过去的伤痛被人再次拿出来嘲讽的时候
我的心滴血了
践踏我竟然还是平时关系稍微好点的同学以及老乡!
我在这种氛围下再也难以立足了
是他们残忍的对我
是他们不给我活路
他们没有给我留后路
他们淋漓尽致地侮辱完我后
居然还那样嚣张与快乐
因为他们生活条件还是比较好的
他们还有资本去玩女孩子
我伤痛的心找不到归处!
总浮现出他们淋漓尽致侮辱我的样子
我没有退路了
我决定玉石俱毁
我决定给那些歧视穷苦人、蔑视穷苦人的人
一个教训
我决定给那些无情践踏、残忍蹂躏穷苦人人格尊严的人
一个教训
我本来习惯被人歧视、被人蔑视的
可是这次他们表现的实在是太淋漓尽致了
他们嘲讽时刻的无情
他们侮辱时刻的面孔可恶
让我下定了决心
终于我买了一把石锤
结束了他们几个人的生命
于是我逃跑
我想没抓到我以后到一个
没有歧视侮辱的地方重新做人
万一抓到就一死百了
我是不怕死的我只想死刑
我不愿意被判无期徒刑
因为那样会给我父母带来压力!许多人现在都说我是杀人恶魔
都说我杀红了眼
其实说心理话
我只想杀那些无情蹂躏糟蹋别人人格的人
我并不想伤及无辜
当我另一个同学来找我的时候
我并没有杀他
因为在我最穷困的时候并没有歧视我
反而打饭给我吃
我深刻懂得人间真情的可贵
我曾对自己说:滴水之恩,涌泉相报
我一定会报答这位同学
可是我现在留下了一个永远的遗憾
我没有机会报答这位同学了!
但我最后想送一句话给我那位同学:
好人自有好报!
我听到到飘曳进来的歌声了
好象是〈梦驮铃〉
多么熟悉的旋律呀
我想起了经常帮助我家的十四叔、十四婶来了
我们那个家虽然很穷
但是大家都很互相关怀
大家都感到很快乐
没有歧视与蔑视
从来不知道什么是人格践踏
我很想和陶渊明那样
就永远生活在我那个村子里
天天看着清澈的流水
望着袅袅的炊烟
写着清新的诗歌
呵 那多美好呀
可是现在—–
只好等来生了
爸爸妈妈,对不起了
儿子不孝
儿子来生一定让你过上好日子
警察又来提审我了
我总听到外面摩托车的声音
为什么总是那么飞扬跋扈
我怀念十哥开的摩托修理店
在我印象中那是很赚钱的
十哥骑摩托车很英姿飒爽
那摩托车的声音是
那样的婉转清脆!
我仿佛又坐在十哥的摩托车上了
慢悠悠的行走在
我可爱、纯朴、亲切的家乡
PS:几个生命就这么消失了
青春其实可以用另一种方式去诠释的
我也很穷
我也很丑
我也经常被抛弃
别太在意别人对你说些什么
也不要去委曲求全
找到和你真心相印的朋友
一路走好

C#中调用Windows API的要点

在.Net Framework SDK文档中,关于调用Windows API的指示比较零散,并且其中稍全面一点的是针对Visual Basic .net讲述的。本文将C#中调用API的要点汇集如下,希望给未在C#中使用过API的朋友一点帮助。另外如果安装了Visual Studio .net的话,在C:Program FilesMicrosoft Visual Studio .NETFrameworkSDKSamplesTechnologiesInteropPlatformInvokeWinAPIsCS 目录下有大量的调用API的例子。

一、调用格式
using System.Runtime.InteropServices; //引用此名称空间,简化后面的代码
...
//使用DllImportAttribute特性来引入api函数,注意声明的是空方法,即方法体为空。
[DllImport("user32.dll")]
public static extern ReturnType FunctionName(type arg1,type arg2,...);
//调用时与调用其他方法并无区别,可以使用字段进一步说明特性,用逗号隔开,如:
[ DllImport( "kernel32", EntryPoint="GetVersionEx" )]

DllImportAttribute特性的公共字段如下:

1、CallingConvention 指示向非托管实现传递方法参数时所用的CallingConvention 值。
CallingConvention.Cdecl : 调用方清理堆栈。它使您能够调用具有 varargs 的函数。
CallingConvention.StdCall : 被调用方清理堆栈。它是从托管代码调用非托管函数的默认约定。

2、CharSet 控制调用函数的名称版本及指示如何向方法封送 String 参数。
此字段被设置为 CharSet 值之一。如果 CharSet 字段设置为 Unicode,则所有字符串参数在传递到非托管实现之前都转换成 Unicode 字符。这还导致向 DLL EntryPoint 的名称中追加字母“W”。如果此字段设置为 Ansi,则字符串将转换成 ANSI 字符串,同时向 DLL EntryPoint 的名称中追加字母“A”。大多数 Win32 API 使用这种追加“W”或“A”的约定。如果 CharSet 设置为 Auto,则这种转换就是与平台有关的(在 Windows NT 上为 Unicode,在 Windows 98 上为 Ansi)。CharSet 的默认值为 Ansi。CharSet 字段也用于确定将从指定的 DLL 导入哪个版本的函数。CharSet.Ansi 和 CharSet.Unicode 的名称匹配规则大不相同。
对于 Ansi 来说,如果将 EntryPoint 设置为“MyMethod”且它存在的话,则返回“MyMethod”。如果 DLL 中没有“MyMethod”,但存在“MyMethodA”,则返回“MyMethodA”。对于 Unicode 来说则正好相反。如果将 EntryPoint 设置为“MyMethod”且它存在的话,则返回“MyMethodW”。如果 DLL 中不存在“MyMethodW”,但存在“MyMethod”,则返回“MyMethod”。如果使用的是 Auto,则匹配规则与平台有关(在 Windows NT 上为 Unicode,在 Windows 98 上为 Ansi)。如果 ExactSpelling 设置为 true,则只有当 DLL 中存在“MyMethod”时才返回“MyMethod”。

3、EntryPoint 指示要调用的 DLL 入口点的名称或序号。
如果你的方法名不想与api函数同名的话,一定要指定此参数,例如:
[DllImport("user32.dll",CharSet="CharSet.Auto",EntryPoint="MessageBox")]
public static extern int MsgBox(IntPtr hWnd,string txt,string caption, int type);

4、ExactSpelling 指示是否应修改非托管 DLL 中的入口点的名称,以与 CharSet 字段中指定的 CharSet 值相对应。
如果为 true,则当 DllImportAttribute.CharSet 字段设置为 CharSet 的 Ansi 值时,向方法名称中追加字母 A,当 DllImportAttribute.CharSet 字段设置为 CharSet 的 Unicode 值时,向方法的名称中追加字母 W。此字段的默认值是 false。

5、PreserveSig 指示托管方法签名不应转换成返回 HRESULT、并且可能有一个对应于返回值的附加 [out, retval] 参数的非托管签名。

6、SetLastError 指示被调用方在从属性化方法返回之前将调用 Win32 API SetLastError。
true 指示调用方将调用 SetLastError,默认为 false。运行时封送拆收器将调用 GetLastError 并缓存返回的值,以防其被其他 API 调用重写。用户可通过调用 GetLastWin32Error 来检索错误代码。

二、参数类型:
1、数值型直接用对应的就可。(DWORD -> int , WORD -> Int16)

2、API中字符串指针类型 -> .net中string

3、API中句柄 (dWord) -> .net中IntPtr

4、API中结构 -> .net中结构或者类。
注意这种情况下,要先用StructLayout特性限定声明结构或类
公共语言运行库利用StructLayoutAttribute控制类或结构的数据字段在托管内存中的物理布局,即类或结构需要按某种方式排列。如果要将类传递给需要指定布局的非托管代码,则显式控制类布局是重要的。它的构造函数中用LayoutKind值初始化 StructLayoutAttribute 类的新实例。 LayoutKind.Sequential 用于强制将成员按其出现的顺序进行顺序布局。
LayoutKind.Explicit 用于控制每个数据成员的精确位置。利用 Explicit,每个成员必须使用 FieldOffsetAttribute 指示此字段在类型中的位置。如:
[StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]
public class MySystemTime
{
[FieldOffset(0)]public ushort wYear;
[FieldOffset(2)]public ushort wMonth;
[FieldOffset(4)]public ushort wDayOfWeek;
[FieldOffset(6)]public ushort wDay;
[FieldOffset(8)]public ushort wHour;
[FieldOffset(10)]public ushort wMinute;
[FieldOffset(12)]public ushort wSecond;
[FieldOffset(14)]public ushort wMilliseconds;
}

下面是针对API中OSVERSIONINFO结构,在.net中定义对应类或结构的例子:
/**********************************************
* API中定义原结构声明
* OSVERSIONINFOA STRUCT
* dwOSVersionInfoSize DWORD ?
* dwMajorVersion DWORD ?
* dwMinorVersion DWORD ?
* dwBuildNumber DWORD ?
* dwPlatformId DWORD ?
* szCSDVersion BYTE 128 dup (?)
* OSVERSIONINFOA ENDS
*
* OSVERSIONINFO equ
*********************************************/
//.net中声明为类
[ StructLayout( LayoutKind.Sequential )]
public class OSVersionInfo
{
public int OSVersionInfoSize;
public int majorVersion;
public int minorVersion;
public int buildNumber;
public int platformId;
[MarshalAs( UnmanagedType.ByValTStr, SizeConst=128 )]
public String versionString;
}
//或者
//.net中声明为结构
[ StructLayout( LayoutKind.Sequential )]
public struct OSVersionInfo2
{
public int OSVersionInfoSize;
public int majorVersion;
public int minorVersion;
public int buildNumber;
public int platformId;
[MarshalAs( UnmanagedType.ByValTStr, SizeConst=128 )]
public String versionString;
}

此例中用到MashalAs特性,它用于描述字段、方法或参数的封送处理格式。用它作为参数前缀并指定目标需要的数据类型。例如,以下代码将两个参数作为数据类型长指针封送给 Windows API 函数的字符串 (LPStr):
[MarshalAs(UnmanagedType.LPStr)]
String existingfile;
[MarshalAs(UnmanagedType.LPStr)]
String newfile;

注意结构作为参数时候,一般前面要加上ref修饰符,否则会出现错误:对象的引用没有指定对象的实例。
[DllImport("kernel32", EntryPoint="GetVersionEx")]
public static extern bool GetVersionEx2(ref OSVersionInfo2 osvi);

三、如何保证使用托管对象的平台调用成功?
如果在调用平台 invoke 后的任何位置都未引用托管对象,则垃圾回收器可能将完成该托管对象。这将释放资源并使句柄无效,从而导致平台invoke 调用失败。用 HandleRef 包装句柄可保证在平台 invoke 调用完成前,不对托管对象进行垃圾回收。
例如下面:
FileStream fs = new FileStream( "a.txt", FileMode.Open );
StringBuilder buffer = new StringBuilder( 5 );
int read = 0;
ReadFile(fs.Handle, buffer, 5, out read, 0 ); //调用Win API中的ReadFile函数

由于fs是托管对象,所以有可能在平台调用还未完成时候被垃圾回收站回收。将文件流的句柄用HandleRef包装后,就能避免被垃圾站回收:
[DllImport( "Kernel32.dll" )]
public static extern bool ReadFile(
HandleRef hndRef,
StringBuilder buffer,
int numberOfBytesToRead,
out int numberOfBytesRead,
ref Overlapped flag );
......
......
FileStream fs = new FileStream( "HandleRef.txt", FileMode.Open );
HandleRef hr = new HandleRef( fs, fs.Handle );
StringBuilder buffer = new StringBuilder( 5 );
int read = 0;
// platform invoke will hold reference to HandleRef until call ends
ReadFile( hr, buffer, 5, out read, 0 );

关于Single Sign On的一些设想

单点登陆,始终有其吸引力。然而就实现而言,结合栋力无限现有情况,比较困难。

主要原因在于栋力无限门户下属各个子站点,不仅有自己开发的,也有直接使用的他人开发的开源项目的。特别对于后者,如果花大力气,对其进行修改,使其使用我们自身的passport,一旦其有所升级、打patch等变动,就会导致大量的重复劳动。

在现有条件下,是否存在一种花费最小代价的Single Sign On实现方式?换句话说,使得插接到passport的各个子站点,所作的修改尽量的少?

最近有一个设想,就是如果想要尽可能少的修改各个子站点,实现passport的通用性,唯有通过模拟各个子站点自身的登陆行为。

法1、填写username password,并post到其form的action链接里面;
法2、直接在用户本机写入cookie文件;

两种方式其实都可以。具体一点说:

第一阶段试验目标:

1、登陆passport.dormforce.net后,passport修改用户登陆状态信息为已登录(并以Web Services或其他方式对外公开)。

2、passport调用放在各个子站点自身程序里的一个模板页面,那个页面实现的唯一功能就是采用法1或法2模拟登陆本子站点,若有必要,在这里,还可以增加对Session,Application等变量的支持;(由于开源,可以跟踪入内,找到其登陆代码,copy过来修改一下即可)(调用的时候,最好使用AJAX等技术后台加载,防止页面频繁刷新,提高用户体验)

3、passport调用放在各个子站点自身程序里的一个模板页面,那个页面实现的唯一功能就是采用法1或法2登出本子站点,若有必要,在这里,还可以增加对Session,Application等变量的支持;(由于开源,可以跟踪入内,找到其登出代码,copy过来修改一下即可)(调用的时候,最好使用AJAX等技术后台加载,防止页面频繁刷新,提高用户体验)

至此,应该能够实现在passport登陆后,畅通访问各个子站点;在passport登出后,退出所有子站点;

第二阶段试验目标:

1、通过用户登陆状态信息等其他验证安全的手段,确保放在各个自站点的模拟登陆页面安全。

2、修改各个子站点的登陆和登出链接,指向passport

至此,应该能够实现我们最初的目标。

我再稍微总结一下:

passport实现的功能就是登陆时,自动登陆各个站点;登出时,自动登出所有站点。这里和通常意义的passport不一样,因为通常意义的passport一般就只有一个cookie。

一个新插入的子站点(不管自身项目还是开源项目),若想使用passport认证,则进行如下操作:

1、找到登陆的代码,套入passport提供的模板页面中;
2、找到登陆和登出的链接,指向passport;

当然,对于第二步来说,仍然比较困难,但是较之完全替换其帐户系统,理论上讲应该轻松许多,这也是我目前能想到的代价最小的实现Single Sign On的方式了。

由于这只是一些设想,跟曾毅和卢浩森聊了一下,据称问题不大,而且曾毅貌似已在音乐站上测试成功第一阶段的目标,所以特贴出来,看看各位达人认为:

1、有可行性么?
2、有没有其他更好更贴近栋力无限自身特点的方式?

多文件批量上传

用一个页面实现文件上传,一般只需要使用HtmlInputFile的runat=server版本就行了,但是现在有些不同的地方:
1、直接上传到数据库的image类型的字段中,而不产生本地文件;
2、多个文件同时上传;
3、上传的文件组最后要同其他相关信息进行关联;

解决方法主要有两种:
1、每次上传一个文件到临时目录,返回并保存相关信息到ViewState,最后提交的时候,统一将文件写入数据库,并根据相关信息做关联;
2、每次上传一个文件到数据库,返回并保存相关信息到ViewState,最后提交的时候,根据相关信息做关联;
3、每次采集HtmlInputFile中的信息到ListBox,最后统一上传多个文件,并做相关关联;

无疑,第三种是其中最好的一种,但是上传多个文件却比较麻烦,本来想在提交的时候,分别多次设置 HtmlInputFile.PostedFile.FileName属性,再多次调用SaveAs进行上传,但在一次提交过程中它不允许重设 FileName属性(事后想想,确实应该不行:P)
后来又想在后台动态添加HtmlInputFile控件并且设其Visible属性为false,结果好像还是不行,因为还是不准自己指定那个怨念的FileName属性

结果:
增加文件按钮:
if( null == m_htmlInputFile )
m_htmlInputFile = new ArrayList();
m_htmlInputFile.Add( FileInputField );

其中FileInputField为System.Web.UI.HtmlControls.HtmlInputFile类型;
并且注意设置m_htmlInputFile这个ArrayList为static,以防每次增加文件后被刷新为null:(
这里其实就每次Add那个Html控件就行了,他们都对应相同的html控件,但是后台的对象每Add一次增加一个:)

提交按钮:
if( null == m_imageList )
m_imageList = new ArrayList();
if( null == m_imageTypeList )
m_imageTypeList = new ArrayList();
foreach( HtmlInputFile hif in m_htmlInputFile )
{
System.Drawing.Image image = System.Drawing.Image.FromStream( hif.PostedFile.InputStream );
m_imageList.Add( image );
m_imageTypeList.Add( hif.PostedFile.ContentType );
}

显示图片:
在GetImage.aspx?ImageID=*页面的Page_Load中
Response.ContentType = m_image.ImageType;
Response.BinaryWrite( m_image.Img );

注意采用BinaryWrite以及设置ContentType为image/pjpeg等

最后在保存文件到后端数据库的时候有点小不爽:
MemoryStream ms = new MemoryStream();
image.Save( ms, System.Drawing.Imaging.ImageFormat.Jpeg );//image是System.Drawing.Image
m_image = new byte[ms.Length];//m_image是byte[]
ms.Position = 0;
//If Too Long, ...
ms.Read( m_image, 0, Convert.ToInt32(ms.Length));

这里ms.Length是long类型,但是同一个类-MemoryStream的Read方法的第三个参数是int型,涉及到一个向下的类型转换,如果Length过大,应该这里会有问题,不知是怎么回事:(

发布Web Services

笔记本上Release后一切正常的Web Services,部署到另外一台2003的服务器上,出现两个问题:

1、Critical Error:

…没有权限访问注册表

其实只是在写入EventLog的时候ASPNET的权限不够,不过奇怪的是把ASPNET和匿名帐户加入Administrator组,重启,好像权限还是不够>.<

查看其列出的跟踪堆栈,发现好像是Enterprise Library中ADO.NET连接数据库的问题,在网上查了查,有用的文章不多,最后居然就在EntLib的FAQ里,发现是没有正确部署EnterLib程序集的问题。

安装程序工具(Installutil.exe)
允许通过执行指定程序集的安装程序组件来安装和卸载服务器资源。
注意: 在“%windir%Microsoft.NETFrameworkv1.1.xxxx”中可以找到Installutil.exe,其中xxxx是您使用的 .NET Framework 的内部版本号。

Installutil Microsoft.Practices.EnterpriseLibrary.Common.dll
Installutil Microsoft.Practices.EnterpriseLibrary.Configuration.dll
Installutil Microsoft.Practices.EnterpriseLibrary.Data.dll

一切恢复正常。

不过还是不知道为什么他的程序集部署的时候必须Install,而自己的dll就不用这一步。

PS:
删除用
Installutil /u xxx.dll

2、localhost访问没有问题,但用www.xxx.com访问,出错

在 .NET Framework 1.1 远程环境中,默认情况下 HTTP GET 和 HTTP POST 同时被禁用。这是出于安全方面的考虑。
当 Web 服务升级到 .NET Framework 1.1 后,应用程序使用 HTTP GET 或 HTTP POST 调用 Web 服务时会失败。
这些应用程序会收到以下一条错误信息 System.Net.WebException指出无法识别请求格式。

由于使用POST,现在已基本不可能转成SOAP协议,只有将其打开,更改Web.Config文件:
<configuration>
<system.web>
<webServices>
<protocols>
<!--
<add name="HttpGet">
-->
<add name="HttpPost"/>
</protocols>
</webServices>
</system.web>
</configuration>