解决XP DDK编译的native程序在win2k下蓝屏的问题

2009年7月5日 万立新 1 条评论

最近写一个 native 程序,使用 xp ddk 编译,发现编出来的程序在 win2k 下运行会导致蓝屏,经过分析,发现原来是链接了一个 win2k 下的 ntdll.dll 未
实现的一个函数 RtlUnhandledExceptionFilter,所以加载时导致蓝屏。

尝试用 xp ddk 带的 Windows 2000 的 build 环境去编译,结果是

1>LINK : error LNK2001: unresolved external symbol _NtProcessStartupForGS@4
1>bufferoverflow.lib(gs_support.obj) : error LNK2019: unresolved external symbol
 __imp__RtlUnhandledExceptionFilter@4 referenced in function ___report_gsfailure

看来不设法把对 RtlUnhandledExceptionFilter 的引用去掉是解决不了这个问题了。

继续用 IDA 简单分析一下,发现这个函数是被 security_check_cookie 所调用到,我们知道 security_check_cookie 这个函数是微软最新的编译器自动添加到函数的尾部,用来检测函数是否存在堆栈发生缓存溢出的错误,如果是普通应用程序,我们可以通过把 c/c++ 的编译选项 Buffer  Security Check 关掉,这样就不使用编译器的 /GS 开关,就不会自动添加 security_check_cookie 这个函数了,可是在native程序里如何去掉 /GS 开关呢。

查找 DDK 目录下的所有包含 /GS 的文件,发现文件 i386mk.inc 里面有个 BUFFER_OVERFLOW_CHECKS 和 /GS 相关,在 sources 文件里加入一句 BUFFER_OVERFLOW_CHECKS=0,重新用 xp ddk 编译,发现编出来的 exe 里面没有再链接 RtlUnhandledExceptionFilter 函数了,放在 win2k下运行,也不蓝屏了,问题解决了。

分类: DDK / Drivers 标签:

几行代码让你的程序加入vbscipt脚本扩展功能

2009年6月27日 万立新 2 条评论

  很多时候,我们都希望在自己的程序中加入脚本的支持,利用脚本来很方便的扩展自己程序功能,脚本的优点我就不多说了,一般脚本用的最多的可能就是lua,python,perl,ruby之类的,其实我们也可以嵌vbscript或jscript,缺点就是不跨平台,因为解释器是微软实现的,但是也就因为如此,vbscript/jscript是这些脚本里面和windows系统结合的最好的,毕竟都是微软自家的东西.

  如果要嵌vbscript/jscript作为自己程序的脚本扩展,微软提供的标准的方法就是实现一个 IActiveScriptSite 接口,创建一个ProgID为VBScript或者JScript的对象,具体msdn的Knowledge Base上有两篇文章,分别介绍如何vbscript添加到基于MFC的程序和ATL的程序,文章id分别是221992和223139,这种方法虽然不复杂,但是我觉的还是太麻烦,我今天要说的是拿脚本组件(Windows Script Components)的方式来非常简单的实现把vbscript/jscript嵌到自己的程序里。

  windows系统内置的两种脚本就是vbscript和jscript,主要应用的场合都是web网页里,除此之外,写一个扩展名为vbs或者js的文件,就可以直接用系统关联好的脚本解释器运行起来,完成一些配置管理的操作,在没有大型开发工具的时候,又需要做一些简单的编程工作,拿个记事本就可以开发了,很有用.除了这些应用之外,windows还允许写一个扩展名为wsc(不一定非要是wsc扩展名,只是系统默认为这个扩展名设置了关联)的xml文件,变成一个com组件,并且可以像普通com组件一些注册,导出类型库.这个就是我要说的 windows 脚本组件.

  Windows Script Components是很早就随着 IE 在windows系统中内置的一种机制,用它来实现程序的脚本扩展,优点如下:

1。学习成本低,vb有大量的文章和忠实的用户,所以学习vbscript应该也很快,jscript就更不用说了,做网页的都熟悉。

2。使用简单,几行代码搞定,简单的东西才好,又不容易出错,开发效率又高。怎么个简单法,下面有例子说明

3。绿色,我们知道com组件需要注册了才能使用,这样就依赖于注册表,这是com组件的优点也是缺点,优点就是组件的使用者不用关注组件在哪,缺点就是注册表里要留点记录,不绿色,这个记录要是被破坏了还不能正常工作了,多版本共存实现的也不方便,所以.net就把这种方式改了,不用向注册表注册,但是为什么我说脚本组件就绿色了呢,因为脚本组件虽然也可以像普通的com组件一样的注册,但是它其实可以不用注册就能像普通com组件一样的使用,这一点也是我最喜欢的一点,因为这样就可以直接拿一个脚本文件生成一个对象去调用而不需要对系统做任何更改,也不用考虑多版本冲突的问题。脚本组件能够绿色的原因主要是因为脚本组件的加载器scrobj.dll默认已经注册了script协议,允许用名字对象(moniker)的方式绑定一个脚本文件到一个对象。

