无法在VBCodeProvider中加载临时文件

时间:2013-02-06 15:05:50

标签: vb.net function

这是我的脚本代码:

Imports System.Diagnostics

Public Class Script
Implements IScript

Public Sub DoWork(w As WebBrowser, f As Form1) Implements IScript.DoWork
    w.Navigate("http://www.google.com")
    wait("5000")
    w.Document.All("input").InvokeMember("click")
    w.Document.All("input").SetAttribute("value", "Torrenter is the best!")
    wait("2000")
    w.Document.All("421").InvokeMember("click")
wait("1000")
End Sub

Public Sub wait(ByVal interval As Integer)
    Dim sw As New Stopwatch
    sw.Start()
    Do While sw.ElapsedMilliseconds < interval
        ' Allows UI to remain responsive
        Application.DoEvents()
    Loop
    sw.Stop()
End Sub

结束班

在代码:

Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
    If int1.Text = "1" Then
        int1.Text = "0"
        Dim script As IScript = GenerateScript(File.ReadAllText(ListBox2.Items.Item(int2).ToString()))
        script.DoWork(WebBrowser1, Me) 'Object reference not set to an instance of an object.
        int2 = int2 + 1
        int1.Text = "1"
    End If
End Sub

为什么呢? :( 它应该在第一个完成后启动下一个脚本。我尝试了4种方法,但我无法理解为什么。

1 个答案:

答案 0 :(得分:0)

问题是你的脚本代码无法编译,但是你正试图从编译的程序集中实例化一个对象。由于编译失败,程序集实际上并不存在,因此出错。如果您修改Return方法中的GenerateScript行,以便显示编译错误,则实际问题会更明确:

Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, codes)
If results.Errors.HasErrors Then
    Dim builder As New StringBuilder()
    builder.AppendLine("Script failed to compile due to the following errors:")
    For Each i As CompilerError In results.Errors
        builder.AppendFormat("Line {0}: {1}", i.Line, i.ErrorText)
        builder.AppendLine()
    Next
    Throw New Exception(builder.ToString())
Else
    Return CType(results.CompiledAssembly.CreateInstance("Script"), IScript)
End If

我怀疑它编译失败的原因之一是因为脚本使用未定义的IScript。它抱怨它未定义的原因有两个原因。首先,您将IScript接口声明为嵌套在Form1类中。您应该将其移动到表单类之外,以便它不嵌套在任何其他类型中。其次,您没有指定完整的命名空间,也没有在脚本中导入命名空间。在编译之前,您可以自动将Imports行添加到脚本代码的开头,如下所示:

Dim interfaceNamespace As String = GetType(IScript).Namespace
Dim codes As String = "Imports " & interfaceNamespace & Environment.NewLine & code

正如我在上面的评论中提到的,你真的应该将字符串数组传递给CompileAssemblyFromSource方法,而不是字符串。我不确定如何编译,除非那是Option Strict Off以某种方式允许的东西?在任何情况下,它都需要一个数组,所以你应该给它一个,就像这样:

Dim interfaceNamespace As String = GetType(IScript).Namespace
Dim codeArray() As String = New String() {"Imports " & interfaceNamespace & Environment.NewLine & code}
Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, codeArray)

脚本无法编译的另一个明显原因是因为您使用了Form1类的方法和属性,就好像它是该类的成员一样。请记住,脚本文件源代码定义的Script类是单独程序集中完全独立的类。它没有引用表单,除非你给它一个引用,例如,你可以像这样定义接口:

Public Interface IScript
    Sub DoWork(f As Form1)
End Interface

然后,在您的脚本中,您可以这样做:

Public Class Script
    Implements IScript
    Public Sub DoWork(f As Form1) Implements IScript.DoWork
        f.WebBrowser1.Navigate("http://www.google.com")
        f.wait("5000")
        f.wait("4000")
        f.WebBrowser1.Document.All("input").InvokeMember("click")
        f.WebBrowser1.Document.All("input").SetAttribute("value", "User")
        f.wait("2000")
        f.WebBrowser1.Document.All("421").InvokeMember("click")
    End Sub
End Class

更新

好的,既然你无法让它工作,我不希望这整个对话完全失败,我把一个工作项目放在一起并进行测试。以下是您需要做的工作。

IScript.vb的内容

Public Interface IScript
    Sub DoWork(w As WebBrowser)
End Interface

Form1.vb的内容

Imports Microsoft.VisualBasic
Imports System.CodeDom.Compiler
Imports System.Reflection
Imports System.IO
Imports System.Text

Public Class Form1
    Dim int1 As Integer = 0
    Dim int2 As Integer = 0
    Dim p As Point

    Public Function GenerateScript(ByVal code As String) As IScript
        Using provider As New VBCodeProvider()
            Dim parameters As New CompilerParameters()
            parameters.GenerateInMemory = True
            parameters.ReferencedAssemblies.Add(GetType(WebBrowser).Assembly.Location)
            parameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location)
            Dim interfaceNamespace As String = GetType(IScript).Namespace
            code = "Imports System.Windows.Forms" & Environment.NewLine & "Imports " & interfaceNamespace & Environment.NewLine & code
            Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, code)
            If results.Errors.HasErrors Then
                Dim builder As New StringBuilder()
                builder.AppendLine("Script failed to compile due to the following errors:")
                For Each i As CompilerError In results.Errors
                    builder.AppendFormat("Line {0}: {1}", i.Line, i.ErrorText)
                    builder.AppendLine()
                Next
                Throw New Exception(builder.ToString())
            Else
                Return CType(results.CompiledAssembly.CreateInstance("Script"), IScript)
            End If
        End Using
    End Function

    Public Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        For Each File As FileInfo In New System.IO.DirectoryInfo(Application.StartupPath & "/scripts").GetFiles
            If CheckedListBox1.GetItemCheckState(int2) = CheckState.Checked Then
                ListBox1.Items.Add(File.FullName)
            End If
            int2 = int2 + 1
        Next
        int2 = 0
        Dim script As IScript = GenerateScript(File.ReadAllText(ListBox1.Items.Item(int2).ToString()))
        script.DoWork(WebBrowser1)
    End Sub
End Class

脚本文件的内容

Imports System.Diagnostics

Public Class Script
    Implements IScript

    Public Sub DoWork(w As WebBrowser) Implements IScript.DoWork
        w.Navigate("http://www.google.com")
        wait("5000")
        wait("4000")
        w.Document.All("input").InvokeMember("click")
        w.Document.All("input").SetAttribute("value", "User")
        wait("2000")
        w.Document.All("421").InvokeMember("click")
    End Sub

    Public Sub wait(ByVal interval As Integer)
        Dim sw As New Stopwatch
        sw.Start()
        Do While sw.ElapsedMilliseconds < interval
            ' Allows UI to remain responsive
            Application.DoEvents()
        Loop
        sw.Stop()
    End Sub
End Class