【问题标题】:Create a DLL that can be run创建一个可以运行的DLL
【发布时间】:2012-08-11 19:03:33
【问题描述】:

Windows 有一个名为 rundll32.exe 的实用程序,可以将本机动态链接库作为应用程序执行。

假设我有一段代码打印“Hello World!”到控制台。是否可以用 C++(最好是 Visual C++)编写一个可以使用 rundll32.exe 执行并运行此代码的库?如果有,怎么做?

【问题讨论】:

  • 你知道,谷歌“rundll32”,检查DOCUMENTATION(即RTFM),然后就去做。好的,我会这样做并将结果作为“答案”呈现。但实际上,发现这些东西在很大程度上是编程的一部分,它是一项基本技能,你应该训练它!
  • @Alf,在 SO 上应该只询问未记录的内容吗?
  • @Petter:我认为,关于不理解的事情的问题很好。当一个人被卡住时,关于在哪里可以找到相关信息的问题也是很好的。但是一个简单地将阅读文档的工作转移给其他人的问题是不好的。然而,显然大多数 SO 居民不同意这种观点。更“困难”的问题通常会以“不具建设性”或“无法回答”的形式结束(通常已经给出了一些明确的答案)。
  • @Alf,我想你在这种情况下是有道理的,但总的来说,我认为一个问题就是一个问题,其余的只是问题是否得到详细回答或仅使用 lmgtfy链接到它。

标签: c++ visual-c++


【解决方案1】:

谷歌搜索“rundll32”,第三次点击是文档链接,

http://support.microsoft.com/kb/164787

根据该文档,rundll32 调用用户指定的函数,其签名类似于wWinMain(除了这里的第一个参数是窗口句柄而不是实例句柄),

void CALLBACK
EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);

所以,试试这个:

// File [foo.def]
EXPORTS
    sayHello
// File [foo.cpp]
#include <iostream>

namespace myCode {

    void sayHello()
    {
        using namespace std;
        cout << "Hello, world!" << endl;
    }

}    // namespace myCode

#undef UNICODE
#define UNICODE
#include <windows.h>

extern "C" 
__declspec( dllexport )
void CALLBACK sayHello( HWND, HINSTANCE, wchar_t const*, int )
{
    AllocConsole();
    freopen( "CONIN$", "r", stdin ); 
    freopen( "CONOUT$", "w", stdout ); 
    freopen( "CONOUT$", "w", stderr ); 

    DWORD const infoBoxOptions = MB_ICONINFORMATION | MB_SETFOREGROUND;
    MessageBox( 0, L"Before call...", L"DLL message:", infoBoxOptions );
    myCode::sayHello();
    MessageBox( 0, L"After call...", L"DLL message:", infoBoxOptions );
}

构建和运行:

[d:\开发\测试] > cl foo.cpp foo.def user32.lib /MD /LD /D _CRT_SECURE_NO_WARNINGS foo.cpp 创建库 foo.lib 和对象 foo.exp [d:\开发\测试] > rundll32 foo.dll,sayHello [d:\开发\测试] > _

输出显示在它自己的控制台窗口中,通过AllocConsole 创建,这通常是必要的,因为rundll32 是一个GUI 子系统程序(这也是freopen 调用的原因)。

要在现有控制台窗口中显示输出,只需省略对AllocConsolefreopen 的调用,并将rundll32 的标准输出重定向到管道。例如。当输出只有几行时,标准输出可以通过 Windows 的more 传递,或者通过一些 *nix-utility cat 传递更多行。但是,在标准命令解释器 [cmd.exe] 中,仅将输出重定向到 con 是行不通的。

【讨论】:

  • @Alf:您的“构建和运行”实际上并未显示预期的输出。
  • @wilx:你可以试试,你知道的。
  • 我需要更加谦虚。再次绊倒这个答案,我不得不尝试它以注意到那里的AllocConsole 呼叫。输出显示在其自己的控制台窗口中,这是必要的,因为rundll32 是一个 GUI 子系统程序。更新答案以解释这一点。发牢骚……
【解决方案2】:

http://support.microsoft.com/kb/164787

我相信这篇 MSDN 文章还是准确的;您将入口点定义为

  void CALLBACK EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);

【讨论】:

  • 我尝试过使用此代码 void CALLBACK func(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) { std::cout &lt;&lt; "Hello world!" &lt;&lt; std::endl; },但 rundll32.exe 失败:Missing entry: func
  • 你是如何用你的 DLL 启动 rundll32 的?
  • rundll32.exe DLLTest.dll,func
  • @MichaelBikovitsky:函数需要外化为C吗?
  • @GManNickG,你是什么意思?
【解决方案3】:

虽然可以使用 rundll32 在 DLL 中运行设计合理的函数,但不建议这样做。这样做意味着您的 DLL 受 rundll32 的进程设置(大地址感知、终端服务感知、DPI 感知、提升清单等)的支配。更糟糕的是:如果其他一些 rundll32 进程触发了应用程序兼容性行为(例如低碎片heap),那么这将影响包括你的所有 rundll32 进程。

只需编写一个单独的 EXE。

【讨论】:

    【解决方案4】:

    就像乔和你自己所说的那样,使用这样的东西:

     void CALLBACK func(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) 
     { 
       std::cout << "Hello world!" << std::endl; 
     }
    

    但是,CALLBACK = __stdcall,当你的“func”被导出时,它会变成_func@16

    您可以更改此名称,http://support.microsoft.com/kb/140485

    所以,您可能想尝试类似的方法:

    rundll32.exe DLLTest.dll,_func@16 
    

    【讨论】:

    • -1 不起作用。嗯,在发布之前尝试你的答案怎么样?
    • 好吧,根据文档(Joe 和你,Alf 发布了指向它的链接),如果不使用 .def 文件,默认情况下它会导出到 _EntryPoint@16。我的想法是,这个小小的改变就是缺少的。但是,我现在注意到它仅适用于 C 代码,而不适用于 C++ 编译的代码。反正我应该删除这篇文章,因为你有这么好的答案。
    • 一个问题是rundll32 是一个GUI 子系统程序,至少在Windows 7 中是这样,所以默认情况下没有控制台。通常可以重定向标准输出来解决这个问题,但在这种情况下,标准库不是由主程序加载,而是由 DLL 加载。所以它需要一些解决方法:虽然没什么特别的,谷歌搜索会找到简单的食谱(我刚刚遵循)。
    猜你喜欢
    • 2015-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多