Windows Rundll 和 Rundll32 接口

来源:互联网 发布:怎么修改mysql的密码 编辑:程序博客网 时间:2024/04/30 05:58
概要
Microsoft Windows 95、Windows 98 和 Windows Millennium Edition (Me) 包含两个命令行实用程序,分别名为 Rundll.exe 和 Rundll32.exe,使用它们可以调用从 16 位或 32 位 DLL 导出的函数。但是,使用 Rundll 和 Rundll32 程序并不能调用从任何 DLL 导出的任何函数。例如,您不能使用这两个实用程序调用从系统 DLL 导出的 Win32 API(应用程序编程接口)调用。这两个程序只允许您调用从 DLL 导出的、明确编写为供这两个程序调用的函数。本文详细介绍了 Rundll 和 Rundll32 程序在上面列出的 Windows 操作系统中的用法。

MIcrosoft Windows NT 4.0、Windows 2000 和 Windows XP 在发行时只附带 Rundll32。这些平台都不提供对 Rundll(Win16 实用程序)的支持。

Rundll 和 Rundll32 实用程序最初设计为仅供 Microsoft 内部使用。但是它们提供的功能非常通用,因此现在可供在一般情况下使用。请注意,Windows NT 4.0 发行时只随附 Rundll32 实用程序,并且只支持 Rundll32。
更多信息
Rundll 与 Rundll32 之比较
Rundll 加载和运行 16 位 DLL,而 Rundll32 加载和运行 32 位 DLL。如果您向 Rundll 或 Rundll32 传递了错误的 DLL 类型,它可能无法运行,并且不显示任何错误消息。
Rundll 命令行
Rundll 的命令行如下所示:    RUNDLL.EXE <dllname>,<entrypoint> <optional arguments>
                
示例如下:    RUNDLL.EXE SETUPX.DLL,InstallHinfSection 132 C:/WINDOWS/INF/SHELL.INF
                
在上面的命令行中,有三个问题需要仔细考虑: 1. Rundll 或 Rundll32 在标准位置搜索给定的 DLL 文件名(请参见有关 LoadLibrary() 函数的文档了解详细信息)。建议您提供 DLL 的完整路径以确保找到所需的 DLL。为获得最佳结果,请使用短文件名而不是长文件名,以确保不会出现非法字符。请特别注意,这意味着“C:/Program Files”文件夹中的 DLL 应转换为短名称。
2. <dllname> 不能包含任何空格、逗号和引号。这是 Rundll 命令行分析器的局限。
3. 在上面的命令行中,<dllname> 和 <entrypont> 函数名称之间的逗号 (,) 极为重要。如果缺少这个逗号分隔符,Rundll 或 Rundll32 将失败,而且不显示任何错误。此外,在 <dllname>、逗号和 <entrypoint> 函数之间不能有任何空白。

Rundll 的工作方式
Rundll 执行以下步骤: 1. 它分析命令行。
2. 它通过 LoadLibrary() 加载指定的 DLL。
3. 它通过 GetProcAddress() 获取 <entrypoint> 函数的地址。
4. 它调用 <entrypoint> 函数,传递作为 <optional arguments> 的命令行尾。
5. 当 <entrypoint> 函数返回时,Rundll.exe 将卸载 DLL 并退出。  

如何编写 DLL
在您的 DLL 中,使用以下原型编写 <entrypoint> 函数:

16 位 DLL:

  void FAR PASCAL __loadds
  EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);
                
32 位 DLL:   void CALLBACK
  EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);
                
同样,对于 EntryPoint 函数也有三个问题需要考虑: 1. 很明显,“EntryPoint”名称应该替换为您的入口点函数的实际名称。请注意,Rundll32 的入口点与 32 位 DLL 中的 DllEntryPoint 函数完全无关,后者处理进程和线程连接/分离通知。
2. Rundll32 的入口点函数必须使用 _stdcall 调用约定定义(CALLBACK 默认情况下使用 _stdcall 属性)。如果缺少 _stdcall 属性,则函数默认使用 _cdecl 调用约定,然后 Rundll32 将在调用该函数后异常终止。
3. 由于您必须如上所述使用 _stdcall 调用约定声明该函数,因此如果 DLL 是用 C 语言编写的,Visual C++ 编译器会将该函数实际导出为 _EntryPoint@16;如果 DLL 是用 C++ 语言编写的,则 Visual C++ 编译器将进一步使用名称修饰。所以,请务必在 Rundll 或 Rundll32 的命令行中使用正确导出的名称。如果您想避免使用修饰名称,请使用 .def 文件,并按名称导出入口点函数。在使用 Visual C++ 编译器时,请参考产品文档和以下文章,以了解有关名称修饰的更多信息:  
引用


Exporting PASCAL-Like Symbols in 32-Bit DLLs





SUMMARY

