从VB.NET向Excel输入框传递值

11

我正在尝试自动填充一些带有宏的Excel表格数据。现在这个Excel被保护了,我无法获取秘钥。虽然我可以运行宏,但当我尝试传递参数时,出现了参数不匹配的问题。

如果我只运行该宏的名称,会得到一个inputbox,需要额外的参数作为输入,并自动生成某些列的值。目前,我必须手动将这个值输入到inputbox中。有没有办法自动化这个过程,即捕获由vb.net脚本引发的宏弹出框,并从那里输入值?也就是说,我想运行该宏,在弹出提示要求输入某个值后,使用vb.net代码向该弹出框输入值。

以下是我迄今为止所拥有的:

Public Class Form1
    Dim excelApp As New Excel.Application
    Dim excelWorkbook As Excel.Workbook
    Dim excelWorkSheet As Excel.Worksheet

    Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
        excelWorkbook = excelApp.Workbooks.Open("D:/excelSheets/plan_management_data_templates_network.xls")
        excelApp.Visible = True

        excelWorkSheet = excelWorkbook.Sheets("Networks")

        With excelWorkSheet
            .Range("B7").Value = "AR"
        End With

        excelApp.Run("createNetworks")
        // now here I would like to enter the value into the createNetworks Popup box
        excelApp.Quit()
        releaseObject(excelApp)
        releaseObject(excelWorkbook)
    End Sub

宏定义

createNetworks()
//does so basic comparisons on existing populated fields
//if true prompts an inputbox and waits for user input.

这也阻塞了我的 VB.NET 脚本继续执行下一行。


在Excel-VBA中,类似这样的代码:A = Inputbox("这是提示", "这是标题", "这里是输入框中的默认值") - Kazimierz Jawor
我没有访问宏代码的权限,但我猜那是语法。当我从vb.net调用宏时,我可以传递一个参数作为输入框的输入吗? - Shouvik
2
当您运行宏时,输入框是否会自动获取焦点?如果是的话,您可以尝试使用sendkeys来填充和提交输入框。 - NickSlash
1
如果输入ox每次都是相同的标题,您可以在窗口上进行搜索并向其发送消息吗?http://stackoverflow.com/a/13150233/1685810和http://www.vbforums.com/showthread.php?637783-RESOLVED-Sendkeys-and-Findwindow - glh
任何答案都需要涉及“模态”对话框的概念:InputBox抓住了焦点和逻辑流 - 您的代码已经将控制权交给了模态对象,并且在模态对象归还控制权之前不会运行任何命令。VBA论坛上有一个非常类似的问题,答案涉及到从API计时器中延迟回调的相当令人生畏的使用 - Nigel Heffernan
显示剩余22条评论
2个回答

13

就像你和我都有名字一样,窗口也有 handles(hWnd)Class 等等。一旦你知道了那个 hWnd 是什么,就更容易与该窗口交互。

这是 InputBox 的屏幕截图:

enter image description here

逻辑:

  1. 使用 FindWindow 和标题为Create Network IDs的输入框的说明查找输入框的句柄。

  2. 找到输入框后,使用 FindWindowEx 查找该窗口中编辑框的句柄。

  3. 一旦找到编辑框的句柄,只需使用 SendMessage 写入它即可。

在下面的示例中,我们将向 Excel 输入框中写入 It is possible to Interact with InputBox from VB.Net

代码:

创建一个窗体并添加一个按钮。

enter image description here

复制以下代码:

Imports System.Runtime.InteropServices
Imports System.Text

Public Class Form1
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
    (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer

    Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
    (ByVal hWnd1 As Integer, ByVal hWnd2 As Integer, ByVal lpsz1 As String, _
    ByVal lpsz2 As String) As Integer

    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
    (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, _
    ByVal lParam As String) As Integer

    Const WM_SETTEXT = &HC

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim Ret As Integer, ChildRet As Integer

        '~~> String we want to write to Input Box
        Dim sMsg As String = "It is possible to Interact with InputBox from VB.Net"

        '~~> Get the handle of the "Input Box" Window
        Ret = FindWindow(vbNullString, "Create Network IDs")

        If Ret <> 0 Then
            'MessageBox.Show("Input Box Window Found")

            '~~> Get the handle of the Text Area "Window"
            ChildRet = FindWindowEx(Ret, 0, "EDTBX", vbNullString)

            '~~> Check if we found it or not
            If ChildRet <> 0 Then
                'MessageBox.Show("Text Area Window Found")
                SendMess(sMsg, ChildRet)
            End If
        End If
    End Sub

    Sub SendMess(ByVal Message As String, ByVal hwnd As Long)
        Call SendMessage(hwnd, WM_SETTEXT, False, Message)
    End Sub
End Class

屏幕截图

当您运行代码时,会得到以下内容:

在此输入图像描述


编辑(基于自动化聊天中的OK / Cancel进一步请求)

自动化输入框的OK / Cancel按钮

这里有一个有趣的事实。

您可以以两种方式调用Excel中的InputBox函数。

Sub Sample1()
    Dim Ret
    Ret = Application.InputBox("Called Via Application.InputBox", "Sample Title")
End Sub

Sub Sample2()
    Dim Ret
    Ret = InputBox("Called Via InputBox", "Sample Title")
End Sub

输入图像描述

在您的情况下,使用了第一种方式,不幸的是,OKCANCEL 按钮没有句柄,所以不幸的是,您将不得不使用 SendKeys(Ouch!!!) 与之交互。如果 Inbutbox 是通过第二种方法生成的,那么我们可以很容易地自动化处理 OKCANCEL 按钮 :)

