| 一片枫叶 的个人资料追梦的人日志列表 | 帮助 |
|
追梦的人3月22日 类似boost库function的免delete委托实现困难不因你害怕而不出现,只因你勇敢面对迎刃而解
------ 鼓励已成大龄程序员的自己 近来试验着重写一个C++的委托功能,就是.net里的delegate关键字描述的东东,似乎C++里喜欢说是成员函数closure,记不太清楚了。 原来的实现跟loki库里的思路差不多,就是利用虚函数描述委托函数的原型,这个原型与具体实现类无关,子类实现函数的功能,实现 里可以记住具体实现函数的类实例,现在看来没有什么很巧妙的东东了: 1、接口层,纯虚接口,与实现对象无关。
class IEvent { public: virtual ~IEvent(){} public: virtual bool operator () (const EventArg& arg)=0; }; 2、实现层,利用模板参数化实现对象类型。
template<typename Obj> class Event : public IEvent { typedef bool (Obj::*Fptr)(const EventArg&); public: Event(Obj* pObj,Fptr fptr) { mObj=pObj; mFunc=fptr; } bool operator () (const EventArg& arg) { return (mObj->*mFunc)(arg); } private: Obj* mObj; Fptr mFunc; }; 3、创建函数,利用模板推导自动提供对象类型。 template<typename Obj> IEvent* create(Obj* pObj,bool (Obj::*fptr)(const EventArg&) { return new Event<Obj>(pObj,fptr); } boost库里的function不使用虚函数机制,而是使用安全的强制转换实现了这种委托功能,它的核心技术在于对成员函数closure
的对象封装(对象指针可以与普通指针互相强制转型)以及模板类的静态成员函数指针(函数原型可以不依赖模板类的模板参数 ,而函数实现可以使用模板类的模板参数,从而实现虚函数接口与实现动态绑定的类似效果)。 委托的实质其实是用一个普通函数原型(实现类无关的)关联一个函数closure对象(绑定到具体实现类的),一个是接口层的,
一个是实现层的。 不管是虚函数的实现还是基于强制转型的实现,closure对象的数据部分主要包含一个对象指针与一个函数指针,这两个指针在委
托的生命周期内是必须有效的,委托无需关心它们指向对象的释放,委托需要关心的是closure对象实例的释放,因为closure对象 有实现类的信息,不能直接作为委托的数据成员(否则委托方就被绑定到一个具体实现类了,而委托的涵义是可以有任意类型的实 现类),所以基于虚函数的实现与boost库的实现均使用了堆内存分配closure对象,从而强加给委托方一个释放的职责。 closure对象因为只是两个指针,占用的空间很小,如果不倒致类型依赖的话,作为委托方的数据成员是完全合适的,由此我们可 以看到一个底层的实现,假设委托方类为Delegation,closure对象类为Closure: class Delegation { public: ... 接口层的东西 ... private: char space[sizeof(Closure)]; }; Delegation d;
Closure c; *(Closure*)&(d.space)=c;//直接浅拷贝 经过这样的处理,Delegation的成员space的空间里就会保存一个Closure实例的数据,在实现代码里再将此数据强制转成Closure
对象也就没问题了(boost的实现里的模板类静态成员函数invoke里正适合做此工作),这里还有一个问题,就是sizeof(Closure) ,其中出现了Closure,会倒致Delegation对Closure的依赖,不过我们只是想知道Closure实例的大小,这个依赖似乎是可以消除的 ,前面说过,Closure里主要是保存一个对象指针与一个成员函数指针,我们只需要伪造一个类似的虚拟类,用这个虚拟类来获取 实例大小即可(也就是Delegation依赖一个虚拟类获取大小,这个大小却适用于任何Closure对象),因为涉及成员函数指针,C++对 有多重继承的类与无多重继承的类的成员函数指针大小是不一样的,我们这里需要取尺寸的最大值,也就是使用一个多重继承的虚 拟类。 上面的方法是利用对象实例浅拷贝来存储实例数据的,与此类似,还有一个办法是像内存池的实现一样重载new操作,采用带placement
参数的new重载,明确的将空间指定,下面我们给出基于重载new的实现,对我而言,用重载new感觉上比对象实例浅拷贝更优雅一些。 //提供Closure存储空间的虚拟类
struct ObjectPlaceHolder { //用来模拟实际情况类的虚拟类,成员函数指针在有继承与无继承下大小不一样, //ObjectPlaceHolder需要提供足够的空间供MemFuncInvoker...实例存储,所以需 //要按多继承情形模拟成员函数指针。 class DummyBaseA {}; class DummyBaseB {}; class DummyClass : public DummyBaseA, public DummyBaseB {}; public: ObjectPlaceHolder() { clear(); } ObjectPlaceHolder(const ObjectPlaceHolder& other) { obj=other.obj; fptr=other.fptr; } inline bool isNull(void) const { return obj==NULL || fptr==NULL; } inline void clear(void) { obj=NULL; fptr=NULL; } private: DummyClass* obj; void (DummyClass::*fptr)(void); }; //Closure对象实现,同时实现了静态成员函数接口invoke,这是实现虚函数相似功能的关键
//重载的new方法使用指定的空间作为对象实例化的空间,因为我们实际上是用的委托方的空 //间,并不需要释放,所以delete方法什么也不做 template<typename Delegation,typename T,typename R> struct MemFuncInvoker0 { private: friend typename Delegation; typedef MemFuncInvoker0<Delegation,T,R> ThisType; typedef R (T::*Fptr)(void); MemFuncInvoker0(T* obj,Fptr func) { mObj=obj; mFunc=func; } static inline R invoke(const ObjectPlaceHolder* address) { const ThisType* pThis=reinterpret_cast<const ThisType*>(address); T* pObj=pThis->mObj; Fptr pFunc=pThis->mFunc; return (pObj->*pFunc)(); } static inline void* operator new (size_t size,const ObjectPlaceHolder* address,size_t space) { _ASSERT(size<=space); return (void*)address; } static inline void operator delete(void*,const ObjectPlaceHolder* address,size_t space) {} private: T* mObj; Fptr mFunc; }; //委托的实现
template<typename R> struct Delegation0 { public: typedef Delegation0<R> ThisType; typedef R (*InvokerType)(const ObjectPlaceHolder*); public: Delegation0() { detach(); } Delegation0(const Delegation0& other) { mObj=other.mObj; invoker=other.invoker; } template<typename T> Delegation0(T* obj,R (T::*fptr)(void)) { attach(obj,fptr); } template<typename T> inline void attach(T* obj,R (T::*fptr)(void)) { invoker=MemFuncInvoker0<ThisType,T,R>::invoke; new(&mObj,sizeof(ObjectPlaceHolder)) MemFuncInvoker0<ThisType,T,R>(obj,fptr); } inline void detach(void) { mObj.clear(); invoker=NULL; } inline bool isNull(void) const { return mObj.isNull() || invoker==NULL; } inline R operator () (void) { return invoker(&mObj); } private: ObjectPlaceHolder mObj;//用来保存MemFuncInvoker实例的空间 InvokerType invoker; }; 上面的实现是对有一个返回类型(含void)无参数的函数样式的委托,一个典型用法如下:
class A { public: char* run(void) { if(!onRun.isNull()) return onRun(); } public: Delegation0<char*> onRun; }; class B
{ public: char* test(void) { return "B::test called !"; } }; int main(void) { A a; B b; a.onRun.attach(&b,&B::test); std::cout<<a.run()<<std::endl; ::_getch(); return 0; } 可以看到动态的将A::run委托到B::test的实现了。 11月20日 笔记:DataGridView用对象做数据源时可能存在一个BUG做了一个小的试验,一个DataGridView,一个BindingSource,然后用List<T>作实际的数据源。
当T为struct时,编辑功能失效,修改完按enter,cell里的内容立即恢复为初始值。
大致看了一下DataGridView的操作过程,
1、DataGridView用户操作的事件处理,比如按键、鼠标等;
2、DataGridView.CommitEdit方法;
3、DataGridViewCell.SetValue方法;
4、DataGridView.DataGridViewDataConnection.PushValue方法;
5、PropertyDescriptor.SetValue方法。
以前在用PropertyGrid时曾经研究过像PropertyDescriptor这一类.net设计时架构的关键元素,依稀记得对于struct与对object的处理有很大
的不同,于是试了试将T由struct改为class,再试,编辑功能正常。
没有时间研究具体原因了,以后有兴趣时再说吧,先记下。
从MS的几个大部头控件可以看到这向个控件背后有一个庞大的架构支撑,不过MS在介绍它们时就像介绍别的普通控件一样一寥寥数笔,实在是
令人费解,而且这个几个大部头控件的实现类相当多,不过大多为internal不为用户所见,如果不使用reflector这样的反编译工具,使用这些控件时出问题基本上是一头雾水的。
另外一点,MS的reflection API对.net framework的几个架构的支持非常重要,而reflection API基本上是目前.net程序安全的一个大问题,不知道以后MS如何处理这个矛盾(当然,从open source的角度看,这里本没有矛盾,呵呵)。 11月8日 笔记:为ntdll.dll创建移入库ntdll.lib本来以为很简单的,结果费了一番周折,为了防止下次重蹈覆辙,记个笔记。
一开始的想法:
1、用impdef或dumpbin生成一个ntdll.dll的模块定义文件ntdll.def;
2、用lib /def:ntdll.def生成ntdll.lib;
3、按网上hacker给出的函数原形写个头文件ntdll.h.
结果编译时说无法链接_LdrLoadDll@16,看起来是__stdcall调用约定修饰名的原因,但是找不到办法将链接时的_LdrLoadDll@16转换到DLL实际输出的名称LdrLoadDll,查def文件格式时有EXPORTS部分的写法:
entryname[=internalname] [@ordinal [NONAME]] [PRIVATE] [DATA]
以为可以用这种方法换名,于是改def文件:
重新生成ntdll.lib后程序编译通过,但是运行提示找不到_LdrLoadDll@16入口点,看来这次反过来了,移入库里我名称对了,但还是没对应到DLL实际输出的名称。
想不出办法了,所以到网上搜了一下,发觉原MS在DDK提供了一个ntdll.lib,汗!
最后不知道从哪看到一个说自己建一个dll工程ntdll,然后生成移入库,于是试了试,自己建一个纯win32 dll项目,加一个模块定义文件:
EXPORTS
LdrLoadDll
再在程序里写一个与ntdll.dll里LdrLoadDll原型一样的空函数,编译一下,得到一个ntdll.lib,用这个编译原来的程序,编译通过,运行,
也OK了,用dumpbin看一下生成的ntdll.lib,呵呵,居然它就是用的_LdrLoadDll@16这个名字,但它却能正确的定位ntdll.dll里的LdrLoadDll,搞不明白了,为什么,lib /def:ntdll.def生成的库就不行:(
这方法麻烦点,不过可行,就这样吧,MS的东西,搞不明白的太多了!
8月16日 .NET里的优化太差了!一般来说编译型,甚至许多解释型的语言教程里会说:请不要自己优化代码,语言编译器是足够聪明的。不过今天我却发现.NET 2.0的优化实在是太。。。。。。了!
我测试了一下foreach与for循环的代码(为了防止误解编译器的优化能力,我加了一个多线程锁,.NET工程没有单线程与多线程的属性,所以我猜测编译器不敢做优化是害怕多线程并发修改变量,于是特地告诉它这块不会有并发修改的情况):
private void TestColl()
{ List<int> intList=new List<int>(); for(int i=0;i<10;i++) { intList.Add(i); } lock (intList) { for (int i = 0; i < intList.Count; i++) { intList[i].CompareTo(1); intList[i].Equals(1); intList[i].GetHashCode(); intList[i].GetTypeCode(); intList[i].ToString(); } } foreach(int i in intList) { i.CompareTo(1); i.Equals(1); i.GetHashCode(); i.GetTypeCode(); i.ToString(); } } 我知道foreach是利用IEnumerable接口实现的,也就是它会在每次循环调用get_Current与MoveNext来得到集合中的下一个元素,而for语句是直白的,它按我们所写的那样去做,现在我假想一下一般编译器应该会做的优化(第二个循环),在循环开始调get_Item(i)来得到集合中元素的值,然后用这个值依次执行后续的方法调用,因为在这段代码里我们并没有改变集合中的元素,而且这段代码加锁了,确保集合在这期间不会被别的线程改变,所以先取出集合中的元素,再访问它的若干方法是最常见的提取公共表达式相似的优化。那么.NET是这么做的吗?我们来看一下对应的IL:
.method private hidebysig instance void TestColl() cil managed
{ .maxstack 2 .locals init ( [0] [mscorlib]System.Collections.Generic.List`1<int32> list1, [1] int32 num1, [2] int32 num2, [3] int32 num3, [4] [mscorlib]System.Collections.Generic.List`1<int32> list2, [5] int32 num4, [6] int32 num5, [7] int32 num6, [8] int32 num7, [9] int32 num8, [10] [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> enumerator1) L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<int32>::.ctor() L_0005: stloc.0 L_0006: ldc.i4.0 L_0007: stloc.1 L_0008: br.s L_0015 L_000a: ldloc.0 L_000b: ldloc.1 L_000c: callvirt instance void [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0) L_0011: ldloc.1 L_0012: ldc.i4.1 L_0013: add L_0014: stloc.1 L_0015: ldloc.1 L_0016: ldc.i4.s 10 L_0018: blt.s L_000a L_001a: ldloc.0 L_001b: dup L_001c: stloc.s list2 L_001e: call void [mscorlib]System.Threading.Monitor::Enter(object) L_0023: ldc.i4.0 L_0024: stloc.2 L_0025: br.s L_0082 L_0027: ldloc.0 L_0028: ldloc.2 L_0029: callvirt instance !0 [mscorlib]System.Collections.Generic.List`1<int32>::get_Item(int32) L_002e: stloc.s num4 L_0030: ldloca.s num4 L_0032: ldc.i4.1 L_0033: call instance int32 int32::CompareTo(int32) L_0038: pop L_0039: ldloc.0 L_003a: ldloc.2 L_003b: callvirt instance !0 [mscorlib]System.Collections.Generic.List`1<int32>::get_Item(int32) L_0040: stloc.s num5 L_0042: ldloca.s num5 L_0044: ldc.i4.1 L_0045: call instance bool int32::Equals(int32) L_004a: pop L_004b: ldloc.0 L_004c: ldloc.2 L_004d: callvirt instance !0 [mscorlib]System.Collections.Generic.List`1<int32>::get_Item(int32) L_0052: stloc.s num6 L_0054: ldloca.s num6 L_0056: call instance int32 int32::GetHashCode() L_005b: pop L_005c: ldloc.0 L_005d: ldloc.2 L_005e: callvirt instance !0 [mscorlib]System.Collections.Generic.List`1<int32>::get_Item(int32) L_0063: stloc.s num7 L_0065: ldloca.s num7 L_0067: call instance [mscorlib]System.TypeCode int32::GetTypeCode() L_006c: pop L_006d: ldloc.0 L_006e: ldloc.2 L_006f: callvirt instance !0 [mscorlib]System.Collections.Generic.List`1<int32>::get_Item(int32) L_0074: stloc.s num8 L_0076: ldloca.s num8 L_0078: call instance string int32::ToString() L_007d: pop L_007e: ldloc.2 L_007f: ldc.i4.1 L_0080: add L_0081: stloc.2 L_0082: ldloc.2 L_0083: ldloc.0 L_0084: callvirt instance int32 [mscorlib]System.Collections.Generic.List`1<int32>::get_Count() L_0089: blt.s L_0027 L_008b: leave.s L_0095 L_008d: ldloc.s list2 L_008f: call void [mscorlib]System.Threading.Monitor::Exit(object) L_0094: endfinally L_0095: ldloc.0 L_0096: callvirt instance [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> [mscorlib]System.Collections.Generic.List`1<int32>::GetEnumerator() L_009b: stloc.s enumerator1 L_009d: br.s L_00d1 L_009f: ldloca.s enumerator1 L_00a1: call instance !0 [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::get_Current() L_00a6: stloc.3 L_00a7: ldloca.s num3 L_00a9: ldc.i4.1 L_00aa: call instance int32 int32::CompareTo(int32) L_00af: pop L_00b0: ldloca.s num3 L_00b2: ldc.i4.1 L_00b3: call instance bool int32::Equals(int32) L_00b8: pop L_00b9: ldloca.s num3 L_00bb: call instance int32 int32::GetHashCode() L_00c0: pop L_00c1: ldloca.s num3 L_00c3: call instance [mscorlib]System.TypeCode int32::GetTypeCode() L_00c8: pop L_00c9: ldloca.s num3 L_00cb: call instance string int32::ToString() L_00d0: pop L_00d1: ldloca.s enumerator1 L_00d3: call instance bool [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext() L_00d8: brtrue.s L_009f L_00da: leave.s L_00ea L_00dc: ldloca.s enumerator1 L_00de: constrained [mscorlib]System.Collections.Generic.List`1/Enumerator<int32> L_00e4: callvirt instance void [mscorlib]System.IDisposable::Dispose() L_00e9: endfinally L_00ea: ret .try L_0023 to L_008d finally handler L_008d to L_0095 .try L_009d to L_00dc finally handler L_00dc to L_00ea } 代码挺长的,不过没必要仔细看,数一下get_Item的个数就知道它有没有提取这个公共调用语句了,它的确没有做这种优化,为什么呢?难道是留给JIT编译器来做?不明白了,看来以后遇到这种有公共调用语句的情形,需要自己搞一个临时变量暂存一下结果,如果是集合的话,则优先使用foreach,这样的语句正好帮我们提取了这个公共调用。 VS.NET 里面即使是Release状态也是可以调试的,这有点出人意料,在这个函数里断下来,打开反汇编,可以看到如下显示:
private void TestColl()
{ List<int> intList=new List<int>(); 00000000 push ebp 00000001 mov ebp,esp 00000003 push edi 00000004 push esi 00000005 push ebx 00000006 sub esp,60h 00000009 mov esi,ecx 0000000b lea edi,[ebp-6Ch] 0000000e mov ecx,10h 00000013 xor eax,eax 00000015 rep stos dword ptr es:[edi] 00000017 mov ecx,esi 00000019 xor eax,eax 0000001b mov dword ptr [ebp-18h],eax 0000001e mov dword ptr [ebp-24h],ecx 00000021 cmp dword ptr ds:[00A79BC0h],0 00000028 je 0000002F 0000002a call 752AEFF6 0000002f xor edx,edx 00000031 mov dword ptr [ebp-68h],edx 00000034 xor edx,edx 00000036 mov dword ptr [ebp-28h],edx 00000039 xor edx,edx 0000003b mov dword ptr [ebp-2Ch],edx 0000003e xor edx,edx 00000040 mov dword ptr [ebp-6Ch],edx 00000043 mov ecx,7919F9FCh 00000048 call FBC7ED14 0000004d mov esi,eax 0000004f mov ecx,esi 00000051 call 74790818 00000056 mov dword ptr [ebp-68h],esi for(int i=0;i<10;i++) 00000059 xor edx,edx 0000005b mov dword ptr [ebp-28h],edx 0000005e nop 0000005f jmp 00000071 { intList.Add(i); 00000061 mov edx,dword ptr [ebp-28h] 00000064 mov ecx,dword ptr [ebp-68h] 00000067 cmp dword ptr [ecx],ecx 00000069 call 74791BA8 for(int i=0;i<10;i++) 0000006e inc dword ptr [ebp-28h] 00000071 cmp dword ptr [ebp-28h],0Ah 00000075 jl 00000061 } lock (intList) 00000077 mov eax,dword ptr [ebp-68h] 0000007a mov dword ptr [ebp-6Ch],eax 0000007d mov ecx,dword ptr [ebp-6Ch] 00000080 call 75090841 { for (int i = 0; i < intList.Count; i++) 00000085 xor edx,edx 00000087 mov dword ptr [ebp-2Ch],edx 0000008a nop 0000008b jmp 00000124 { intList[i].CompareTo(1); 00000090 mov edx,dword ptr [ebp-2Ch] 00000093 mov ecx,dword ptr [ebp-68h] 00000096 cmp dword ptr [ecx],ecx 00000098 call 74791278 0000009d mov esi,eax 0000009f mov dword ptr [ebp-34h],esi 000000a2 lea ecx,[ebp-34h] 000000a5 mov edx,1 000000aa call 7459E490 000000af nop intList[i].Equals(1); 000000b0 mov edx,dword ptr [ebp-2Ch] 000000b3 mov ecx,dword ptr [ebp-68h] 000000b6 cmp dword ptr [ecx],ecx 000000b8 call 74791278 000000bd mov esi,eax 000000bf mov dword ptr [ebp-38h],esi 000000c2 lea ecx,[ebp-38h] 000000c5 mov edx,1 000000ca call 7457AD14 000000cf nop intList[i].GetHashCode(); 000000d0 mov edx,dword ptr [ebp-2Ch] 000000d3 mov ecx,dword ptr [ebp-68h] 000000d6 cmp dword ptr [ecx],ecx 000000d8 call 74791278 000000dd mov esi,eax 000000df mov dword ptr [ebp-3Ch],esi 000000e2 lea ecx,[ebp-3Ch] 000000e5 call 7456B10C 000000ea nop intList[i].GetTypeCode(); 000000eb mov edx,dword ptr [ebp-2Ch] 000000ee mov ecx,dword ptr [ebp-68h] 000000f1 cmp dword ptr [ecx],ecx 000000f3 call 74791278 000000f8 mov esi,eax 000000fa mov dword ptr [ebp-40h],esi 000000fd lea ecx,[ebp-40h] 00000100 call 7456B398 00000105 nop intList[i].ToString(); 00000106 mov edx,dword ptr [ebp-2Ch] 00000109 mov ecx,dword ptr [ebp-68h] 0000010c cmp dword ptr [ecx],ecx 0000010e call 74791278 00000113 mov esi,eax 00000115 mov dword ptr [ebp-44h],esi 00000118 lea ecx,[ebp-44h] 0000011b call 7456B400 00000120 nop for (int i = 0; i < intList.Count; i++) 00000121 inc dword ptr [ebp-2Ch] 00000124 mov edi,dword ptr [ebp-2Ch] 00000127 mov ecx,dword ptr [ebp-68h] 0000012a cmp dword ptr [ecx],ecx 0000012c call 74790EDC 00000131 mov esi,eax 00000133 cmp edi,esi 00000135 jl 00000090 0000013b nop 0000013c mov dword ptr [ebp-1Ch],0 00000143 mov dword ptr [ebp-18h],0FCh 0000014a push 4DE3503h 0000014f jmp 00000151 00000151 mov ecx,dword ptr [ebp-6Ch] 00000154 call 75090ABB 00000159 pop eax 0000015a jmp eax foreach(int i in intList) 0000015c lea edx,[ebp-64h] 0000015f mov ecx,dword ptr [ebp-68h] 00000162 cmp dword ptr [ecx],ecx 00000164 call 747971F8 00000169 lea edi,[ebp-54h] 0000016c lea esi,[ebp-64h] 0000016f movs dword ptr es:[edi],dword ptr [esi] 00000170 movs dword ptr es:[edi],dword ptr [esi] 00000171 movs dword ptr es:[edi],dword ptr [esi] 00000172 movs dword ptr es:[edi],dword ptr [esi] 00000173 nop 00000174 jmp 000001BA 00000176 lea ecx,[ebp-54h] 00000179 call 747972B8 0000017e mov esi,eax 00000180 mov dword ptr [ebp-30h],esi { i.CompareTo(1); 00000183 lea ecx,[ebp-30h] 00000186 mov edx,1 0000018b call 7459E490 00000190 nop i.Equals(1); 00000191 lea ecx,[ebp-30h] 00000194 mov edx,1 00000199 call 7457AD14 0000019e nop i.GetHashCode(); 0000019f lea ecx,[ebp-30h] 000001a2 call 7456B10C 000001a7 nop i.GetTypeCode(); 000001a8 lea ecx,[ebp-30h] 000001ab call 7456B398 000001b0 nop i.ToString(); 000001b1 lea ecx,[ebp-30h] 000001b4 call 7456B400 000001b9 nop foreach(int i in intList) 000001ba lea ecx,[ebp-54h] 000001bd call 7479725C 000001c2 mov esi,eax 000001c4 test esi,esi 000001c6 jne 00000176 000001c8 nop 000001c9 mov dword ptr [ebp-1Ch],0 000001d0 mov dword ptr [ebp-18h],0FCh 000001d7 push 4DE34FAh 000001dc jmp 000001DE 000001de lea ecx,[ebp-54h] 000001e1 call 74797250 000001e6 pop eax 000001e7 jmp eax } } 000001e9 nop 000001ea lea esp,[ebp-0Ch] 000001ed pop ebx 000001ee pop esi 000001ef pop edi 000001f0 pop ebp 000001f1 ret 000001f2 mov dword ptr [ebp-18h],0 000001f9 jmp 000001E9 000001fb mov dword ptr [ebp-18h],0 00000202 jmp 0000015C 这些应该便是JIT编译后的最终代码了,不过在VS.NET中调试运行程序时JIT是按调试状态处理的代码,所以这里没办法确认JIT在实际运行时是否会做优化,反正上面的代码是没有优化的了。
注:
上面我说.NET编译器没做优化的原因欠妥,真实的原因可能是.NET语言中方法没有const修饰这个设计缺陷倒致编译器不能确认何时能提取公共调用语句,我没想到的那种情形牛顿顿说的挺明白的(呵呵,他总是想得比较全面):
“
这个,这个似乎不应该优化吧。
foreach 是不是只针对一个特定的可枚举的接口工作?(这和C++的原生数组不同--原生数组是编译器完全理解和控制的,反而和重载的[]很像)
那么实际上编译器不知道各个GetItem(我不了解具体的名字是什么)是否有副作用。
比如说我实现的GetItem 不恰当的在内部统计 本方法的调用次数。如果按照你说的思路自动优化,那么该计数必然失败。
也就是说,该优化是需要了解实现的,编译器很难基于接口外的具体实现去进行优化。 ” 8月3日 在.NET里使用脚本语言jscript.NET框架库里包含了对MS推出的几个语言的完整编译支持,所以这些语言理论上都可以用来作脚本,不过脚本是随手写的,还是动态语言作为脚本比较合适,我指的这个动态语言就是jscript,在.NET库里对编译的支持集中在一个抽象类System.CodeDom.Compiler.CodeDomProvider,编译与装入一个脚本的写法如下:
CodeDomProvider cdp=new JScriptCodeProvider();
CompilerParameters args = new CompilerParameters();
args.GenerateExecutable = false; args.GenerateInMemory = true; args.ReferencedAssemblies.Add("System.dll"); args.ReferencedAssemblies.Add("System.Data.dll"); args.ReferencedAssemblies.Add("System.Web.dll"); args.ReferencedAssemblies.Add("System.Windows.Forms.dll");
//执行编译,字符串参数scriptCode为脚本内容
CompilerResults res = cdp.CompileAssemblyFromSource(args, scriptCode);
if (res.Output.Count > 0) return null;//编译失败 //执行装入实例的功能,指定全限定名,设为MyLib.MyType
MyType obj=res.CompiledAssembly.CreateInstance("MyLib.MyType") as MyType; 之后就可以调用obj的方法了,上面的MyType是一个定义在宿主程序里的抽象类,脚本中继承这个类以提供具体的功能实现,使用宿主定义抽象类的好处在于宿主程序里可以像常规库一样的使用脚本提供的功能,而不需要使用Type.InvokeMember的怪异句法调用脚本,此外,通过抽象类宿主还可以向脚本展示宿主对象模型。
一个典型的抽象类如下:
public abstract class MyType
{
public MyDOM Dom
{
get{return dom;}
set{dom=value;}
}
public abstract void Run();
}
在脚本里的作法:
public class JSObj extends MyType
{
public override function Run()
{
//脚本功能实现代码
}
}
在宿主程序中将入脚本后,如前述我们得到了MyType实例obj,则调用脚本的过程为:
obj.Dom=innerDom;//向脚本暴露内部对象模型,虽然也可以直接在Run的参数里传递,不过用参数传递的对象数量不能
//过多,如果不是封装成一个根对象的DOM,则可以用多个特性提供。
obj.Run();//执行脚本提供的功能
上面这个过程脚本修改过便需要重新编译再装入,编译会消耗一些时间,我们可以利用JScript语言的特性来提供一个解释式的脚本,那就是使用eval函数。只需要改变一个JSObj类就可以了:
public class JSObj extends MyType
{
public override function Run()
{
//假设我们的脚本代码由一个控件提供,这个控件实例通过抽象类的特性Dom.ScriptEdit提供.
eval(Dom.ScriptEdit.Text,"unsafe");//unsafe参数比较关键,不然脚本会有诸多限制
}
}
现在只需要编译装入一次,之后每次修改Dom.ScriptEdit.Text之后,只要调用obj.Run执行就可以了。
解释执行虽然没有编译的时间消耗,但是执行效率会远低于编译执行,所以我的想法是解释与编译同时提供,解释用于执行短小的脚本,一般这类用于试验或测试,编译用于正式的功能脚本,性能较高。
解释与编译的另一个差别是JScript的eval里的代码不能识别Array,不过可以识别IList,做法很简单,将Array型的对象强制转为IList即可:
var listObj=IList(arrayObj);
.net 2.0开始支持泛型,不过JScript不能访问使用非封闭的泛型参数的方法(参照一下C++的函数模板,这种调用基于模板参数推导,作为动态语言可能不能提供足够的类型信息吧,这是我的猜测:) ),所以如果宿主的DOM里有这样的方法,需要在宿主提供这些方法的非泛型版本(其实就是对每个封闭的泛型参数写一个方法,方法实现就是调相应的泛型方法)。
在实际使用中,我对脚本的编译进行了封装,然后脚本代码主要基于模板实现,因为脚本通常是随手写的,通常不需要定义类与方法,往往就是一个代码片断而已,但是如前所述脚本代码需要实现抽象类,这些框架性的东西用一个模板来提供就可以了。
封装类代码如下:
using System;
using System.Collections.Generic; using System.CodeDom.Compiler; using System.IO; namespace MeshLib
{ public class ScriptInterpreter<T> where T : CodeDomProvider,new() { public ScriptInterpreter() {} /// <summary> /// 借用一个模板文件来构造一个含有一个装配与一个类的script源代码,模板文件中必须有二个可替换变量:#ASSEMBLYNAME#,#CLASSNAME#,分别对应装配名、类名。 /// </summary> public static string ReadTemplate(string templateFile, string assemblyName, string className) { try { StreamReader sr = new StreamReader(templateFile); string templateCode = sr.ReadToEnd(); sr.Close(); return templateCode.Replace("#ASSEMBLYNAME#", assemblyName).Replace("#CLASSNAME#", className); } catch(Exception) { return ""; } } public List<string> ReferencedAssemblies { get { return referencedAssemblies; } } public string CompilerOptions { get { return compilerOptions; } set { compilerOptions = value; } } public List<string> Results { get { return compilerResults; } } public R LoadCode<R>(string scriptCode, string assemblyName, string className) where R : class { try { T cdp = new T(); CompilerParameters args = new CompilerParameters(); args.GenerateExecutable = false; args.GenerateInMemory = true; foreach (string s in ReferencedAssemblies) { args.ReferencedAssemblies.Add(s); } args.CompilerOptions = CompilerOptions; CompilerResults res = cdp.CompileAssemblyFromSource(args, scriptCode); Results.Clear(); foreach (string s in res.Output) { Results.Add(s); } if (res.Output.Count > 0) return null; return res.CompiledAssembly.CreateInstance(assemblyName + "." + className) as R; } catch(Exception eee) { Results.Add(eee.Message); return null; } } public R LoadFile<R>(string file, string assemblyName, string className) where R : class { string content = ReadTemplate(file, assemblyName, className); if (content.Length <= 0) return null; return LoadCode<R>(content, assemblyName, className); } private List<string> referencedAssemblies = new List<string>();
private string compilerOptions = null; private List<string> compilerResults = new List<string>(); } } 脚本模板(一个编译型的模板,一个解释型的模板)
编译型:
import System;
import System.Collections; import System.Collections.Generic; import System.Data; import System.Data.OleDb; import System.IO; import System.Net; import System.Net.Sockets; import System.Diagnostics; import System.Drawing; import System.Web; import System.Web.SessionState; import System.Web.UI; import System.Web.UI.WebControls; import System.Web.UI.HtmlControls; import System.Text; import System.Text.RegularExpressions; import System.Windows.Forms; import System.Threading; import System.Xml; import MeshLib.ShapeFile; import MeshLib; import MeshUI; package #ASSEMBLYNAME#
{ public class #CLASSNAME# extends ScriptCode { public override function Run() { //此处开始写您的脚本代码 } } } 解释型: import System;
import System.Collections; import System.Collections.Generic; import System.Data; import System.Data.OleDb; import System.IO; import System.Net; import System.Net.Sockets; import System.Diagnostics; import System.Drawing; import System.Web; import System.Web.SessionState; import System.Web.UI; import System.Web.UI.WebControls; import System.Web.UI.HtmlControls; import System.Text; import System.Text.RegularExpressions; import System.Windows.Forms; import System.Threading; import System.Xml; import MeshLib.ShapeFile; import MeshLib; import MeshUI; package #ASSEMBLYNAME#
{ public class #CLASSNAME# extends ScriptCode { public override function Run() { eval(script.Text,"unsafe"); } } } 抽象类ScriptCode: public abstract class ScriptCode { public ShapeFile.ShapeFile shapeFile; public MeshPanel meshPanel; public Control script; public Control output; public Mesh<SharedData,NodeData, SideData, CellData> mesh; public List<Mesh<GisSharedData, GisNodeData, GisSideData, GisCellData>> gisMeshes; public abstract void Run(); } 针对编译型与解释型脚本的编译运行与编译+多次运行的方法
编译运行是将模板文件装入供用户编辑,然后直接编译运行最终编辑过的完整代码:
private void CheckCScript() { if (cscriptEdit.Text.Trim().Length <= 0) { string path = Application.ExecutablePath; path = Path.GetDirectoryName(path); string tempFile = Path.Combine(path, "CompileScript.js"); cscriptEdit.Text = ScriptInterpreter<JScriptCodeProvider>.ReadTemplate(tempFile, "CompiledMeshScript", "CompiledJScript"); } } private void CompileAndRunScript() { try { string path = Application.ExecutablePath; path = Path.GetDirectoryName(path); scriptResult.Text = ""; ScriptInterpreter<JScriptCodeProvider> si = new ScriptInterpreter<JScriptCodeProvider>(); si.ReferencedAssemblies.Add("System.dll"); si.ReferencedAssemblies.Add("System.Data.dll"); si.ReferencedAssemblies.Add("System.Drawing.dll"); si.ReferencedAssemblies.Add("System.Web.dll"); si.ReferencedAssemblies.Add("Microsoft.JScript.dll"); si.ReferencedAssemblies.Add("System.Windows.Forms.dll"); si.ReferencedAssemblies.Add(Path.Combine(path, "MeshLib.dll")); si.ReferencedAssemblies.Add(Path.Combine(path, "MeshUI.exe")); ScriptCode compiledScriptCode = si.LoadCode<ScriptCode>(cscriptEdit.Text, "CompiledMeshScript", "CompiledJScript"); if (compiledScriptCode != null) { compiledScriptCode.shapeFile = mainForm.shapeFile; compiledScriptCode.meshPanel = mainForm.meshPanel; compiledScriptCode.script = cscriptEdit; compiledScriptCode.output = scriptResult; compiledScriptCode.mesh = mainForm.mesh; compiledScriptCode.gisMeshes = mainForm.gisMeshes; compiledScriptCode.Run(); } else { string ss = "编译错误:"; foreach (string s in si.Results) { ss += "\r\n" + s; } scriptResult.Text = ss; } } catch (Exception eee) { scriptResult.Text = "产生异常:" + eee.Message; } } 解释型脚本检查是否更新,如有更新则编译:
private void CheckScript() { try { string path = Application.ExecutablePath; path = Path.GetDirectoryName(path); string tempFile = Path.Combine(path, "EvalScript.js"); DateTime dt = File.GetLastWriteTime(tempFile);
if (scriptTime != dt) { scriptTime = dt; } else { return; } ScriptInterpreter<JScriptCodeProvider> si = new ScriptInterpreter<JScriptCodeProvider>();
si.ReferencedAssemblies.Add("System.dll"); si.ReferencedAssemblies.Add("System.Data.dll"); si.ReferencedAssemblies.Add("System.Drawing.dll"); si.ReferencedAssemblies.Add("System.Web.dll"); si.ReferencedAssemblies.Add("Microsoft.JScript.dll"); si.ReferencedAssemblies.Add("System.Windows.Forms.dll"); si.ReferencedAssemblies.Add(Path.Combine(path, "MeshLib.dll")); si.ReferencedAssemblies.Add(Path.Combine(path, "MeshUI.exe")); scriptCode = si.LoadFile<ScriptCode>(tempFile, "MeshScript", "JScript"); if (scriptCode == null) { string ss = "编译脚本文件" + tempFile + "出现了错误:"; foreach (string s in si.Results) { ss += "\r\n" + s; } scriptResult.Text = ss; } } catch (Exception eee) { scriptResult.Text = "产生异常:" + eee.Message; } } private void RunScript()
{ if (scriptCode != null) { try { scriptResult.Text = ""; scriptCode.shapeFile = mainForm.shapeFile; scriptCode.meshPanel = mainForm.meshPanel; scriptCode.script = iscriptEdit; scriptCode.output = scriptResult; scriptCode.mesh = mainForm.mesh; scriptCode.gisMeshes = mainForm.gisMeshes; scriptCode.Run(); } catch (Exception eee) { scriptResult.Text = "产生异常:" + eee.Message; } } } 使用PropertyGrid来为应用提供兼容.NET设计时架构的特性编辑功能只要在应用中使用PropertyGrid控件来提供对应用中的对象模型实例的特性编辑,这便已经是兼容了.NET设计时架构了,不过我发现一个问题,
当我们需要一次性编辑许多对象的时候,量变引起质变,此时严重的性能问题会让我们怀疑是不是不该使用PropertyGrid了(这种量变引起质变的现象在使用第三方库的时候好象经常碰到,大概原因在于库的设计者当初定位的使用环境是常规的,而量变到一个程度时,问题已经不再是常规的了,这好象与库的设计是否可伸缩无关。另一个例子,一般情况动态数组可以容下足够多的东东,我们无需关心它的存储问题,但如果元素数目非常非常多,比如100G的数目,这时就必须要自己考虑了[呵呵,这个例子有点夸张了]。),然而介于.NET设计时架构的良好设计的吸引,实在不愿意就此放弃,所以我想看看如何改造一下PropertyGrid来适应这种新情况新问题。
前面提到的性能问题主要是PropertyGrid在编辑一组对象时是在循环中依次修改各个对象,数目大的时候,这个循环需要很长的时间(这个没办法改变,时间不大可能变少),此时应用界面会失去响应,这在WIN32 GUI里的解决办法是在循环中插入消息处理,顺便加上一个进度条。我们在.net程序里当然也是这个解决思路,不过PropertyGrid在设计时并没有为这个留下接口(附带说一下,PropertyGrid的设计复杂的很,它的代码主要集中在System.Windows.Forms.PropertyGridInternal名空间下,不过很可惜,主要类都是internal的),经过阅读PropertyGrid的代码(用reflector看的,这个不太好玩,关系实在太复杂,只能了解大概),发现直接使用PropertyGrid来编辑一组大数目的对象不失去响应不大可能,所以考虑变通的办法。
虽然PropertyGrid的实现非常复杂,但是在.net框架库里只暴露了很少的几个类:GridItem,PropertyGrid,PropertiesTab等,PropertiesTab类主要用于实现自定义特性(或动态特性)类的编辑,没有逮到什么机会。GridItem代表了特性编辑器里的一行,也就是说它代表对一个特性的编辑(因为特性编辑器的行是可以树状展开的,GridItem类也是组织成树状的,GridItem可以有若干个子GridItem),它有三个属性与我们的目标相关,一个Value代表当前编辑器里的值,一个PropertyDescriptor用来实现它对应的特性的修改,一个Parent用来获取父项目。PropertyGrid的成员N多,不过对我们有用的是一个事件:PropertyValueChanged ,在特性值改变之后,它会触发这个事件,不过在触发这个事件时,与PropertyGrid关联的那组对象的特性已经被修改了。
我们没办法让PropertyGrid直接修改全部需要编辑的对象,现在有一个可能的办法是,提取这组对象中的一部分与PropertyGrid关联,并在PropertyValueChanged事件里对剩下的对象进行更改。(当然也可以只与PropertyGrid控件关联一个对象,不过这样有一个问题是PropertyGrid会显示那个对象的所有特性值,当你的应用展示给最终用户时,用户选择了许多对象,结果展示的是其中一个对象的值,很容易让用户误解所有这些对象的特性值相同,而让PropertyGrid关联多个对象时,它会检查这组对象的特性值是否相同,不同的则设为空[对引用对象]或默认值[对值对象],显然我们可以挑出一些有代表性的对象关联[也就是如果有不同的对象,就用这几个不同的]从而利用PropertyGrid的这一特性)。
现在来看看PropertyValueChanged事件给了我们哪些信息:OldValue 该项目更改前的值,ChangedItem 发生更改的项目(一个GridItem), 也就是我们可以知道新值、旧值、以及一个用于修改对象特性的PropertyDescriptor,看起来好象够了:
PropertyDescriptor.SetValue(obj,ChangedItem.Value);
试验了一下,惨败!!!原因:
一、前面提到过GridItem是组织成树状的,也就是说当一个对象的特性又是一个对象,而我们编辑的是这个特性对象的某个特性时,ChangedItem.PropertyDescriptor是针对那个特性对象的那个特性的,我们调用PropertyDescriptor.SetValue时,第一个参数必须是那个特性对象,然而我们现在并不知道它;
二、如果PropertyGrid关联的是一组对象,那么ChangedItem.PropertyDescriptor不是一个单一对象的特性编辑器,PropertyDescriptor.SetValue的第一个参数是对象数组,并且,数组元素个数必须是PropertyGrid关联的那组对象的个数(我们显然不能假设我们要编辑的对象总是可以分解为PropertyGrid关联的对象个数的整数倍的,特别的,对象总数还可能是质数)。
对问题一,我们可以利用GridItem.Parent来向上回溯到顶级对象,再利用各级GridItem.PropertyGrid.GetValue来得到特性对象的值,直到更改的那个GridItem的上一级,然后,在我们修改了发生更改的那个特性对象的特性后,我们还必须再次回溯一次,因为特性对象有可能是一个值对象的特性,而值对象特性的修改必须通过赋值才能影响到它所属的对象(引用便无此要求)。
对问题二,我们需要的是一个单一对象的特性编辑器,看一下源代码(当然还是用reflector)
internal class MergePropertyDescriptor : PropertyDescriptor
{ ...
public PropertyDescriptor this[int index] { get; }
...
} 这个MergePropertyDescriptor就是编辑一组对象特性的PropertyDescriptor,它有一个索引化的属性可以得到它用来修改每个对象的PropertyDescriptor,现在剩下最后一个问题,便是MergePropertyDescriptor类是internal的,我们不能直接访问,怎么办?呵呵,利用
反射机制:Type.InvokeMember ,这个是通杀的!
基本实现机制差不多就是这样了,剩下的便是在对每个对象修改的空隙里调用Application.DoEvents(),然后更新一下进度条了。
前面说的有点太轻松了,实际做起来还是比较费劲的,好在reflector给了不少参考源代码,下面是对上述一、二的实现:
public static class PropertyAccess
{ public static void UpdateNodeValue<SharedDataT, NodeDataT, SideDataT, CellDataT>(PropertyValueChangedEventArgs e, Mesh<SharedDataT, NodeDataT, SideDataT, CellDataT> mesh, int startIndex) where SharedDataT : ISharedData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() where NodeDataT : INodeData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() where SideDataT : ISideData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() where CellDataT : ICellData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() { LinkedList<AccessInfo> list = BuildPropertyHiberarchy(e); mesh.OnProgressTip("开始更新选中的结点数据......"); mesh.OnProgress(0); for (int i = startIndex; i < mesh.SelectedNodes.Count; i++) { mesh.OnProgress(100 * i / mesh.SelectedNodes.Count); object obj = mesh.SelectedNodes[i];
UpdatePropertyValue(e, obj, list); } mesh.OnProgress(0); mesh.OnProgressTip("结点数据更新完成."); } public static void UpdateSideValue<SharedDataT, NodeDataT, SideDataT, CellDataT>(PropertyValueChangedEventArgs e, Mesh<SharedDataT, NodeDataT, SideDataT, CellDataT> mesh, int startIndex) where SharedDataT : ISharedData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() where NodeDataT : INodeData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() where SideDataT : ISideData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() where CellDataT : ICellData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() { LinkedList<AccessInfo> list = BuildPropertyHiberarchy(e); mesh.OnProgressTip("开始更新选中的边数据......"); mesh.OnProgress(0); for (int i = startIndex; i < mesh.SelectedSides.Count; i++) { mesh.OnProgress(100 * i / mesh.SelectedSides.Count); object obj = mesh.SelectedSides[i];
UpdatePropertyValue(e, obj, list); } mesh.OnProgress(0); mesh.OnProgressTip("边数据更新完成."); } public static void UpdateCellValue<SharedDataT, NodeDataT, SideDataT, CellDataT>(PropertyValueChangedEventArgs e, Mesh<SharedDataT, NodeDataT, SideDataT, CellDataT> mesh, int startIndex) where SharedDataT : ISharedData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() where NodeDataT : INodeData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() where SideDataT : ISideData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() where CellDataT : ICellData<SharedDataT, NodeDataT, SideDataT, CellDataT>, new() { LinkedList<AccessInfo> list = BuildPropertyHiberarchy(e); mesh.OnProgressTip("开始更新选中的多边形区域数据"); mesh.OnProgress(0); for (int i = startIndex; i < mesh.SelectedCells.Count; i++) { mesh.OnProgress(100 * i / mesh.SelectedCells.Count); object obj = mesh.SelectedCells[i];
UpdatePropertyValue(e, obj, list); } mesh.OnProgress(0); mesh.OnProgressTip("多边形区域数据更新完成."); } private static LinkedList<AccessInfo> BuildPropertyHiberarchy(PropertyValueChangedEventArgs e) { GridItem gridItem = e.ChangedItem; LinkedList<AccessInfo> list = new LinkedList<AccessInfo>(); GridItem gi = gridItem.Parent; while (gi != null && (gi.GridItemType == GridItemType.Category || gi.GridItemType == GridItemType.Root)) gi = gi.Parent; while (gi != null) { AccessInfo ai = new AccessInfo(GetMergePropertyDescriptorItem0(gi.PropertyDescriptor)); if (list.Count <= 0) { list.AddLast(ai); } else { list.AddFirst(ai); } gi = gi.Parent; while (gi != null && (gi.GridItemType == GridItemType.Category || gi.GridItemType == GridItemType.Root)) gi = gi.Parent; } return list; } private static void UpdatePropertyValue(PropertyValueChangedEventArgs e, object target, LinkedList<AccessInfo> list) { GridItem gridItem = e.ChangedItem; object obj = target; foreach (AccessInfo ai in list) { obj = GetPropertyValueCore(obj, ai.PropertyDescriptor); ai.Value = obj; } SetPropertyValueCore(obj, CopyValue(gridItem.Value), GetMergePropertyDescriptorItem0(gridItem.PropertyDescriptor)); LinkedListNode<AccessInfo> infoNode = list.Last; if (infoNode != null) { infoNode.Value.Value = obj; } while (infoNode != null) { obj = infoNode.Value.Value; if (obj.GetType().IsValueType || obj.GetType().IsArray) { if (infoNode.Previous != null) { SetPropertyValueCore(infoNode.Previous.Value.Value, obj, infoNode.Value.PropertyDescriptor); } else { SetPropertyValueCore(target, obj, infoNode.Value.PropertyDescriptor); } } infoNode = infoNode.Previous; } } /// <summary> /// 利用Type的动态成员访问方法获取MergePropertyDescriptor.Item[0],MergePropertyDescriptor类是System.Windows.Forms.PropertyGridInternal下的internal类,不能直接访问。 /// </summary> /// <param name="pd"></param> /// <returns></returns> /// <remarks> /// 另一种访问不可访问成员的办法是用TypeDescriptor.CreateProperty创建一个PropertyDescriptor,然后用PropertyDescriptor.GetValue得到特性值,不过这种方法无法访问对象索 /// 引化属性Item,不适于这里的需求。 /// </remarks> private static PropertyDescriptor GetMergePropertyDescriptorItem0(PropertyDescriptor pd) { Type t = pd.GetType(); PropertyDescriptor _pd = null; PropertyInfo pi=t.GetProperty("Item"); if(pi!=null) { object o = t.InvokeMember("Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.IgnoreCase, null, pd, new object[] { 0 }); _pd = o as PropertyDescriptor; } if(_pd!=null) { return _pd; } return pd; } private static object GetPropertyValueCore(object target, PropertyDescriptor pd) { if (pd == null) { return null; } if (target is ICustomTypeDescriptor) { target = ((ICustomTypeDescriptor)target).GetPropertyOwner(pd); } object obj = pd.GetValue(target); return obj; } private static void SetPropertyValueCore(object target, object value, PropertyDescriptor pd) { if (pd != null) { object obj = target; if (obj is ICustomTypeDescriptor) { obj = ((ICustomTypeDescriptor)obj).GetPropertyOwner(pd); } if (obj != null) { pd.SetValue(obj, value); } } } private static object CopyValue(object value) { if (value != null) { Type type = value.GetType(); if (type.IsValueType) { return value; } object obj = null; ICloneable cloneable = value as ICloneable; if (cloneable != null) { obj = cloneable.Clone(); } if (obj == null) { TypeConverter converter = TypeDescriptor.GetConverter(value); if (converter.CanConvertTo(typeof(InstanceDescriptor))) { InstanceDescriptor descriptor = (InstanceDescriptor)converter.ConvertTo(null, CultureInfo.InvariantCulture, value, typeof(InstanceDescriptor)); if ((descriptor != null) && descriptor.IsComplete) { obj = descriptor.Invoke(); } } if (((obj == null) && converter.CanConvertTo(typeof(string))) && converter.CanConvertFrom(typeof(string))) { object _obj = converter.ConvertToInvariantString(value); obj = converter.ConvertFromInvariantString((string)_obj); } } if ((obj == null) && type.IsSerializable) { BinaryFormatter formatter = new BinaryFormatter(); MemoryStream stream = new MemoryStream(); formatter.Serialize(stream, value); stream.Position = 0; obj = formatter.Deserialize(stream); } if (obj != null) { return obj; } } return value; } class AccessInfo
{ public PropertyDescriptor PropertyDescriptor { get { return propertyDescriptor; } set { propertyDescriptor = value; } } public object Value { get { return val; } set { val = value; } } public AccessInfo() { } public AccessInfo(PropertyDescriptor pd) { propertyDescriptor = pd; } private PropertyDescriptor propertyDescriptor = null;
private object val = null; } } 最后说点别的,与前面提到的值类型与引用类型处理的差异相关,当我们实现一个自定义的UITypeEditor的时候,因为我们是脱离了PropertyGrid控件来操作对象属性,对对象属性的直接更改将不能引起PropertyGrid的GridItem树的逐级更新,PropertyGrid在调用UITypeEditor.EditValue后,它会比较传给EditValue的obj以及由EditValue返回的obj,仅在二者不同时才触发更新,因为EditValue接受的参数是object的,即使是一个值类型的值,也会被装箱成引用对象,因此EditValue中一定不能直接在传入参数上进行in-place修改,而必须要COPY一份对象实例,然而如何才能COPY任意的对象实例呢,事实上不可能,上面的代码中有一个CopyValue,这是来自.net 2.0的实现代码的,可以看到有三种方法让一个对象支持COPY(在上面讲的UITypeEditor.EditValue实现中,值类型也需要这些条件),即:
1、实现ICloneable;
2、与对象类型关联一个TypeConverter并且它可以将特性实例转为InstanceDescriptor,或者,可以将特性实例转为字符串并能由字符串转为特性实例;
3、对象类型指明了[Serializable]属性。 注:这里所说的UITypeEditor.EditValue的要求是考虑一个通用的UITypeEditor时的,如果是一个特定对象,因为我们十分了解那个特定的对象,所以完全可以构造一份实例并逐个特性的赋值,或者,那个对象本来有一个Copy构造。
呵呵,这篇的技巧可是费了我相当多的时间与脑力呢。 使用Lambda表达式为jscript脚本提供一个快速搜索功能这几天为Ollydbg的插件OllyHtml加了一个快速搜索反汇编代码的API,起因是因为在jscript脚本里逐语句的查找太慢了,然而搜索条件又不方便简单的利用函数参数表示,最后我想到了Lambda表达式,感觉在为脚本提供的库中使用Lambda表达式来提高某些操作的效率与灵活性还是比较有用的。(算了,今天写的太多了,手酸了,直接贴代码了)
LAMBDA表达式实现,主要用于构造逻辑表达式,表达式中的基本数据来自ollydbg插件SDK的结构t_disasm:
#pragma once
#include "stdafx.h" #include "resource.h" #include "Plugin.h" #include <vector> #include <map> class ConditionExpression
{ public: virtual bool operator () (t_disasm* p)=0; public: ConditionExpression() { TotalObjectsRef()++; refCount=1; } virtual ~ConditionExpression(void) {} public: virtual void AddRef(void) { refCount++; } virtual void Release(void) { refCount--; if(refCount==0) { TotalObjectsRef()--; /* CString temp; temp.Format("release one , left : %u",TotalObjectsRef()); ::MessageBox(NULL,temp,"TotalCount",MB_OK); */ delete this;
} } public: static inline int TotalObjects(void) { return TotalObjectsRef(); } private: static inline int& TotalObjectsRef(void) { static int totalCount=0; return totalCount; } private: int refCount; }; class AndCondition : public ConditionExpression
{ public: virtual bool operator () (t_disasm* p) { if(!exp1 || !exp2) return false; if((*exp1)(p) && (*exp2)(p)) return true; else return false; } virtual ~AndCondition() { if(exp1) exp1->Release(); if(exp2) exp2->Release(); } public: AndCondition(ConditionExpression* p1,ConditionExpression* p2) { exp1=p1; exp2=p2; if(p1) p1->AddRef(); if(p2) p2->AddRef(); } private: ConditionExpression* exp1; ConditionExpression* exp2; }; class OrCondition : public ConditionExpression
{ public: virtual bool operator () (t_disasm* p) { if(!exp1 || !exp2) return false; if((*exp1)(p) || (*exp2)(p)) return true; else return false; } virtual ~OrCondition() { if(exp1) exp1->Release(); if(exp2) exp2->Release(); } public: OrCondition(ConditionExpression* p1,ConditionExpression* p2) { exp1=p1; exp2=p2; if(p1) p1->AddRef(); if(p2) p2->AddRef(); } private: ConditionExpression* exp1; ConditionExpression* exp2; }; class NotCondition : public ConditionExpression
{ public: virtual bool operator () (t_disasm* p) { if(!exp) return false; if(!(*exp)(p)) return true; else return false; } virtual ~NotCondition() { if(exp) exp->Release(); } public: NotCondition(ConditionExpression* p) { exp=p; if(p) p->AddRef(); } private: ConditionExpression* exp; }; typedef int (*IntValuePtr)(t_disasm* p); typedef char* (*StrValuePtr)(t_disasm* p); //-------------------------------------------------------- /* *CompareType : 0 = EQ , 1 = GE , -1 = LE */ //-------------------------------------------------------- template<int CompareType> class CompareCondition : public ConditionExpression { public: virtual bool operator () (t_disasm* p) { if(!p || !fptr) return false; return Compare<CompareType>::Do(p,fptr,value); } virtual ~CompareCondition() {} public: CompareCondition(IntValuePtr _fptr,int val) { fptr=_fptr; value=val; } private: IntValuePtr fptr; int value; private: template<int CT=0> class Compare { public: static inline bool Do(t_disasm* p,IntValuePtr fptr,int val) { return (*fptr)(p)==val; } }; template<> class Compare<1> { public: static inline bool Do(t_disasm* p,IntValuePtr fptr,int val) { return (*fptr)(p)>=val; } }; template<> class Compare<-1> { public: static inline bool Do(t_disasm* p,IntValuePtr fptr,int val) { return (*fptr)(p)<=val; } }; }; class StringLikeCondition : public ConditionExpression { typedef std::vector<CString> Strings; public: virtual bool operator () (t_disasm* p) { if(!p || !fptr) return false; CString src=fptr(p); Strings::iterator it=strs.begin(); while(it!=strs.end()) { if(src.Find(*it)>=0) return true; it++; } return false; } virtual ~StringLikeCondition() {} public: StringLikeCondition(StrValuePtr _fptr,const char* s) { fptr=_fptr; char *buf=new char[::strlen(s)+1]; ::strcpy(buf,s); char* token = strtok(buf, "%"); while( token != NULL ) { strs.push_back(CString(token)); token = strtok(NULL, "%"); } } private: StrValuePtr fptr; Strings strs; }; //下面是为表达式提供基本值的若干个类,一个类对应t_disasm结构的一个成员,它们实际上被用作函数指针。
#define FieldValueClass(NAME) NAME##FieldValue
#define DefFieldValueClass(NAME,FN) \
class FieldValueClass(NAME)\ {\ public:\ static inline int Value(t_disasm* p)\ {\ return (int)p->##FN;\ }\ }; #define DefStrFieldValueClass(NAME,FN) \
class FieldValueClass(NAME)\ {\ public:\ static inline char* Value(t_disasm* p)\ {\ return (char*)p->##FN;\ }\ }; DefFieldValueClass(IP,ip)
DefFieldValueClass(CmdType,cmdtype) DefFieldValueClass(MemType,memtype) DefFieldValueClass(PrefixNum,nprefix) DefFieldValueClass(Indexed,indexed) DefFieldValueClass(JumpConst,jmpconst) DefFieldValueClass(JumpTable,jmptable) DefFieldValueClass(AddrConst,adrconst) DefFieldValueClass(ImmConst,immconst) DefFieldValueClass(ZeroImm,zeroconst) DefFieldValueClass(FixupOffset,fixupoffset) DefFieldValueClass(FixupSize,fixupsize) DefFieldValueClass(JumpAddr,jmpaddr) DefFieldValueClass(Error,error) DefFieldValueClass(Warnings,warnings) DefFieldValueClass(OPType1,op[0].optype) DefFieldValueClass(OPType2,op[1].optype) DefFieldValueClass(OPType3,op[2].optype) DefFieldValueClass(OPSize1,op[0].opsize) DefFieldValueClass(OPSize2,op[1].opsize) DefFieldValueClass(OPSize3,op[2].opsize) DefFieldValueClass(OPSeg1,op[0].seg) DefFieldValueClass(OPSeg2,op[1].seg) DefFieldValueClass(OPSeg3,op[2].seg) DefFieldValueClass(OPConst1,op[0].opconst) DefFieldValueClass(OPConst2,op[1].opconst) DefFieldValueClass(OPConst3,op[2].opconst) DefFieldValueClass(OPEaxScale1,op[0].regscale[REG_EAX]) DefFieldValueClass(OPEcxScale1,op[0].regscale[REG_ECX]) DefFieldValueClass(OPEdxScale1,op[0].regscale[REG_EDX]) DefFieldValueClass(OPEbxScale1,op[0].regscale[REG_EBX]) DefFieldValueClass(OPEspScale1,op[0].regscale[REG_ESP]) DefFieldValueClass(OPEbpScale1,op[0].regscale[REG_EBP]) DefFieldValueClass(OPEsiScale1,op[0].regscale[REG_ESI]) DefFieldValueClass(OPEdiScale1,op[0].regscale[REG_EDI]) DefFieldValueClass(OPEaxScale2,op[1].regscale[REG_EAX]) DefFieldValueClass(OPEcxScale2,op[1].regscale[REG_ECX]) DefFieldValueClass(OPEdxScale2,op[1].regscale[REG_EDX]) DefFieldValueClass(OPEbxScale2,op[1].regscale[REG_EBX]) DefFieldValueClass(OPEspScale2,op[1].regscale[REG_ESP]) DefFieldValueClass(OPEbpScale2,op[1].regscale[REG_EBP]) DefFieldValueClass(OPEsiScale2,op[1].regscale[REG_ESI]) DefFieldValueClass(OPEdiScale2,op[1].regscale[REG_EDI]) DefFieldValueClass(OPEaxScale3,op[2].regscale[REG_EAX]) DefFieldValueClass(OPEcxScale3,op[2].regscale[REG_ECX]) DefFieldValueClass(OPEdxScale3,op[2].regscale[REG_EDX]) DefFieldValueClass(OPEbxScale3,op[2].regscale[REG_EBX]) DefFieldValueClass(OPEspScale3,op[2].regscale[REG_ESP]) DefFieldValueClass(OPEbpScale3,op[2].regscale[REG_EBP]) DefFieldValueClass(OPEsiScale3,op[2].regscale[REG_ESI]) DefFieldValueClass(OPEdiScale3,op[2].regscale[REG_EDI]) DefStrFieldValueClass(Dump,dump)
DefStrFieldValueClass(Disasm,result) DefStrFieldValueClass(Comment,comment) DefStrFieldValueClass(OPComment1,opinfo[0]) DefStrFieldValueClass(OPComment2,opinfo[1]) DefStrFieldValueClass(OPComment3,opinfo[2]) 下面是对这些表达式的IDispatch封装,从而可用于jscript:
//逻辑条件类,支持AND,OR,NOT逻辑操作
class Condition : public IDispatch
{ public: virtual BOOL __stdcall get_IsCondition(void) { return TRUE; } virtual IDispatch* __stdcall And(IDispatch* v) { BOOL r=DispatchDriver::GetProperty(v,L"IsCondition",Type2Type<BOOL>()); if(r && pCondition) { Condition* pv=static_cast<Condition*>(v); AndCondition* p=new AndCondition(pCondition,pv->GetCondition());
Condition* ptr=Condition::CreateDispatch(); ptr->Init(p); p->Release(); return ptr; } return NULL; } virtual IDispatch* __stdcall Or(IDispatch* v) { BOOL r=DispatchDriver::GetProperty(v,L"IsCondition",Type2Type<BOOL>()); if(r && pCondition) { Condition* pv=static_cast<Condition*>(v); OrCondition* p=new OrCondition(pCondition,pv->GetCondition());
Condition* ptr=Condition::CreateDispatch(); ptr->Init(p); p->Release(); return ptr; } return NULL; } virtual IDispatch* __stdcall Not(void) { if(pCondition) { NotCondition* p=new NotCondition(pCondition); Condition* ptr=Condition::CreateDispatch(); ptr->Init(p); p->Release(); return ptr; } return NULL; } public: BEGIN_INTF(Condition) PROPERTYGET(IsCondition,true) METHOD(And) METHOD(Or) METHOD(Not) END_INTF() public: Condition() { pCondition=NULL; } ~Condition() { if(pCondition) pCondition->Release(); } void Init(ConditionExpression* p) { pCondition=p; if(p) p->AddRef(); } ConditionExpression* GetCondition(void) { return pCondition; } private: ConditionExpression* pCondition; }; #define DefFieldMember(NAME) \
virtual IDispatch* __stdcall get_##NAME(void)\ {\ IntegerFields* p=IntegerFields::CreateDispatch();\ p->Init(FieldValueClass(NAME)::Value);\ return p;\ } #define DefStrFieldMember(NAME) \
virtual IDispatch* __stdcall get_##NAME(void)\ {\ StringFields* p=StringFields::CreateDispatch();\ p->Init(FieldValueClass(NAME)::Value);\ return p;\ } //t_disasm结构中整型成员的封装,这个类提供基本值,以及由基本值参与比较运算EQ(等于),GE(大于等于),LE(小于等于)
//而得到的条件表达式,这样得到的条件表达式可以直接传给FindDisasm2方法用作条件,也可以经由前一个类的AND,OR,NOT操作
//合成新的条件表达式后再用于FindDisasm2方法。
class IntegerFields : public IDispatch
{ public: virtual IDispatch* __stdcall EQ(int v) { if(!fptr) return NULL; ConditionExpression* p=new CompareCondition<0>(fptr,v); Condition* ptr=Condition::CreateDispatch(); ptr->Init(p); p->Release(); return ptr; } virtual IDispatch* __stdcall GE(int v) { if(!fptr) return NULL; ConditionExpression* p=new CompareCondition<1>(fptr,v); Condition* ptr=Condition::CreateDispatch(); ptr->Init(p); p->Release(); return ptr; } virtual IDispatch* __stdcall LE(int v) { if(!fptr) return NULL; ConditionExpression* p=new CompareCondition<-1>(fptr,v); Condition* ptr=Condition::CreateDispatch(); ptr->Init(p); p->Release(); return ptr; } virtual IDispatch* __stdcall get_IP(void) { IntegerFields* p=IntegerFields::CreateDispatch(); p->Init(FieldValueClass(IP)::Value); return p; } DefFieldMember(CmdType)
DefFieldMember(MemType) DefFieldMember(PrefixNum) DefFieldMember(Indexed) DefFieldMember(JumpConst) DefFieldMember(JumpTable) DefFieldMember(AddrConst) DefFieldMember(ImmConst) DefFieldMember(ZeroImm) DefFieldMember(FixupOffset) DefFieldMember(FixupSize) DefFieldMember(JumpAddr) DefFieldMember(Error) DefFieldMember(Warnings) DefFieldMember(OPType1) DefFieldMember(OPType2) DefFieldMember(OPType3) DefFieldMember(OPSize1) DefFieldMember(OPSize2) DefFieldMember(OPSize3) DefFieldMember(OPSeg1) DefFieldMember(OPSeg2) DefFieldMember(OPSeg3) DefFieldMember(OPConst1) DefFieldMember(OPConst2) DefFieldMember(OPConst3) DefFieldMember(OPEaxScale1) DefFieldMember(OPEcxScale1) DefFieldMember(OPEdxScale1) DefFieldMember(OPEbxScale1) DefFieldMember(OPEspScale1) DefFieldMember(OPEbpScale1) DefFieldMember(OPEsiScale1) DefFieldMember(OPEdiScale1) DefFieldMember(OPEaxScale2) DefFieldMember(OPEcxScale2) DefFieldMember(OPEdxScale2) DefFieldMember(OPEbxScale2) DefFieldMember(OPEspScale2) DefFieldMember(OPEbpScale2) DefFieldMember(OPEsiScale2) DefFieldMember(OPEdiScale2) DefFieldMember(OPEaxScale3) DefFieldMember(OPEcxScale3) DefFieldMember(OPEdxScale3) DefFieldMember(OPEbxScale3) DefFieldMember(OPEspScale3) DefFieldMember(OPEbpScale3) DefFieldMember(OPEsiScale3) DefFieldMember(OPEdiScale3) public:
BEGIN_INTF(IntegerFields) METHOD(EQ) METHOD(GE) METHOD(LE) PROPERTYGET(IP,true) PROPERTYGET(CmdType,true) PROPERTYGET(MemType,true) PROPERTYGET(PrefixNum,true) PROPERTYGET(Indexed,true) PROPERTYGET(JumpConst,true) PROPERTYGET(JumpTable,true) PROPERTYGET(AddrConst,true) PROPERTYGET(ImmConst,true) PROPERTYGET(ZeroImm,true) PROPERTYGET(FixupOffset,true) PROPERTYGET(FixupSize,true) PROPERTYGET(JumpAddr,true) PROPERTYGET(Error,true) PROPERTYGET(Warnings,true) PROPERTYGET(OPType1,true) PROPERTYGET(OPType2,true) PROPERTYGET(OPType3,true) PROPERTYGET(OPSize1,true) PROPERTYGET(OPSize2,true) PROPERTYGET(OPSize3,true) PROPERTYGET(OPSeg1,true) PROPERTYGET(OPSeg2,true) PROPERTYGET(OPSeg3,true) PROPERTYGET(OPConst1,true) PROPERTYGET(OPConst2,true) PROPERTYGET(OPConst3,true) PROPERTYGET(OPEaxScale1,true) PROPERTYGET(OPEcxScale1,true) PROPERTYGET(OPEdxScale1,true) PROPERTYGET(OPEbxScale1,true) PROPERTYGET(OPEspScale1,true) PROPERTYGET(OPEbpScale1,true) PROPERTYGET(OPEsiScale1,true) PROPERTYGET(OPEdiScale1,true) PROPERTYGET(OPEaxScale2,true) PROPERTYGET(OPEcxScale2,true) PROPERTYGET(OPEdxScale2,true) PROPERTYGET(OPEbxScale2,true) PROPERTYGET(OPEspScale2,true) PROPERTYGET(OPEbpScale2,true) PROPERTYGET(OPEsiScale2,true) PROPERTYGET(OPEdiScale2,true) PROPERTYGET(OPEaxScale3,true) PROPERTYGET(OPEcxScale3,true) PROPERTYGET(OPEdxScale3,true) PROPERTYGET(OPEbxScale3,true) PROPERTYGET(OPEspScale3,true) PROPERTYGET(OPEbpScale3,true) PROPERTYGET(OPEsiScale3,true) PROPERTYGET(OPEdiScale3,true) END_INTF() public: IntegerFields() { fptr=NULL; } void Init(IntValuePtr _fptr) { fptr=_fptr; } private: IntValuePtr fptr; }; //t_disasm结构中字符串成员的封装,这个类提供基本值,以及由基本值参与字符串搜索Like
//而得到的条件表达式,这样得到的条件表达式可以直接传给FindDisasm2方法用作条件,也可以经由前一个类的AND,OR,NOT操作
//合成新的条件表达式后再用于FindDisasm2方法。
class StringFields : public IDispatch { public: virtual IDispatch* __stdcall Like(BSTR str) { if(!fptr) return NULL; StringLikeCondition* p=new StringLikeCondition(fptr,CString(str)); Condition* ptr=Condition::CreateDispatch(); ptr->Init(p); p->Release(); return ptr; } DefStrFieldMember(Dump)
DefStrFieldMember(Disasm) DefStrFieldMember(Comment) DefStrFieldMember(OPComment1) DefStrFieldMember(OPComment2) DefStrFieldMember(OPComment3) public:
BEGIN_INTF(StringFields) METHOD(Like) PROPERTYGET(Dump,true) PROPERTYGET(Disasm,true) PROPERTYGET(Comment,true) PROPERTYGET(OPComment1,true) PROPERTYGET(OPComment2,true) PROPERTYGET(OPComment3,true) END_INTF() public: StringFields() { fptr=NULL; } void Init(StrValuePtr _fptr) { fptr=_fptr; } private: StrValuePtr fptr; }; 反汇编代码搜索方法的实现:
virtual IDispatch* __stdcall FindDisasm2(DWORD addr,IDispatch* condition)
{ //判断参数是否条件表达式,因为没有定义新接口,所以不能用QueryInterface来确定。
BOOL r=DispatchDriver::GetProperty(condition,L"IsCondition",Type2Type<BOOL>()); if(!r) return NULL; Condition* pv=static_cast<Condition*>(condition); ConditionExpression* pCE=pv->GetCondition(); pCE->AddRef();//引用计数加1 DWORD endaddr;
int ct=0; int size = 0; t_memory* tmem = Findmemory(addr); char cmd[MAXCMDSIZE]={0}; do { addr = Disassembleforward(0, tmem->base, tmem->size, addr, 1, 1); endaddr = Disassembleforward(0, tmem->base, tmem->size, addr, 1, 1); size = Readcommand(addr, cmd); if(addr == tmem->base + tmem->size)
size = 0; if(size) { t_disasm dasm; ::memset(&dasm,0,sizeof(t_disasm)); DWORD dsize=0; unsigned char* bytes=Finddecode(addr,&dsize); ::Disasm((uchar*)cmd,size,addr,bytes,&dasm,DISASM_CODE,NULL); //应用LAMBDA表达式执行计算来得到判断
if((*pCE)(&dasm)) { pCE->Release();//引用计数减1 DisasmInfo* p=DisasmInfo::CreateDispatch();
p->Init(&dasm); return p; } } if(ct % EXTERN_EVENT_INTERVAL == 0)
{ dlg.DoMessageLoopOnce(); } ct++; } while(size != 0); pCE->Release();//引用计数减1 return NULL; } |
|||||
|
|