There is no _pascal keyword in the 32-bit editions of Visual C++. Instead the Windef.h header file has PASCAL defined as __stdcall. This creates the correct style calling convention for the function (the called function cleans up the stack) but decorates the function name differently. So, when __declspec(dllexport) is used (in a .dll file, for example), the decorated name is exported instead of the desired PASCAL style name, which is undecorated and all uppercase.

MORE INFORMATION

PASCAL name decoration is simply the undecorated symbol name in uppercase letters. __stdcall name decoration prefixes the symbol name with an underscore (_) and appends the symbol with an at sign (@) character followed by the number of bytes in the argument list (the required stack space). So, the function when declared as:    int  __stdcall func (int a, double b)

                

is decorated as:    _func@12

                

The C calling convention (__cdecl) decorates the name as _func. Whereas the desired PASCAL style name is FUNC.



To get the decorated name set the Generate Mapfile option in the Linker General category setting.



Use of __declspec(dllexport) does the following: &#8226; If the function is exported with C calling convention (_cdecl), it strips the leading underscore (_) when the name is exported.  

&#8226; If the function being exported does not use the C calling convention (for example, __stdcall ), it exports the decorated name.  

So to simulate PASCAL name decoration and calling conventions, you must have the "Called Function stack clean-up" provided by using __stdcall and the undecorated uppercase name.



Because there is no way to override who does the stack clean up, you must use __stdcall. To undecorate names with __stdcall, you must specify them by using aliases in the EXPORTS section of the .def file. This is shown below for the following function declaration:    int  __stdcall MyFunc (int a, double b);

   void __stdcall InitCode (void);

                

In the .def file:    EXPORTS

      MYFUNC=_MyFunc@12

      INITCODE=_InitCode@0

                

For .dll files to be called by programs written in the 32-bit versions of Visual Basic (versions 4.0 and above), the alias technique shown in this article is needed in the .def file. If alias is done in the Visual Basic program, use of aliasing in the .def file is not necessary. It can be done on the Visual Basic program by adding an Alias clause to the Declare statement as shown here: Declare Function MyFunc Lib "dlllibname" Alias "_MyFunc@12"  (...)

   As Integer

                

The complete syntax for the Visual Basic Declare statement follows:    [Public | Private ] Declare Function name Lib

   "libname" [Alias "aliasname" ] [([arglist])][As type]

                

References

For more information, query the MSDN compact disc using these keywords:

VB alias DLL



Rundll 入口点的参数如下所示: hwnd -  窗口句柄,它应该用作您的 DLL 创建的任何窗口的
所有者窗口
hinst - 您的 DLL 的实例句柄
lpszCmdLine - 您的 DLL 应该分析的 ASCIIZ 命令行
nCmdShow - 描述您的 DLL 的窗口应如何显示
                
在下面的示例中:      RUNDLL.EXE SETUPX.DLL,InstallHinfSection 132 C:/WINDOWS/INF/SHELL.INF
                
Rundll 将调用 Setupx.dll 中的 InstallHinfSection() 入口点函数,并向其传递以下参数: hwnd =(父窗口句柄)
hinst = HINSTANCE of SETUPX.DLL
lpszCmdLine = "132 C:/WINDOWS/INF/SHELL.INF"
nCmdShow =(传递给 CreateProcess 的任何 nCmdShow)
                
请注意,<entrypoint> 函数(在上例中为 InstallHinfSection())必须分析它自己的命令行(上面的 lpszCmdLine 参数),并在必要时使用个别参数。Rundll.exe 最多只分析到传递给它的命令行的可选参数。其余的分析工作则由 <entrypoint> 函数完成。
有关 Windows 95 和 Windows NT 之间的区别的特别提示
在 Windows NT、Windows 2000 和 Windows XP 中,Rundll32.exe 的行为稍有不同,以便适应 UNICODE 命令行。

Windows NT 首先尝试对 <EntryPoint>W 调用 GetProcAddress 以获取其地址。如果找到该入口点,则假定原型为:    void CALLBACK
   EntryPointW(HWND hwnd, HINSTANCE hinst, LPWSTR lpszCmdLine,
               int nCmdShow);
                
这与 ANSI EntryPoint 相同,只是 lpszCmdLine 参数现在是一个 UNICODE 字符串。

如果找不到 <EntryPoint>W 入口点,则 Windows NT 将对 <entrypoint>A 和 <entrypoint> 调用 GetProcAddress 以获取其地址。如果这两个地址都找不到,则将入口点视为 ANSI 入口点,并像在 Windows 95/98/Me 中那样进行处理。因此,如果您希望您的 DLL 在带 ANSI 支持的 Windows 95 中以及带 UNICODE 支持的 Windows NT/2000/XP 中运行,您应该导出以下两个函数:EntryPointW 和 EntryPoint。在 Windows NT/2000/Me 中,通过 UNICODE 命令行调用 EntryPointW 函数;在 Windows 95/98/Me 中,则通过 ANSI 命令行调用 EntryPoint 函数。