输入图像描述

附加信息:

在 Visual Studio 2010 Ultimate (64 位) / Excel 2010 (32 位) 上测试通过

受您的问题启发,我实际上写了一篇 文章 ,介绍如何与 InputBox 上的 OK 按钮交互。


哇,这是很棒的东西。让我明天来看看。与此同时,我不明白为什么这不能回答我的问题,所以接受答案! - Shouvik
@SiddharthRout 好的,它运行得非常好!我将再次开启悬赏以获得更多积分。但是我似乎无法颁发悬赏。 :( - Shouvik
感谢你的友善举动,Shouvik。但我回答这个问题并不是为了赏金,而是因为这是一个有趣且具有挑战性的问题。你知道吗?你的问题甚至启发了我写一篇博客文章 :) 顺便说一句,我不确定像这样已经解决的问题是否可以提出赏金。让我去查一下元数据。 :) - Siddharth Rout
2
+1 就像我想的一样!https://dev59.com/YmUo5IYBdhLWcg3wmAWi#v5PjnYgBc1ULPQZFz2kQ - glh
@SiddharthRout 欢迎接受挑战,至于赏金点数!这是应该归你的,感谢你接受挑战! :) - Shouvik
1
@glh,我其实不明白你的意思。但在Siddharth的回答之后,情况变得更加清晰了。所以,感谢你的探究。 - Shouvik

4

目前,我采用一种方法,在脚本调用宏之前运行一个线程。该线程检查输入框是否被调用。如果是,则从该位置获取值,并使用sendkeys提交该框。

这是一个基本的解决方案,但我希望有一个更优雅的解决方案来解决这个问题。

我的解决方案代码:

Public Class Form1
    Dim excelApp As New Excel.Application
    Dim excelWorkbook As Excel.Workbook
    Dim excelWorkSheet As Excel.Worksheet

    Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
        excelWorkbook = excelApp.Workbooks.Open("D:/excelSheets/some_excel.xls")
        excelApp.Visible = True

        excelWorkSheet = excelWorkbook.Sheets("SheetName")

        With excelWorkSheet
            .Range("B7").Value = "Value"
        End With

        Dim trd = New Thread(Sub() Me.SendInputs("ValueForInputBox"))
        trd.IsBackground = True
        trd.Start()

        excelApp.Run("macroName")
        trd.Join()
        releaseObject(trd)

        excelApp.Quit()
        releaseObject(excelApp)
        releaseObject(excelWorkbook)
    End Sub
    Private Sub releaseObject(ByVal obj As Object)
       Try
           System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
           obj = Nothing
       Catch ex As Exception
           obj = Nothing
       Finally
           GC.Collect()
       End Try
    End Sub
    Private Sub SendInputs(ByVal noOfIds As String)
        Thread.Sleep(100)
        SendKeys.SendWait(noOfIds)
        SendKeys.SendWait("{ENTER}")
        SendKeys.SendWait("{ENTER}")
    End Sub

你介意分享一下你检测输入框并发送值的方法吗? - ajakblackgoat
我花了一个小时来解决这个问题,并得出了相同的结论。SE网站的目标是成为未来多年的知识和答案资源。如果您发布您当前的解决方案,可能会有人能够改进它。 - Jeremy Thompson
@MandeepSingh 这是一个可行的解决方案,但 siddhart 的解决方案更加优雅。如果你采用他的方法,我相信它会比这个更好地工作,特别是在这段代码无法有效帮助你的情况下,例如当你有一个备用活动窗口时,但这需要花费更多时间。 - Shouvik
@Shouvik 在我的 Excel 文件中,我有 Application.InputBox,siddhart 的解决方案在 Application.InputBox 上不起作用,所以我必须使用这个解决方案。 - Mandeep Singh
这真的不应该成为问题。你可以使用UI Spy进行检查,并获取此窗口的句柄。无论如何,如果这个解决方案适用于您,我很高兴能帮助到您。 - Shouvik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接