4。发布成本低,不需要带任何额外的库,因为vbscript/jscript的运行环境是windows系统内置的,ie用的就是这个环境,ie能工作你的代码就能工作。

5。直接实现http方式网络下载脚本,这个主要也是得益于强大的moniker机制,ie本身实现了一个urlmoniker,这样你在绑定脚本组件的时候加上http就能绑定一个网络路径了,具体写法看下面的例子。

6。和windows结合的好,vb很好用主要是windows大部分模块都提供了组件供vb调用,这些组件除了ActiveX类型运行需要容器的,别的大部分都可以在vbscript/jscript里面调用。

  综上所述,如果你的应用基于windows,希望用最少的代码,最快的方式加入脚本的扩展功能,并且脚本有可能不在本地,放在服务器上,经常更新,就像网页一样希望每次运行都取最新的,那么用 windows 脚本组件就是一个很好的选择了。

   下面讲讲怎么实现,首先是脚本组件的写法,就是一个xml文件,描述了组件的名字,暴露的方法,以及实际的代码,最简单的一个例子如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0"?>
<component id="mywsc">
<public>
<method name="test"/>
</public>
 
<script language="VBScript">
<![CDATA[
 
Sub test()
 msgbox "Hello, World"
End Sub
 
]]>
</script>
</component>

这个应该一看就明白,具体格式参考 MSDN->Web Development->Scripting->Windows Script Technologies->Windows Script Components。

如何一个文件实现多个组件,实现事件等,MSDN都非常详细,其中值得一提的就是调试方法,可以在脚本文件里面加入

<?component error="true" debug="true"?>

error=”true” 的意思是当脚本出错时就会提示错误信息,以及哪一行出的错。debug=”true” 的意思是把这个脚本暴露出来允许外部脚本调试器发现并调试自己,如果你有支持脚本调试的工具,比如Visual Studio 2008,你可以直接Attach到这个进程进行脚本调试。

当你发布这个脚本文件时,可以根据需求把这两个调试开关给关了。

说完了脚本文件,就该说如何让你的程序很简单的调用这个脚本文件了,如果是把这个组件注册到系统里,那么它就是一个标准的 com 组件,这种调用方法很成熟,文章很多,不是本文要讨论的内容,这里我主要要说一下用名字对象(moniker)的方式绑定一个脚本文件到一个对象,来使用这个脚本文件,这也是我认为最有意义的方式,因为这种方法不需要注册任何东西,很绿色,并且还可以实现自动从网上下载最新版本。

首先是 Visual C++的调用方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <windows.h>
#include <atlbase.h>
int _tmain(int argc, _TCHAR* argv[])
{
 CoInitialize(NULL);
 
 do
 {
  CComPtr<IBindCtx> pbc;
  CComPtr<IMoniker> pMoniker;
  CComPtr<IDispatch> pScript;
 
  if (FAILED(CreateBindCtx(0, &pbc)))
   break;
 
  ULONG lEaten;
  if (FAILED(MkParseDisplayName(pbc, CComBSTR(L"script:d:\\test.wsc"), &lEaten, &pMoniker)))
   break;
 
  if (FAILED(BindMoniker(pMoniker, 0, __uuidof(IDispatch), (void**)&pScript)))
   break;
 
  if (FAILED(pScript.Invoke0(L"test")))
   break;
 }
 while (false);
 
 CoUninitialize();
 
 return 0;
}

 这里面用到了ATL,尤其脚本组件绑定后返回的是一个 IDispatch 指针,直接调的话很麻烦,所以用ATL的智能指针封装一下,可以根据参数个数直接调Invoke0,Invoke1,Invoke2等,如果你习惯MFC,也可以用MFC 的 COleDispatchDriver 来封装返回的这个IDispatch 指针。

然后是 VB 或者 vbscript 的调用方法,代码如下:

1
2
Set oScript = GetObject("script:d:\\test.wsc")
oScript.test

VB 是不是够简单

下面说一下 C# 的调用方法,代码如下:

1
2
3
object oScript = System.Runtime.InteropServices.Marshal.BindToMoniker("script:d:\\test.wsc");
Type tScript = oScript.GetType();
tScript.InvokeMember("test", System.Reflection.BindingFlags.InvokeMethod, null, oScript, new object[0]);

在上面的三个例子里,如果你的脚本不是在d盘,而是服务器的某个路径,比如 http://www.lixinwan.com/test.wsc,那么那串字符串可以改为 “script:http://www.lixinwan.com/test.wsc”,就可以实现自动从服务器上取最新的代码,是不是很方便。

分类: Windows 标签: