【问题标题】:C++ DLL which can inform a calling program about its progressC++ DLL,可以通知调用程序其进度
【发布时间】:2011-07-17 11:57:30
【问题描述】:

我的老板希望我用 C++ (MSVC++2010) 编写一个 DLL,该 DLL 可以执行卷影复制,他可以从 VB6(或稍后其他语言)调用该 DLL,并且可以在执行时返回状态更新还没有结束。他称之为“事件”。

我有一种感觉,我终于需要学习 COM(我宁愿不......)......而且,我想到了一个回调函数,但是将函数指针从 VB6 移交给 C++ 肯定是不可能的?

谁能概述我必须学习的内容以及如何完成,无论是否使用 COM?

编辑: 要回答一个问题,工作流程应该是:

  1. VB6 应用程序确定要备份哪些文件

  2. 给我一​​个路径并制作一个包含此路径的临时卷影副本并返回一个安装点(或类似的)

    • 在这一步中,我会定期告诉 VB6 应用程序我还有多远

  3. VB6 应用程序备份卷影副本,然后删除卷影副本。

【问题讨论】:

    标签: c++ dll com vb6 volume-shadow-service


    【解决方案1】:

    您可以使用AddressOf 运算符将指向“显示进度”函数的指针从 VB 应用程序传递给 C++ DLL 应用程序:

    Declare Function CallMyDll ...
    
    Sub DisplayStatus(ByVal SomeParameter As Long)
        ' ...
    End SUb
    
    Sub DoSomething()
        Call CallMyDll(AddressOf DisplayStatus)
    End Sub
    

    一些不那么明显的陷阱:

    1. 您必须使用 __stdcall calling convention 声明您的 C++ 函数指针。 (谢谢,Alexandre C!)

    2. 在您的 VB 回调函数中,explicitly mark your parameters as by-value 使用关键字 ByVal。同样,在 C++ 函数指针中,不要将参数标记为引用。

    3. 如果要将字符串传递给回调,或从中检索字符串,则必须考虑到 VB Strings 不等于 C char*s,C++ std::string s,或微软的CStrings。 VB Strings 必须映射到微软相当晦涩的BSTR 数据类型。

    4. 我忘记了一件非常重要的事情:您的回调必须在 VB 模块内(即,它必须是“纯粹的函数”,而不是类或表单的方法)。强>

    【讨论】:

    • 它也适用于 VBA。请注意,约定通常是stdcall(它在 VBA 中),因此您必须在 C 代码中明确指定它。 BSTR 类型可以用CComBstrstd::wstring 和一些你一生中编写过的魔法代码来处理。
    • 谢谢!实际上,检索字符串不是问题:BSTR 还指向一个以零结尾的 C 字符串。只是另一种方式不起作用,因为BSTR 包含BSTR-1 的长度,afaik。你是对的__stdcall
    • @Felix:反之则用SysAllocString。如果让 VB 释放字符串,没有什么难的。
    • @Alexandre C:我喜欢 C++,我喜欢 OOP,我喜欢模板,但是处理 VB 让我陷入了 hack 模式。
    • 是的,在我的工作中,我们也需要使用 VB 进行“函数式编程”。这很糟糕。
    【解决方案2】:

    您的老板可以使用 Declare 语句调用由 DLL 导出的函数。这不能很好地扩展,但对于简单的 API 来说很好。您的函数应使用 extern "C" 和 __declspec(dllexport) 声明符导出,使用 __stdcall 调用约定并仅使用简单的参数类型。

    【讨论】:

      【解决方案3】:

      何时必须提供这些状态更新?
      在设置 VSS 时?还是在备份数据等时? 在后一种情况下,VSS 只返回一个路径,可以使用 直接来自 VB。
      但是对于设置......它也可能有意义,因为它可能相当慢, 但我想你可以把它变成一个状态机——把所有的 VSS API 调用 进入一个大 switch() 并创建一个函数,该函数将一个一个地调用它们并且 更新状态变量。

      更新:我的意思是这样的。 Init() 和 Step() 是由 dll 导出并从 VB 调用的函数。
      或者,您可以生成一个线程来执行所有这些操作(仍在 dll 中)并返回 像 Step() 中的 Sleep(100) 之后的状态更新。

      int s; // state
      
      int Init( void ) { s=0; }
      
      int Step( void ) {
      
        switch( s ) {
      
          default: break;
      
          case 0: 
          CoInitialize(0); break;
      
          case 1: 
          r = CreateVssBackupComponents(&vssc); 
          if( FAILED(r) ) s=-1;
          if( vssc==0 ) s=-2;
          break;
      
          case 2: 
          r = vssc->InitializeForBackup();
          if( FAILED(r) ) s=-3; 
          break;
      
          case 3: 
          r = vssc->SetBackupState( FALSE, FALSE, VSS_BT_COPY, FALSE );
          if( FAILED(r) ) s=-4;
          break;
      
          [...]
      
        }
      
        s += (s>=0);
      }
      

      【讨论】:

      • 你的意思是,VB6 有一个线程,它会定期检查我通过引用给出的变量是否发生了变化?
      • 这也是我向他建议的解决方案之一!特别感谢详细的过程,您显然以前使用过卷影副本!我已经查看了 Windows SDK 示例 (VSHADOW.EXE 3.0),这对于初学者来说非常困难
      【解决方案4】:

      我会在没有 COM 的情况下完成这项工作。相反,我会让 VB 部分向 DLL 发送一个窗口句柄,然后 DLL 将向窗口发布一条消息,告知其进度。

      你可以使用 COM,但这有点像用大锤打苍蝇。

      【讨论】:

        【解决方案5】:

        走 unix-y 路线。创建一个执行复制并将进度指示器输出到标准输出的程序。让 VB 应用解析此输出以获取完成百分比。

        【讨论】:

        • 谢谢,这是我会建议他的解决方案之一
        【解决方案6】:

        我会这样做:

        • VB 应用程序调用 DLL 中要求启动卷影副本的函数。您的 Dll 启动一个执行卷影复制的线程并将一个 ID(线程 ID?)返回给 VB 应用程序

        • VB 应用定期调用 DLL 中的函数“Progress”,传递之前收到的操作 ID:progress 函数返回一个整数 0-100 来指示进度

        • 这样 VB 应用程序可以并行启动多个操作。

        执行复制的线程应该不时更新“进度”变量。 DLL 中的另一个停止复制的函数也很有用。

        【讨论】:

        • 这与我在@Shelwien 的回答之后(在他澄清之前)的想法非常相似。有可能这将是我选择的方式。这取决于是否有必要同时启动多个备份,但我对此表示怀疑。尽管如此,这是一个非常好的和干净的选择,我一定会考虑。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多