| 海风月影's profile海风月影BlogListsNetwork | Help |
测试一下
用WEBBROWSER控件实现在浏览器中点击超链接时不弹出新窗口,新页面永远在当前窗口显示用WEBBROWSER控件实现在浏览器中点击超链接时不弹出新窗口,新页面永远在当前窗口显示
程序很简单,用VB6实现,其他语言类似,xp pro sp2 下编译通过。
原理:建立2个WEBBROWSER控件,当WebBrowser1需要弹出新窗口的时候,将窗体设为我们的WebBrowser2,然后在WebBrowser2_BeforeNavigate2中cancel掉WebBrowser2,并将WebBrowser2 的URL传给WebBrowser1,用WebBrowser1打开网页。将WebBrowser2.visible设为flase,不可见。OK,这样就达到效果了
代码:
Private Sub WebBrowser1_NewWindow2(ppDisp As Object, Cancel As Boolean)
Set ppDisp = Form1.WebBrowser2.Object End Sub Private Sub WebBrowser2_BeforeNavigate2(ByVal pDisp As Object, URL As Variant, Flags As Variant, TargetFrameName As Variant, PostData As Variant, Headers As Variant, Cancel As Boolean) WebBrowser1.Navigate URL Cancel = True End Sub 偶变了。。。。。每天早上去公司,要坐公交车,这辆车早上老人比较多,以前偶都主动给老人让座,现在偶发现偶变了。。。基本不给别人让座了。。。。。。。。惭愧,惭愧。。。 被点名,无奈回答问题忽然被传说中的fly点名,好像要回答问题 1.你最喜欢的食物是什么?你会让你未来的爱人为你亲手做这道菜吗? 吃过的都很喜欢,会的
2.你曾经在逛街或者地铁站幻想过遇到一个帅哥/美女,他/她主动和你搭讪,你希望你们之间发生什么样的事情吗?
没想过。。
3.假如老天就给你只剩3天的时间可以活了,你打算怎么度过这宝贵的3天,期待有什么样的事情发生呢?
睡觉,或者跳崖,前者轻松,后者刺激
4.假如你有一个魔镜,你打算用它做什么?
看看世界,从上帝的角度
5.谈谈你未来10年的远大计划吧。
努力,成为成熟技术人士
6.心目中的白马王子/公主是谁?
米有
7.将来会不会要求自己的老公或为自己的老婆做家务?
不一定
8.对自己现在的生活满意吗?
还好
9.如果现在给你10万块钱,你会拿来做什么?
投资
10.打算什么时候把自己嫁出去或娶老婆?
娶!
11.男女之间有纯粹的友谊么?
基本没有
12.最近一次失眠是为了什么事情?
工作犯愁
13. 爱情重要么?
很重要
14.我在你眼里是怎样的人?
恩,好人
唉,文人啊
15.你眼中的世界是什么颜色的?
蓝色
16.如果让你自己重新给世界上色,你会选择什么颜色?
白色,世界很多地方太黑暗,要白一点
17.你对自己的性别满意吗?
显然满意
18 如果可以随意选择,你想要上什么大学,什么专业?
麻省理工,计算机系
19 如果世界上只能保留一种艺术,在音乐,绘画,雕塑,诗歌,etc.中,你会选择哪一种?
音乐
20 说说自己喜欢的季节和原因。
秋季,不热不冷
21 会不会因为文字和音乐莫名的喜欢上一座城市,是哪个和哪座。
不会,没这个境界
22 最近在看什么书呢?有什么心得吗? 没书看。。
23 本命年你会选择穿红色内裤吗?为什么?
为什么要穿 啊
24 最近生活中最大的变化是什么?
越来越累
25 老妈和老婆或是老公掉进河里,你会先救谁?
不知道,没这个可能性
26.你最想改变身上的哪一个缺点?
近视
27.如果给你一次机会,你想从自己多大那一年从头过起?
大一
28.你未来最想定居的城市?
天城,浮在空中
29.你觉得你现在的工作和你的理想距离大吗?
有点大
30.你觉得你的屁股够翘么?
不
31.你爱你的男/女朋友么?
当然
32.你有情人么?
不懂
33.你会选择每天睡觉3小时的工作么?
不会,太累了
34.你最想成为一个什么样的人?最欣赏什么样的人?
成功的人,十年磨一剑的成功人士
35.2007年你最想做的是什么事情?
找个好工作,并且好好工作
36.最喜欢歌曲的名字(就一首)
我BLOG上的
37 是不是经常感到迷茫?
是的,人活着太苦了
38. 将来想过什么样的生活?
快乐就行
39. 长这么大给你印象最深的一件事/一本书/一部电影是什么?
没想过,最近郁闷,想不出来
40.最喜欢金庸小说里的哪个人物?
乔峰!男子汉
41.什么时候最开心?
轻松的时候,没压力的时候
42. 生过得最严重的病?
水痘
43. 小时候最大的梦想是什么?
做个科学家,可惜没几会了
44.你最后悔的一件事情是什么?你想怎么弥补?
没有后悔的事情
自己想的题目
45.这样点名累嘛?
很累
开始点名:老卜,杨帆,逆风飞舞,苏苏,for beck Visual C++线程同步技术剖析 (转载)作者:中国电波传播研究所 郎锐■来自:yesky 摘要: 多线程同步技术是计算机软件开发的重要技术,本文对多线程的各种同步技术的原理和实现进行了初步探讨。 正文 临界区
管理事件内核对象
在创建线程前,首先创建一个可以自动复位的事件内核对象hEvent,而线程函数则通过WaitForSingleObject()等待函数无限等待 hEvent的置位,只有在事件置位时WaitForSingleObject()才会返回,被保护的代码将得以执行。对于以自动复位方式创建的事件对象,在其置位后一被WaitForSingleObject()等待到就会立即复位,也就是说在执行ThreadProc12()中的受保护代码时,事件对象已经是复位状态的,这时即使有ThreadProc13()对CPU的抢占,也会由于WaitForSingleObject()没有hEvent的置位而不能继续执行,也就没有可能破坏受保护的共享资源。在ThreadProc12()中的处理完成后可以通过SetEvent()对hEvent的置位而允许ThreadProc13()对共享资源g_cArray的处理。这里SetEvent()所起的作用可以看作是对某项特定任务完成的通知。 使用临界区只能同步同一进程中的线程,而使用事件内核对象则可以对进程外的线程进行同步,其前提是得到对此事件对象的访问权。可以通过OpenEvent()函数获取得到,其函数原型为:
如果事件对象已创建(在创建事件时需要指定事件名),函数将返回指定事件的句柄。对于那些在创建事件时没有指定事件名的事件内核对象,可以通过使用内核对象的继承性或是调用DuplicateHandle()函数来调用CreateEvent()以获得对指定事件对象的访问权。在获取到访问权后所进行的同步操作与在同一个进程中所进行的线程同步操作是一样的。 如果需要在一个线程中等待多个事件,则用 WaitForMultipleObjects()来等待。WaitForMultipleObjects()与WaitForSingleObject ()类似,同时监视位于句柄数组中的所有句柄。这些被监视对象的句柄享有平等的优先权,任何一个句柄都不可能比其他句柄具有更高的优先权。 WaitForMultipleObjects()的函数原型为:
参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。 dwMilliseconds在这里的作用与在WaitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回 WAIT_TIMEOUT。如果返回WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1中的某个值,则说明所有指定对象的状态均为已通知状态(当fWaitAll为TRUE时)或是用以减去WAIT_OBJECT_0而得到发生通知的对象的索引(当fWaitAll为FALSE 时)。如果返回值在WAIT_ABANDONED_0与WAIT_ABANDONED_0+nCount-1之间,则表示所有指定对象的状态均为已通知,且其中至少有一个对象是被丢弃的互斥对象(当fWaitAll为TRUE时),或是用以减去WAIT_OBJECT_0表示一个等待正常结束的互斥对象的索引(当fWaitAll为FALSE时)。下面给出的代码主要展示了对WaitForMultipleObjects()函数的使用。通过对两个事件内核对象的等待来控制线程任务的执行与中途退出:
MFC为事件相关处理也提供了一个CEvent类,共包含有除构造函数外的4个成员函数PulseEvent()、ResetEvent()、 SetEvent()和UnLock()。在功能上分别相当与Win32 API的PulseEvent()、ResetEvent()、SetEvent()和CloseHandle()等函数。而构造函数则履行了原 CreateEvent()函数创建事件对象的职责,其函数原型为:
按照此缺省设置将创建一个自动复位、初始状态为复位状态的没有名字的事件对象。封装后的CEvent类使用起来更加方便,图2即展示了CEvent类对A、B两线程的同步过程: ![]() 图2 CEvent类对线程的同步过程示意 B线程在执行到CEvent类成员函数Lock()时将会发生阻塞,而A线程此时则可以在没有B线程干扰的情况下对共享资源进行处理,并在处理完成后通过成员函数SetEvent()向B发出事件,使其被释放,得以对A先前已处理完毕的共享资源进行操作。可见,使用CEvent类对线程的同步方法与通过 API函数进行线程同步的处理方法是基本一致的。前面的API处理代码可用CEvent类将其改写为:
信号量(Semaphore)内核对象对线程的同步方式与前面几种方法不同,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在用 CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过 ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。 ![]() 图3 使用信号量对象控制资源 下面结合图例3来演示信号量对象对资源的控制。在图3中,以箭头和白色箭头表示共享资源所允许的最大资源计数和当前可用资源计数。初始如图(a)所示,最大资源计数和当前可用资源计数均为4,此后每增加一个对资源进行访问的线程(用黑色箭头表示)当前资源计数就会相应减1,图(b)即表示的在3个线程对共享资源进行访问时的状态。当进入线程数达到4个时,将如图(c)所示,此时已达到最大资源计数,而当前可用资源计数也已减到0,其他线程无法对共享资源进行访问。在当前占有资源的线程处理完毕而退出后,将会释放出空间,图(d)已有两个线程退出对资源的占有,当前可用计数为2,可以再允许2个线程进入到对资源的处理。可以看出,信号量是通过计数来对线程访问资源进行控制的,而实际上信号量确实也被称作Dijkstra计数器。 使用信号量内核对象进行线程同步主要会用到CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()、 WaitForSingleObject()和WaitForMultipleObjects()等函数。其中,CreateSemaphore()用来创建一个信号量内核对象,其函数原型为:
参数lMaximumCount是一个有符号32位值,定义了允许的最大资源计数,最大取值不能超过4294967295。lpName参数可以为创建的信号量定义一个名字,由于其创建的是一个内核对象,因此在其他进程中可以通过该名字而得到此信号量。OpenSemaphore()函数即可用来根据信号量名打开在其他进程中创建的信号量,函数原型如下:
在线程离开对共享资源的处理时,必须通过ReleaseSemaphore()来增加当前可用资源计数。否则将会出现当前正在处理共享资源的实际线程数并没有达到要限制的数值,而其他线程却因为当前可用资源计数为0而仍无法进入的情况。ReleaseSemaphore()的函数原型为:
该函数将lReleaseCount中的值添加给信号量的当前资源计数,一般将lReleaseCount设置为1,如果需要也可以设置其他的值。 WaitForSingleObject()和WaitForMultipleObjects()主要用在试图进入共享资源的线程函数入口处,主要用来判断信号量的当前可用资源计数是否允许本线程的进入。只有在当前可用资源计数值大于0时,被监视的信号量内核对象才会得到通知。 信号量的使用特点使其更适用于对Socket(套接字)程序中线程的同步。例如,网络上的HTTP服务器要对同一时间内访问同一页面的用户数加以限制,这时可以为没一个用户对服务器的页面请求设置一个线程,而页面则是待保护的共享资源,通过使用信号量对线程的同步作用可以确保在任一时刻无论有多少用户对某一页面进行访问,只有不大于设定的最大用户数目的线程能够进行访问,而其他的访问企图则被挂起,只有在有用户退出对此页面的访问后才有可能进入。下面给出的示例代码即展示了类似的处理过程:
![]() 图4 开始进入的两个线程 ![]() 图5 线程二退出后线程三才得以进入 上述代码在开启线程前首先创建了一个初始计数和最大资源计数均为2的信号量对象hSemaphore。即在同一时刻只允许2个线程进入由 hSemaphore保护的共享资源。随后开启的三个线程均试图访问此共享资源,在前两个线程试图访问共享资源时,由于hSemaphore的当前可用资源计数分别为2和1,此时的hSemaphore是可以得到通知的,也就是说位于线程入口处的WaitForSingleObject()将立即返回,而在前两个线程进入到保护区域后,hSemaphore的当前资源计数减少到0,hSemaphore将不再得到通知, WaitForSingleObject()将线程挂起。直到此前进入到保护区的线程退出后才能得以进入。图4和图5为上述代脉的运行结果。从实验结果可以看出,信号量始终保持了同一时刻不超过2个线程的进入。 在MFC中,通过CSemaphore类对信号量作了表述。该类只具有一个构造函数,可以构造一个信号量对象,并对初始资源计数、最大资源计数、对象名和安全属性等进行初始化,其原型如下:
在构造了CSemaphore类对象后,任何一个访问受保护共享资源的线程都必须通过CSemaphore从父类CSyncObject类继承得到的 Lock()和UnLock()成员函数来访问或释放CSemaphore对象。与前面介绍的几种通过MFC类保持线程同步的方法类似,通过 CSemaphore类也可以将前面的线程同步代码进行改写,这两种使用信号量的线程同步方法无论是在实现原理上还是从实现结果上都是完全一致的。下面给出经MFC改写后的信号量线程同步代码:
互斥(Mutex)是一种用途非常广泛的内核对象。能够保证多个线程对同一共享资源的互斥访问。同临界区有些类似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。与其他几种内核对象不同,互斥对象在操作系统中拥有特殊代码,并由操作系统来管理,操作系统甚至还允许其进行一些其他内核对象所不能进行的非常规操作。为便于理解,可参照图6给出的互斥内核对象的工作模型: ![]() 图6 使用互斥内核对象对共享资源的保护 图(a)中的箭头为要访问资源(矩形框)的线程,但只有第二个线程拥有互斥对象(黑点)并得以进入到共享资源,而其他线程则会被排斥在外(如图(b)所示)。当此线程处理完共享资源并准备离开此区域时将把其所拥有的互斥对象交出(如图(c)所示),其他任何一个试图访问此资源的线程都有机会得到此互斥对象。 以互斥内核对象来保持线程同步可能用到的函数主要有CreateMutex()、OpenMutex()、 ReleaseMutex()、WaitForSingleObject()和WaitForMultipleObjects()等。在使用互斥对象前,首先要通过CreateMutex()或OpenMutex()创建或打开一个互斥对象。CreateMutex()函数原型为:
参数bInitialOwner主要用来控制互斥对象的初始状态。一般多将其设置为FALSE,以表明互斥对象在创建时并没有为任何线程所占有。如果在创建互斥对象时指定了对象名,那么可以在本进程其他地方或是在其他进程通过OpenMutex()函数得到此互斥对象的句柄。OpenMutex()函数原型为:
当目前对资源具有访问权的线程不再需要访问此资源而要离开时,必须通过ReleaseMutex()函数来释放其拥有的互斥对象,其函数原型为:
其唯一的参数hMutex为待释放的互斥对象句柄。至于WaitForSingleObject()和WaitForMultipleObjects()等待函数在互斥对象保持线程同步中所起的作用与在其他内核对象中的作用是基本一致的,也是等待互斥内核对象的通知。但是这里需要特别指出的是:在互斥对象通知引起调用等待函数返回时,等待函数的返回值不再是通常的WAIT_OBJECT_0(对于WaitForSingleObject()函数)或是在 WAIT_OBJECT_0到WAIT_OBJECT_0+nCount-1之间的一个值(对于WaitForMultipleObjects()函数),而是将返回一个WAIT_ABANDONED_0(对于WaitForSingleObject()函数)或是在WAIT_ABANDONED_0 到WAIT_ABANDONED_0+nCount-1之间的一个值(对于WaitForMultipleObjects()函数)。以此来表明线程正在等待的互斥对象由另外一个线程所拥有,而此线程却在使用完共享资源前就已经终止。除此之外,使用互斥对象的方法在等待线程的可调度性上同使用其他几种内核对象的方法也有所不同,其他内核对象在没有得到通知时,受调用等待函数的作用,线程将会挂起,同时失去可调度性,而使用互斥的方法却可以在等待的同时仍具有可调度性,这也正是互斥对象所能完成的非常规操作之一。 在编写程序时,互斥对象多用在对那些为多个线程所访问的内存块的保护上,可以确保任何线程在处理此内存块时都对其拥有可靠的独占访问权。下面给出的示例代码即通过互斥内核对象hMutex对共享内存快g_cArray[]进行线程的独占访问保护。下面给出实现代码清单:
互斥对象在MFC中通过CMutex类进行表述。使用CMutex类的方法非常简单,在构造CMutex类对象的同时可以指明待查询的互斥对象的名字,在构造函数返回后即可访问此互斥变量。CMutex类也是只含有构造函数这唯一的成员函数,当完成对互斥对象保护资源的访问后,可通过调用从父类 CSyncObject继承的UnLock()函数完成对互斥对象的释放。CMutex类构造函数原型为:
该类的适用范围和实现原理与API方式创建的互斥内核对象是完全类似的,但要简洁的多,下面给出就是对前面的示例代码经CMutex类改写后的程序实现清单:
小结 线程的使用使程序处理更够更加灵活,而这种灵活同样也会带来各种不确定性的可能。尤其是在多个线程对同一公共变量进行访问时。虽然未使用线程同步的程序代码在逻辑上或许没有什么问题,但为了确保程序的正确、可靠运行,必须在适当的场合采取线程同步措施。 二进制格雷码与自然二进制码的互换二进制格雷码与自然二进制码的互换2006年03月19日, by mathon
二进制格雷码与自然二进制码的互换 示例工程下载
二、二进制格雷码与自然二进制码的互换 1、自然二进制码转换成二进制格雷码 自然二进制码转换成二进制格雷码,其法则是保留自然二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,而格雷码其余各位与次高位的求法相类似。 ![]() 2、二进制格雷码转换成自然二进制码 二进制格雷码转换成自然二进制码,其法则是保留格雷码的最高位作为自然二进制码的最高位,而次高位自然二进制码为高位自然二进制码与次高位格雷码相异或,而自然二进制码的其余各位与次高位自然二进制码的求法相类似。 ![]() 三、二进制格雷码与自然二进制码互换的实现方法 1、自然二进制码转换成二进制格雷码 A)、软件实现法(参见示例工程中的 Binary to Gray) 根据自然二进制转换成格雷码的法则,可以得到以下的代码: static unsigned int DecimaltoGray(unsigned int x)
{
return x^(x>>1);
}
//以上代码实现了unsigned int型数据到格雷码的转换,最高可转换32位自然二进制码,超出32位将溢出。
static int DecimaltoGray( int x)
{
return x^(x>>1);
}
//以上代码实现了 int型数据到格雷码的转换,最高可转换31位自然二进制码,超出31位将溢出。
上述代码即可用于VC控制程序中,也可以用于单片机控制程序中。在单片机程序设计时,若采用汇编语言编程,可以按相同的原理设计程序;若采用C语言编程,则可以直接利用上述代码,但建议用unsigned int函数。
B)、硬件实现法 根据自然二进制转换成格雷码的法则,可以得到以下电路图: 上图所示电路图即可用异或集成电路74ls136实现,也可以利用可编程器件PLD等编程实现。 2、二进制格雷码转换成自然二进制码 A)、软件实现法(参见示例工程中的 Gray to Binary ) 根据二进制格雷码转换成自然二进制码的法则,可以得到以下的三种代码方式:
//以上代码实现了unsigned int型数据到自然二进制码的转换,最高可转换32位格雷码,超出32位将溢出。将数据类型改为int型即可实现31位格雷码转换。
上述代码即可用于VC控制程序中,也可以用于单片机控制程序中。在单片机程序设计时,若采用汇编语言编程,可以按相同的原理设计程序;若采用C语言编程,则可以直接利用上述代码,但建议用unsigned int函数。
B)、硬件实现法
根据二进制格雷码转换成自然二进制码的法则,可以得到以下电路图:
上图所示电路图即可用异或集成电路74ls136实现,也可以利用可编程器件PLD等编程实现。 最近不能写BLOG?怎么好像到今天才能上嘛。。。最近比较忙,唉~~ 要实习了考试前天终于考完了,晚上全班聚了聚,夜里去飙歌,真热闹。前所未有,聚餐来了28个,飙歌来了27个!!
马上要实习了,还不知道是个什么样子,最近忙死了,还要回家一趟。据说宿舍东西要搬完。。。。汗~~什么学校啊
招聘会今天本部招聘会,投了3家公司,新宇,神游,苏州软件评测公司。实在太挤了~~我这个专业去投简历让别人感到很奇怪,别人尴尬,我也尴尬
不知结果如何~~心里没底 K掉卡巴斯基(kis6)主动防御功能啥?我可不懂,记录一下而已,下载地址:
XP + SP2 应该不会蓝屏。运行一下即可,没反应就是正常,不过卡巴斯基的主动防御已悄悄地离开了(想要地话重启一下)
BTW:咔吧司机也被K掉了,还有几个几个AV能真正防御呢?所以,我们的做法是:不用杀毒软件
当代“生物学的爱因斯坦”James D.Watson今天访问我们学院今天下午15:30分,James Watson先生来我院访问,会议室挤满了人,看到了几眼,领略了科学家的风采。
资料:
1953年James Watson与Francis Crick发现生命遗传机制DNA的双螺旋结构,开启了当代分子生物学、遗传学及生物技术的大门,荣获1962年诺贝尔奖。 1989年成为国家卫生院人类基因组研究全国中心的主任,成功地领导完成了全球参与的人类基因组序列分析。1953年James Watson与Francis Crick发现生命遗传机制DNA的双螺旋结构,开启了当代分子生物学、遗传学及生物技术的大门,荣获1962年诺贝尔奖。 1989年成为国家卫生院人类基因组研究全国中心的主任,成功地领导完成了全球参与的人类基因组序列分析。
笔记本的光驱神奇地好了~~本来XP连光驱都找不到。。。。
刚刚回宿舍开机,发现新硬件,找到光驱,找了一张盘试试,居然好了
哇,谢天谢地~~~ 系统又坏了~~前几天的系统又坏了,汗~~~~被一个软件搞的。。
SourceFormatX,里面大量破坏系统的代码,网上居然没有破解版,于是花了一点时间破解了,发布出来
感谢tankaiha提供的空间 VBox 4.3 脱壳 - MINITAB(Registered Trademark) Release 14.20【文章标题】: VBox 4.3 脱壳 - MINITAB(Registered Trademark) Release 14.20
【文章作者】: 海风月影 【作者主页】: http://depteicn.spaces.live.com 【软件名称】: MINITAB(Registered Trademark) Release 14.20 【软件大小】: 78M 【下载地址】: 自己搜索下载 【加壳方式】: VBOX 4.3 【使用工具】: OllyDBG ,LordPE ,ImportREC ,PEID 【操作平台】: WinXP SP2 【软件介绍】: MINITAB统计软件是为六西格玛和其它质量改善项目采用的理想套装软件。从统计过程控制(Statistical Process Control)到试验设计(Design of Experiments),MINITAB为你提供实现质量项目各阶段目标的方法;同时“StatGuide”和“ReportPad”等工具将帮助你理解和联系你的操作结果。与普通的软件相比,MINITAB统计软件更为精确、可靠、易于使用。 除了具有比老版本更多的统计功能外,MINITAB 14 还有许多更令人兴奋的新特点,如: - 一个强大的新图形引擎将基于你的数据传递出精彩的、有洞察力的图形结果
- 更容易创建、编辑、升级图形 - 可自定义菜单、工具栏的功能将使你能够便捷地访问常用的工具 【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
-------------------------------------------------------------------------------- 【详细过程】 由于专业需要用到统计软件,老师推荐了 MINITAB 14 ,当然比不过强大的 MATLAB ,不过比起MATLAB 6XXM的庞大身躯来说,minitab 14.10 只有15M,非常适合传播。 老师给的是minitab 14.10 30天试用版,30天后就不能用了~~。老师还说暂时找不到破解版,有谁能通过某些途径获得破解版的,本课程期末可以加分=。=,我汗~~ 查壳,VBOX 4.3 - 4.6.x -> WeijunLi [Overlay]
VBOX的anti不强,只是修复IAT稍有点麻烦。我最怕anti,所以拣个anti不强的软柿子来捏捏 一,找OEP
OD设置忽略所有异常,用HideOD插件选中 Auto Run HideOD,如果你没有HideOD,用IsDebug 1.4插件去掉Ollydbg的调试器标志。(什么?你也没有IsDebug 1.4插件?那么你的OD八成是某大侠秘传的,可以直接过IsDebug检测
载入Mtb14.exe。
如果先停在入口处,则F9运行它,直到出现试用的窗口,OD设置去掉忽略INT 3异常的勾,点 试用(Try)
如果先出现试用窗口,则点 试用(Try) ,让它停在入口处,OD设置去掉忽略INT 3异常的勾,F9运行 程序暂停在最后一次 INT 3 异常处,MINITAB 14.10只有1次异常,MINITAB 14.20有2次异常,不管有几次,停在最后一次
Ctrl + F9 返回
搜索二进制代码
C3 8B ?? ?? 6A 01
搜索到这里:
07007F08 . C3 retn ; <===搜索到这里
07007F09 > 8B75 08 mov esi, dword ptr ss:[ebp+8] ; <===这行下硬件执行断点 07007F0C . 6A 01 push 1 下硬件执行断点: he 7007f09 , F9运行,停在 7007F09处,删除硬件断点 hd 7007f09,下面就可以单步执行或者下F2断点了,程序自校验已经结束。
[ESI+14]里就是OEP,向下看,不远处:
07007F08 . C3 retn ; <===搜索到这里
07007F09 > 8B75 08 mov esi, dword ptr ss:[ebp+8] ; <===这行下硬件执行断点 07007F0C . 6A 01 push 1 ; 向下看 07007F0E . 5F pop edi 07007F0F . C745 FC 16000>mov dword ptr ss:[ebp-4], 16 07007F16 . 817E 0C F8000>cmp dword ptr ds:[esi+C], 0F8 07007F1D . 75 1D jnz short vboxt430.07007F3C 07007F1F . F645 A8 40 test byte ptr ss:[ebp-58], 40 07007F23 . 74 17 je short vboxt430.07007F3C 07007F25 . 8B46 14 mov eax, dword ptr ds:[esi+14] ; <===[esi+14]里就是OEP★ 07007F28 . 3B05 D0210407 cmp eax, dword ptr ds:[70421D0] ; kernel32.ExitProcess 07007F2E . 75 07 jnz short vboxt430.07007F37 07007F30 . E8 04C4FFFF call vboxt430.07004339 07007F35 . EB 05 jmp short vboxt430.07007F3C 07007F37 > E8 CEC1FFFF call vboxt430.0700410A 07007F3C > 8366 1C 00 and dword ptr ds:[esi+1C], 0 07007F40 . 817E 0C C7000>cmp dword ptr ds:[esi+C], 0C7 07007F47 . 8D5E 1C lea ebx, dword ptr ds:[esi+1C] 07007F4A . 75 61 jnz short vboxt430.07007FAD 07007F4C . 8B46 14 mov eax, dword ptr ds:[esi+14] ; <===[esi+14]里就是OEP★ 07007F4F . 33C9 xor ecx, ecx 07007F51 . 3B05 D0210407 cmp eax, dword ptr ds:[70421D0] ; kernel32.ExitProcess 07007F57 . 0F94C1 sete cl 07007F5A . 85C9 test ecx, ecx 07007F5C . 894D 08 mov dword ptr ss:[ebp+8], ecx 07007F5F . 75 10 jnz short vboxt430.07007F71 07007F61 . FF75 14 push dword ptr ss:[ebp+14] 07007F64 . FF75 10 push dword ptr ss:[ebp+10] 07007F67 . FF75 0C push dword ptr ss:[ebp+C] 07007F6A . FFD0 call eax 07007F6C . 8946 18 mov dword ptr ds:[esi+18], eax 07007F6F . EB 04 jmp short vboxt430.07007F75 07007F71 > 8366 18 00 and dword ptr ds:[esi+18], 0 07007F75 > 837D 10 00 cmp dword ptr ss:[ebp+10], 0 07007F79 . 75 03 jnz short vboxt430.07007F7E 07007F7B . 57 push edi 07007F7C . EB 1C jmp short vboxt430.07007F9A 07007F7E > 397D 10 cmp dword ptr ss:[ebp+10], edi 07007F81 . 75 2A jnz short vboxt430.07007FAD 07007F83 . 837D 08 00 cmp dword ptr ss:[ebp+8], 0 07007F87 . 74 24 je short vboxt430.07007FAD 07007F89 . E8 61D1FFFF call vboxt430.070050EF 07007F8E . 83F8 02 cmp eax, 2 07007F91 . 74 05 je short vboxt430.07007F98 07007F93 . 83F8 03 cmp eax, 3 07007F96 . 75 15 jnz short vboxt430.07007FAD 07007F98 > 6A 00 push 0 07007F9A > 53 push ebx 07007F9B . 8D85 74FEFFFF lea eax, dword ptr ss:[ebp-18C] 07007FA1 . FF75 0C push dword ptr ss:[ebp+C] 07007FA4 . 50 push eax 07007FA5 . E8 98050000 call vboxt430.07008542 07007FAA . 83C4 10 add esp, 10 07007FAD > 83BD BCFEFFFF>cmp dword ptr ss:[ebp-144], 0 07007FB4 . 74 0C je short vboxt430.07007FC2 07007FB6 . FFB5 BCFEFFFF push dword ptr ss:[ebp-144] 07007FBC . E8 5892FFFF call vboxt430.07001219 07007FC1 . 59 pop ecx 07007FC2 > 8B76 14 mov esi, dword ptr ds:[esi+14] ; <===[esi+14]里就是OEP★ 这3行都是取OEP,3行都下断点,F9运行,停下来,看一下OEP是多少 ds:[05400014]=009DB8B4 (Mtb14.009DB8B4)
esi=05400000, (ASCII "XlXl") 跳转来自 07007FB4 OEP=[05400014]=009DB8B4
清除前面所有断点,下硬件执行断点: he 009DB8B4,F9运行,已经稳稳地停在了OEP处,程序是VC++写的
009DB8B4 /. 55 push ebp ; <===OEP 009DB8B5 |. 8BEC mov ebp, esp 009DB8B7 |. 6A FF push -1 009DB8B9 |. 68 00D5A700 push Mtb14.00A7D500 009DB8BE |. 68 12BF9D00 push Mtb14.009DBF12 ; jmp 到 MSLURT._except_handler3; SE 处理程序安装 009DB8C3 |. 64:A1 0000000>mov eax, dword ptr fs:[0] 009DB8C9 |. 50 push eax 009DB8CA |. 64:8925 00000>mov dword ptr fs:[0], esp 009DB8D1 |. 83EC 68 sub esp, 68 009DB8D4 |. 53 push ebx 009DB8D5 |. 56 push esi 009DB8D6 |. 57 push edi 009DB8D7 |. 8965 E8 mov dword ptr ss:[ebp-18], esp 009DB8DA |. 33DB xor ebx, ebx 009DB8DC |. 895D FC mov dword ptr ss:[ebp-4], ebx 009DB8DF |. 6A 02 push 2 009DB8E1 |. 5F pop edi 009DB8E2 |. 57 push edi 009DB8E3 |. FF15 C07FA300 call dword ptr ds:[A37FC0] ; MSLURT.__set_app_type ◆小插曲 对于VC++写的程序可以这样找OEP
下断点BP GetVersion,然后F9运行,不过这个程序入口处没用到GetVersion,不过仔细看看可以发现VC++写的程序入口处也会出现这个函数MSLURT.__set_app_type,也可以用BP MSLURT.__set_app_type方式来找OEP,不过有个缺陷,就是MSLURT.dll加壳后不一定是静态导入的,如
果不是静态导入是不能这样下断点的,用LORDPE查看加壳的文件,发现有MSLURT.dll文件,呵呵,那么找OEP就方便多了。
●简单找OEP的方法
入口处下断点:bp mslurt.__set_app_type,把断点改在RETN处,忽略所有异常 , F9运行
780067D4 > 8B4424 04 mov eax, dword ptr ss:[esp+4] ; mslurt.__set_app_type
780067D8 A3 088F0378 mov dword ptr ds:[78038F08], eax 780067DD C3 retn ; <===把断点改在这里 很稳地停在了 retn 处,F8返回
009DB8B4 55 push ebp ; <====OEP
009DB8B5 8BEC mov ebp, esp 009DB8B7 6A FF push -1 009DB8B9 68 00D5A700 push Mtb14.00A7D500 009DB8BE 68 12BF9D00 push Mtb14.009DBF12 ; jmp 到 MSLURT._except_handler3 009DB8C3 64:A1 00000000 mov eax, dword ptr fs:[0] 009DB8C9 50 push eax 009DB8CA 64:8925 0000000>mov dword ptr fs:[0], esp 009DB8D1 83EC 68 sub esp, 68 009DB8D4 53 push ebx 009DB8D5 56 push esi 009DB8D6 57 push edi 009DB8D7 8965 E8 mov dword ptr ss:[ebp-18], esp 009DB8DA 33DB xor ebx, ebx 009DB8DC 895D FC mov dword ptr ss:[ebp-4], ebx 009DB8DF 6A 02 push 2 009DB8E1 5F pop edi 009DB8E2 57 push edi 009DB8E3 FF15 C07FA300 call dword ptr ds:[A37FC0] ; MSLURT.__set_app_type 009DB8E9 59 pop ecx ; 返回到这里,向上看 当然这里是可以DUMP的
用LordPE ,Correct ImageSize后dump full,保存为dump.exe
二,修复IAT
运行ImportREC,选择Mtb14.exe,OEP填 5DB8B4 (9DB8B4-400000),搜索 IAT,GET Imports。
开始地址:00636FFC 大小: 00006D90 可以看到有很多无效的指针,随便找一个看看。点右键,disassemble/hex view :
056B0000 call 070103BA // ~= vboxt430.dll/0001
056B0005 outs dx,byte ptr es:[edi] 056B0006 ja short 056AFF8F 056B0008 add [eax],al 056B000A add [eax],al 跑到VBOX空间里去了,显然这个指针不能CUT掉,再多看几个,发现都是call 070103BA,看来070103BA这个函数是解码API的函数。在OD中跳过去看看:
070103BA /. 55 push ebp ; <===VBOX 解码 API的过程
070103BB |. 8BEC mov ebp, esp 070103BD |. 83EC 10 sub esp, 10 070103C0 |. 53 push ebx 070103C1 |. 8945 FC mov dword ptr ss:[ebp-4], eax ; 保存寄存器的值 070103C4 |. 895D F8 mov dword ptr ss:[ebp-8], ebx 070103C7 |. 894D F4 mov dword ptr ss:[ebp-C], ecx 070103CA |. 8955 F0 mov dword ptr ss:[ebp-10], edx 070103CD |. 8D45 F0 lea eax, dword ptr ss:[ebp-10] 070103D0 |. 50 push eax 070103D1 |. 8D45 F4 lea eax, dword ptr ss:[ebp-C] 070103D4 |. 50 push eax 070103D5 |. 8D45 F8 lea eax, dword ptr ss:[ebp-8] 070103D8 |. 50 push eax 070103D9 |. 8D45 FC lea eax, dword ptr ss:[ebp-4] 070103DC |. 50 push eax 070103DD |. E8 12000000 call vboxt430.070103F4 ; 解码过程 070103E2 |. 83C4 10 add esp, 10 070103E5 |. 8B45 FC mov eax, dword ptr ss:[ebp-4] ; 这4行恢复寄存器 070103E8 |. 8B5D F8 mov ebx, dword ptr ss:[ebp-8] 070103EB |. 8B4D F4 mov ecx, dword ptr ss:[ebp-C] 070103EE |. 8B55 F0 mov edx, dword ptr ss:[ebp-10] 070103F1 |. 5B pop ebx ; 取出EBX 070103F2 |. C9 leave ; 平衡堆栈 070103F3 \. C3 retn ; 这里跳到正确的API函数 vbox根据什么解码呢?是根据地址解码的,比如056B0000 call 070103BA ,是根据056B0000解码的。这个我就不解释了,shoooo大侠里关于ASProtect 里修复 Advanced Import protection 已经说得很清楚了(不解释其实是我解释不清楚,哈哈)。这里的vbox很温柔,解码后直接跳到正确的API函数,因此,我们就没必要跟进去看了,直接写段代码来修复一下就可以了。 修复前我们需要得到一些数据,首先要知道IAT起始和结束位置,这个ImportREC已经帮我们做到了(什么?你还是找不到?填入OEP,点IAT
Auto Search),然后要知道加密的IAT区域的上下限(不能把正确的函数也拿去修复吧),这个其实也很简单,在ImportREC看看那些无效的指针的值,最小的,和最大的就可以确定了(啥?太多了看不过来?看不过来就多修复几次,每修复一次肯定能少一些无效指针),当然还可以这样来找,按Alt + M,查看内存,找到这里:见图1,这里是下限
然后向下找,一直找到这里:见图2,这里是上限 这段区域内都是vbox申请的区域,所以肯定包含了加密IAT的区域。 随便找块空地,写入下面代码
E8 14000000 call 05470019
00 70 A3 00 dd 00A37000 ; IAT 起始位置 00 84 DD A3 dd 00A3DD84 ; IAT 结束位置 00 00 F4 01 dd 01F40000 ; 加密的IAT区域的下限 00 00 93 06 dd 06950000 ; 加密的IAT区域的上限 90 nop 90 nop 90 nop 90 nop 5A pop edx 81EA 05004705 sub edx, 5470005 ; 自定位计算 8B82 05004705 mov eax, dword ptr ds:[edx+5470005] ; 取当前IAT指针 8B18 mov ebx, dword ptr ds:[eax] ; 取当前IAT 3B9A 0D004705 cmp ebx, dword ptr ds:[edx+547000D] ; 是否小于指定区域 7C 12 jl short 05470042 3B9A 11004705 cmp ebx, dword ptr ds:[edx+5470011] ; 是否大于指定区域 7F 0A jg short 05470042 FFD3 call ebx ; 在区域内,调用它修复 8B82 05004705 mov eax, dword ptr ds:[edx+5470005] ; 取当前IAT指针 8918 mov dword ptr ds:[eax], ebx ; 保存正确的IAT 8B82 05004705 mov eax, dword ptr ds:[edx+5470005] 83C0 04 add eax, 4 8982 05004705 mov dword ptr ds:[edx+5470005], eax 3B05 09004705 cmp eax, dword ptr ds:[5470009] ; 还没结束,循环 7F 02 jg short 0547005B ; 结束了,跳走 ^ EB CB jmp short 05470026 - EB FE jmp short 0547005B ; 原地打转,防止断点断不下来 二进制代码: E8 14 00 00 00 00 70 A3 00 84 DD A3 00 00 00 F4 01 00 00 95 06 90 90 90 90 5A 81 EA 05 00 47 05 8B 82 05 00 47 05 8B 18 3B 9A 0D 00 47 05 7C 12 3B 9A 11 00 47 05 7F 0A FF D3 8B 82 05 00 47 05 89 18 8B 82 05 00 47 05 83 C0 04 89 82 05 00 47 05 3B 82 09 00 47 05 7F 02 EB CB EB FE 进入解码 API的过程修改一下返回代码
070103BA /. 55 push ebp ; <===VBOX 解码API的过程
070103BB |. 8BEC mov ebp, esp 070103BD |. 83EC 10 sub esp, 10 070103C0 |. 53 push ebx 070103C1 |. 8945 FC mov dword ptr ss:[ebp-4], eax 070103C4 |. 895D F8 mov dword ptr ss:[ebp-8], ebx 070103C7 |. 894D F4 mov dword ptr ss:[ebp-C], ecx 070103CA |. 8955 F0 mov dword ptr ss:[ebp-10], edx 070103CD |. 8D45 F0 lea eax, dword ptr ss:[ebp-10] 070103D0 |. 50 push eax 070103D1 |. 8D45 F4 lea eax, dword ptr ss:[ebp-C] 070103D4 |. 50 push eax 070103D5 |. 8D45 F8 lea eax, dword ptr ss:[ebp-8] 070103D8 |. 50 push eax 070103D9 |. 8D45 FC lea eax, dword ptr ss:[ebp-4] 070103DC |. 50 push eax 070103DD |. E8 12000000 call vboxt430.070103F4 070103E2 |. 83C4 10 add esp, 10 070103E5 8B55 F0 mov edx, dword ptr ss:[ebp-10] ; 取回我们的EDX 070103E8 C9 leave ; 平衡堆栈 070103E9 5B pop ebx ; 取出正确的IAT 070103EA C3 retn ; 返回我们的代码 修改了4行,二进制代码
8B 55 F0 C9 5B C3
代码中带自定位,随便放在哪里都能执行,然后对最后一行原地打转的下断点,在call 05470019新建eip,F9运行,停在了最后一行,此时
用ImportREC重新Get Imports,已经全部为有效的,Ok,Fix Dump。运行脱壳后的程序已经没有30天试用的信息了。
三,后记 注意:如果软件已经过期,是无法脱壳的,一定要能够试用才能够脱壳。我当时没有想过要去脱壳,只是好玩,把日期改到了1年以后,发现不
能用了,改回来也不能用,换到同学机器上才把壳给脱掉==!
老师给的是MINITAB 14.10,安装包只有15M,由于国庆没带回来,在网上找不到14.10的了,找了个14.20的,发现安装包有78M,汗~~升级了
一个小版本,突然膨胀这么大。
-------------------------------------------------------------------------------- 【版权声明】: 本文原创于海风月影, 转载请注明作者并保持文章的完整, 谢谢! 2006年10月02日 18:00:58 穷人,买不起正版前2天用到一些专业软件,穷人,买不起,花了一些时间去破解,真累
一个是中国人做的 xxxx生物信息软件 ,VB 6 写的,功能好像比较强大,一套要2000左右,学院穷,只买了一套,我们学生显然用不到了~~~,只好操刀“干”了它~
还有一个是minitab,统计软件,当然是比不上大名鼎鼎的matlab,不过它有优势,只有38M,不像matlab随便就要X百兆。是小型统计软件中功能最全的一个,我们要的就是小巧玲珑
转贴:正则表达式30分钟入门教程正则表达式30分钟入门教程作者:deerchao 来源:unibetter大学生社区 转载请注明来源 本文目标30分钟内让你明白正则表达式是什么,并对它有一些基本的了解,让你可以在自己的程序或网页里使用它。一旦入门后,你可以从网上找到更多更详细的资料来继续学习。 别被下面那些复杂的表达式吓倒,只要跟着我一步一步来,你会发现正则表达式其实并不像你想像中的那么困难。当然,如果你看完了这篇教程之后发现自己明白了很多,却又几乎什么都记不得,那也是很正常的--其实我认为没接触过正则表达式的人在看完这篇教程后能把提到过的语法记住80%以上的可能性为零。这里只是让你明白基本道理,以后你还需要多练习,多查资料,才能熟练掌握正则表达式。 说明正则表达式是用于进行文本匹配的工具,所以本文里多次提到了在字符串里搜索/查找,这种说法的意思是在给定的字符串中,查找与给定的正则表达式相匹配的部分。有可能字符串里有不止一个部分满足给定的正则表达式,这时每一个这样的部分被称为一个匹配。匹配在本文里可能会有三种意思:一种是形容词性的,比如说一个字符串匹配一个表达式;一种是动词性的,比如说在字符串里匹配正则表达式;还有一种是名字性的,就是刚刚说到的“字符串中满足给定的正则表达式的一部分”。 文本格式约定:专业术语 特殊代码/语法格式 正则表达式 正则表达式中的一部分(用于分析) 用于在其中搜索的字符串 对正则表达式或其中一部分的说明 什么是正则表达式?很可能你使用过Windows/Dos下用于文件查找的通配符,也就是*和?。如果你想查找某个目录下的所有的Word文档的话,你会搜索*.doc。在这里,*会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比通配符更能精确地描述你的需求--当然,代价就是更复杂。比如你可以编写一个正则表达式来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像010-12345678或0376-7654321)。 入门在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。例如,\d+就是一个简洁的代码,代表着规则1位或更多位数字,2008就符合这个规则,而A3则不符合(它包含了不是数字的字符)。 学习正则表达式的最好方法是从例子开始,理解例子之后再自己对例子进行修改,实验。下面给出了不少简单的例子,并对它们作了详细的说明。 假设你在一篇英文小说里查找hi,你可以使用正则正则表达式hi。 这是最简单的正则表达式了,它可以精确匹配这样的字符串:由两个字符组成,前一个字符是h,后一个是i。通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配hi,HI,Hi,hI。 不幸的是,很多单词里包含hi这两个连续的字符,比如him,history,high等等。用hi来查找的话,这里边的hi也会被找出来。如果要精确地查找hi这个单词的话,我们应该使用\bhi\b。 \b是正则表达式规定的一个特殊代码,代表着单词的开头或结尾。虽然通常英文的单词是由空格或标点符号或换行为分隔的,但是\b并不代表这些单词分隔符中的任何一个,只代表一个位置。 假如你要找的是hi后面不远处跟着一个Lucy,你应该用\bhi\b.*\bLucy\b。 这里,.是另一个特殊代码,代表除了换行符以外的任意字符。*同样是特殊的代码,不过它代表的不是字符,也不是位置,而是数量--它指定*前边的内容可以重复任意次以使整个表达式得到匹配。因此,.*连在一起就意味着任意数量的不包含换行的字符。现在\bhi\b.*\bLucy\b的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词。 如果同时使用其它的一些特殊代码,我们就能构造出功能更强大的正则表达式。比如下面这个例子: 0\d\d-\d\d\d\d\d\d\d\d代表着这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话号码,当然,这个例子只能匹配区号为3位的情形,想同时匹配区号为4位的话,请在教程的下面寻找答案)。 这里的\d是一个新的特殊代码,代表任意的数字(0,或1,或2,或。。。)。-不是特殊代码,只代表它本身--连字号。 为了避免那么多烦人的重复,我们也可以这样写这个表达式:0\d{2}-\d{8} 这里\d后面的{2}({8})指定的是前面\d必须连续重复出现2次(8次)。 测试正则表达式如果你不觉得正则表达式很难读写的话,要么你是一个天才,要么,你不是地球人。正则表达式的语法很令人头疼,即使对经常使用它的人来说也是如此。由于难于读写,容易出错,所以很有必要创建一种工具来测试正则表达式。 由于在不同的环境下正则表达式的一些细节是不相同的,本教程介绍的是Microsoft .net下正则表达式的行为,所以,我向你介绍一个.net下的工具The Regulator。首先你确保已经安装了.net Framework1.1,然后下载The Regulator,下载完后打开压缩包,运行setup.exe安装。
特殊代码现在你已经知道几个具有特殊意义的代码了,如\b,.,*,还有\d.事实上还有更多的特殊代码,比如\s代表任意的空白符,包括空格,制表符(Tab),换行符。\w代表着字母或数字。 下面来试试更多的例子: \ba\w*\b匹配以字母a开头的单词-先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)。 \d+匹配1个或更多连续的数字。这里的+是和*类似的特殊代码,不同的是*代表重复任意次(可能是0次),而+则代表重复1次或更多次。 \b\w{6}\b 匹配刚好6个字母/数字的单词。
特殊代码^以及$和\b有点类似,都匹配一个位置。^匹配你要用来查找的字符串的开头,$匹配结尾。这两个代码在验证输入的内容时非常有用,比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$。 这里的{5,12}和前面介绍过的{2}是类似的,只不过{2}代表只能不多不少重复2次,{5,12}则是必须重复最少5次,最多12次,否则都不匹配。 因为使用了^和$,所以输入的整个字符串都要用来和\d{5,12}来匹配,也就是说整个输入必须是5到12个数字,因此如果输入的QQ号能匹配这个正则表达式的话,那就符合要求了。 和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^和$的意义就变成了匹配行的开始处和结束处。 字符转义如果你想查找特殊代码本身的话,比如你查找.,或者*,就出现了问题:你没法指定它们,因为它们会被解释成其它的意思。这时你就必须使用\来取消这些字符的特殊意义。因此,你应该使用\.和\*。当然,要查找\本身,你也得用\\. 例如:www\.unibetter\.com匹配www.unibetter.com,c:\\windows匹配c:\windows,2\^8匹配2^8(通常这是2的8次方的书写方式)。 重复你已经看过了前面的*,+,{2},{5,12}这几个代表重复的方式了。下面是正则表达式中所有指定重复的方式:
下面是一些使用重复的例子: Windows\d+匹配Windows后面跟1个或更多数字 13\d{9}匹配以13后面跟9个数字(中国的手机号) ^\w+匹配一行的第一个单词(或整个字符串的第一个单词,具体代表哪个意思得看选项设置) 字符类要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集的特殊代码,但是如果你想匹配没有预定义特殊代码的字符集比如元音字母(a,e,i,o,u),怎么办? 很简单,你只需要在中括号里列出它们就行了,像[aeiou]就匹配任何一个元音字母,[.?!]匹配标点符号(.或?或!)(英文语句通常只以这三个标点结束)。要注意的是,在中括号中,特殊代码不会被解释成其它意义,所以我们不需要写成[\.\?!](事实上这样写会出错,因为出现了两次\)。 我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与\d就是完全一致的:一位数字,同理[a-z0-9A-Z]也完全等同于\w。 下面是一个更复杂的表达式:\(?0\d{2}[) -]?\d{8}。 这个表达式可以匹配几种格式的电话号码,像(010)88886666,或022-22334455,或02912345678等。我们对它进行一些分析吧:首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字({2}),然后是)或-或空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8})。不幸的是,它也能匹配010)12345678或(022-87654321这样的“不正确”的格式。要解决这个问题,请在本教程的下面查找答案。 反义有时需要查找不属于某个简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义:
例子:\S+代表不包含空白符的字符串。 <a[^>]+>代表用尖括号括起来的以a开头的字符串。 替换好了,现在终于到了解决3位或4位区号问题的时间了。正则表达式里的替换指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。听不明白?没关系,看例子: 0\d{2}-\d{8}|0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)。 \(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用替换|把这个表达式扩展成也支持4位区号的。 \d{5}-\d{4}|\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用替换时,顺序是很重要的。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配替换时,将会从左到右地测试每个条件,如果满足了某个条件的话,就不会去管其它的替换条件了。 Windows98|Windows2000|WindosXP这个例子是为了告诉你替换不仅仅能用于两种规则,也能用于更多种规则。 分组我们已经提到了怎么重复单个字符;但如果想要重复一个字符串又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(教程后面会有介绍)。 (\d{1,3}\.){3}\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}代表1到3位的数字,(\d{1,3}\.}{3}代表三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。 不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址(IP地址中每个数字都不能大于255)。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)。 理解这个表达式的关键是理解2[0-4]\d|25[0-5]|[01]?\d\d?,这里我就不细说了,你自己应该能分析得出来它的意义。 后向引用使用小括号指定一个子表达式后,匹配这个子表达式的文本可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:以分组的左括号为标志,从左向右,第一个分组的组号为1,第二个为2,以此类推。 后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。难以理解?请看示例: \b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, kitty kitty。首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),然后是1个或几个空白符(\s+,最后是前面匹配的那个单词(\1)。 你也可以自己指定子表达式的组号或组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k<Word>,所以上一个例子也可以写成这样:\b(?<Word>\w+)\b\s*\k<Word>\b。 使用小括号的时候,还有很多特定用途的语法。下面列出了最常用的一些:
我们已经讨论了前两种语法。第三个(?:exp)不会改变正则表达式的处理方式,只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面。 位置指定接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们用于指定一个位置,就像\b,^,$那样,因此它们也被称为零宽断言。最好还是拿例子来说明吧: (?=exp)也叫零宽先行断言,它匹配文本中的某些位置,这些位置的后面能匹配给定的后缀exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如果在查找I'm singing while you're dancing.时,它会匹配sing和danc。 (?<=exp)也叫零宽后行断言,它匹配文本中的某些位置,这些位置的前面能给定的前缀匹配exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。 假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})*\b。请仔细分析这个表达式,它可能不像你第一眼看出来的那么简单。 下面这个例子同时使用了前缀和后缀:(?<=\s)\d+(?=\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符)。 负向位置指定前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样: \b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总是匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w+\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting。负向位置指定能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b。 零宽负向先行断言(?!exp),只会匹配后缀exp不存在的位置。\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字。 同理,我们可以用(?<!exp),零宽负向后行断言来查找前缀exp不存在的位置:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字(实验时发现错误?注意你的“区分大小写”先项是否选中)。 一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容。(<?(\w+)>)指定了这样的前缀:被尖括号括起来的单词(比如可能是<b>),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是<b>的话,后缀就是</b>了。整个表达式匹配的是<b>和</b>之间的内容(再次提醒,不包括前缀和后缀本身)。 注释小括号的另一种用途是能过语法(?#comment)来包含注释。要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以把上一个表达式写成这样: (?<= # 查找前缀,但不包含它
<(\w+)> # 查找尖括号括起来的字母或数字(标签)
) # 前缀结束
.* # 匹配任意文本
(?= # 查找后缀,但不包含它
<\/\1> # 查找尖括号括起来的内容:前面是一个"/",后面是先前捕获的标签
) # 后缀结束
贪婪与懒惰当正则表达式中包含能接受重复的量词(指定数量的代码,例如*,{5,12}等)时,通常的行为是匹配尽可能多的字符。考虑这个表达式:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。 有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的量词都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧: a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab和ab。
还有些什么东西没提到我已经描述了构造正则表达式的大量元素,还有一些我没有提到的东西。下面是未提到的元素的列表,包含语法和简单的说明。你可以在网上找到更详细的参考资料来学习它们--当你需要用到它们的时候。如果你安装了MSDN Library,你也可以在里面找到关于.net下正则表达式详细的文档。
一些我认为你可能已经知道的术语的参考
网上的资源VC中添加小音乐国外很多注册机都有小音乐,下面我们来尝试如何给直接的程序加上小音乐,这里用的是 winmm 版的
先用ufmod中的工具 eff 选一首歌,生成一个歌的数据文件:如 xm.c
将 xm.c , ufmod.h , ufmod.obj 复制到工程目录下 在工程中加入上面3个文件,然后在主文件中加入下面2行: #include "ufmod.h"
#include "xm.c" 打开工程-设置,选连接,手工加入库文件 : winmm.lib
然后在程序启动的时候也就是
case WM_CREATE:
后面加上播放歌的语句
uFMOD_PlaySong ( xm , (void*)11163 , 1);
其中的 11163是生成文件 xm 里那个 xm[]数组的大小 ,1代表是内存数据
ok,收工。
附上需要下载的工具
更多的XM音乐下载:
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|