0

So I've been looking around for a solution to this for a little over a week to no avail. I have a program that needs to be able to print htm(l) files and I'm having a terrible time getting it to comply.

This is the code I'm using at the moment:

Private Sub HtmlPrinterLaunch(i As Integer)
    'Dim htmlWBPrinter As New WebBrowser()
    'AddHandler htmlWBPrinter.DocumentCompleted, New WebBrowserDocumentCompletedEventHandler(AddressOf HtmlPrinter)
    'htmlWBPrinter.Visible = True
    'htmlWBPrinter.ScriptErrorsSuppressed = False
    'htmlWBPrinter.Show()
    ''frmHTMLPrint.wbPrintHtml.AllowNavigation = True
    ''AddHandler frmHTMLPrint.wbPrintHtml.DocumentCompleted, AddressOf HtmlPrinter
    ''frmHTMLPrint.wbPrintHtml.Visible = False

    ''frmHTMLPrint.wbPrintHtml.Navigate("file:///" & IO.Path.GetFullPath(_prints(i).SourcePathFileName))
    ''Application.Run(frmHTMLPrint)

    ''Dim appPath As String = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase)
    ''Dim reportPath As String = Path.Combine(appPath, Path.GetFileName(_prints(i).SourcePathFileName))

    ''htmlWBPrinter.Url = New Uri(reportPath) 'New Uri(Path.Combine("file:///", reportPath)) 'New Uri("file://" & IO.Path.GetFullPath(_prints(i).SourcePathFileName))
    'htmlWBPrinter.Url = (New Uri(Path.Combine("file:///" & IO.Path.GetFullPath(_prints(i).SourcePathFileName))))
    'While ((htmlWBPrinter.DocumentText = ""))
    '    Thread.Sleep(10000)
    'End While
    'htmlWBPrinter.ShowPrintDialog()
    '' htmlWBPrinter.Dispose()
    Dim wb As New WebBrowser
    AddHandler wb.DocumentCompleted, Sub() If wb.ReadyState = WebBrowserReadyState.Complete Then wb.Print()
    wb.ScriptErrorsSuppressed = True
    Dim url As New Uri(Path.Combine("file:///" & IO.Path.GetFullPath(_prints(i).SourcePathFileName)))
    wb.Navigate(url)
End Sub

Private Sub HtmlPrinter(sender As Object, e As WebBrowserDocumentCompletedEventArgs)
    state = 4
    Dim wbPrinter As WebBrowser = CType(sender, WebBrowser)
    wbPrinter.Print()
    wbPrinter.Dispose()
    'frmHTMLPrint.BeginInvoke(New Action(Sub() frmHTMLPrint.Close()))
End Sub

As you can see I have a couple attempts in there (kept some older code that sort of worked but I'd rather not us it as I kept getting pop ups)

Some likely related issues:

-the WebBrowser state stays in loading (1)

-the Url never updates with the file:///path even if I pass it directly

So to put it in brief, my in code WebBrowser control won't hit the DocumentCompleted event, nor will it print out files. I need this code to print documents with no input from the user. What am I missing here?

Edit:

So I've messed around with this some more. I have the webbrowser control on its own form and I can get it to load/print when called from the main thread, but I'm having no luck invoking it. My current code for invocation:

If wbPrintHtml.InvokeRequired Then
            If url.SourcePathFileName = "about:blank" Then
                wbPrintHtml.Invoke(CType(Sub()
                                             wbPrintHtml.Navigate(url.SourcePathFileName)
                                         End Sub, MethodInvoker))
            Else
                wbPrintHtml.Invoke(CType(Sub()
                                             wbPrintHtml.Navigate("file:///" & url.SourcePathFileName)
                                         End Sub, MethodInvoker))
            End If
        Else
            If url.SourcePathFileName = "about:blank" Then
                wbPrintHtml.Navigate(url.SourcePathFileName)
            Else
                wbPrintHtml.Navigate("file:///" & url.SourcePathFileName)
            End If
        End If
dcfyj
  • 121
  • 1
  • 10
  • It's not clear if you want to: 1) Print the Document *silently*, 2) Show a preview of the Html page then show the Print dialog 3) show just the Print Dialog without preview. Remove the `While ... End While` block. In the meanwhile, add to your Project the `WebBrowserAdvancedFetures` class you can see [here](https://stackoverflow.com/a/57615713/7444103) and call the `ActivateWBAdvancedFeatures` method on startup. – Jimi Feb 03 '20 at 18:58
  • @Jimi Sorry, I didn't think to add that to the question. I need these documents to print without any prompts from the user (it's an automatic report creation feature). The loop I currently have in there was just to test something, it doesn't actually sit there for any length of time since the documentText gets changed prior to the loop. – dcfyj Feb 03 '20 at 19:01
  • All right. Remove everything and try this: `Dim wb As New WebBrowser() AddHandler wb.DocumentCompleted, Sub() If wb.ReadyState = WebBrowserReadyState.Complete Then wb.Print() wb.Navigate([Some URI])`. The `DocumentCompleted` handler may need to be more complex than that, since you may need to handle multiple IFrame contents. See the notes [here](https://stackoverflow.com/a/53218064/7444103). – Jimi Feb 03 '20 at 19:05
  • @Jimi That seems to have the same issue. One thing I forgot to add to the question (will be doing momentarily) the wb.url doesn't update with the file:///path even if I pass it directly into it and the readystate of the page seems to stay at Loading(1) – dcfyj Feb 03 '20 at 19:15
  • I don't think the events will fire unless the WebBrowser control is added to a visible container. – LarsTech Feb 03 '20 at 19:30
  • @LarsTech If that's the case then I've got major issues lol. I don't want a visible pop up for it since users could close it or if I make it a brief pop up they'll wondering what it is/find it annoying – dcfyj Feb 03 '20 at 19:32
  • You can test it. You could also just park the control with dimensions that would effectively put it off screen. – LarsTech Feb 03 '20 at 19:34
  • Yes, it work loading the Html from file. Use an Uri: `wb.ScriptErrorsSuppressed = True Dim url As New Uri(Path.Combine([Path], "Page.html")) wb.Navigate(url)`. – Jimi Feb 03 '20 at 19:54
  • @LarsTech This is not used as a control. The visible instance of the ActiveX is not created, just the `mshtml` wrapper is used here. The content is parsed and rendered, it doesn't need to be shown for the Print function to work. – Jimi Feb 03 '20 at 20:02
  • @Jimi I'm still getting nothing showing up. Even if I switch to my existing handler it never hits the breakpoint. – dcfyj Feb 03 '20 at 20:03
  • Have you used the code as shown here and nothing else? Have you checked that `URI` you're passing is correct? Have you added the class I've linked before and called it's `ActivateWBAdvancedFeatures` method on startup? You have to pass to that method the path of your executable. Post the code you're using now. – Jimi Feb 03 '20 at 20:08
  • WebBrowser raises its DocumentCompleted event on the main thread. That can't happen in this code, the main thread is stuck in the While loop. You just don't need the loop at all, move the ShowPrintDialog() call to the event handler. – Hans Passant Feb 03 '20 at 20:09
  • `Dim url As New Uri(Path.Combine("c:\SomePath", "SomeFile.html"))` – Jimi Feb 03 '20 at 20:14
  • @Jimi I've updated the code. Hans Neither of those functions are the main thread. The function that calls HtmlPrinterLaunch puts it in its own thread and loops while checking the state of the browser. It's doing this because I need to update a database and I need to know if the item printed before it gets back to that function. – dcfyj Feb 03 '20 at 20:15
  • @Jimi _prints(i).SourcePathFileName is the full path. I'm adding file:/// to it because the way I understand it it needs to be there to process the file path as a Uri – dcfyj Feb 03 '20 at 20:16
  • What? Don't you think you should have mentioned this before! The WebBrowser handles the loading asynchronously and operates through events. You don't need a thread. There are already a number of threads that the WB spawns for this. If you do, it's all another matter. --- NO, you don't need to add anything, it's the Uri class that does that for you, that's why I suggested to use it. – Jimi Feb 03 '20 at 20:16
  • @Jimi It's a fairly convoluted piece of inherited code. Originally it was written as a separate application but it was decided that it be placed within the parent application. So this code (along with Email code and FTP code) are placed on a timer. The timer calls a task that runs all 3. The htmlprinter is the only one on a thread in that task. Like I said convoluted. – dcfyj Feb 03 '20 at 20:20
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/207126/discussion-between-dcfyj-and-jimi). – dcfyj Feb 03 '20 at 20:23
  • Well, you can ask @Hans Passant if you can borrow the code he wrote, to run a WebBrowser in a non-UI thread (if you think you need that). The rest doesn't change. – Jimi Feb 03 '20 at 20:24

1 Answers1

0

I finally got this to work as intended. It's not the prettiest thing in the world, but it functions, and I'm ok with that.

Public Class frmHTMLPrint

Public Shared formHandle As frmHTMLPrint

Private Sub wbPrintHtml_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles wbPrintHtml.DocumentCompleted
    Dim wbPrinter As WebBrowser = CType(sender, WebBrowser)
    If wbPrinter.ReadyState = WebBrowserReadyState.Complete AndAlso Not wbPrinter.Url.ToString() = "about:blank" Then
        wbPrinter.Print()
    End If
End Sub

Shared Function setURL(url As Reporter.ReportServer.PrintMessageType) As Boolean
    If formHandle.wbPrintHtml.InvokeRequired Then
        If url.SourcePathFileName = "about:blank" Then
            formHandle.wbPrintHtml.Invoke(CType(Sub()
                                                    formHandle.wbPrintHtml.Navigate(url.SourcePathFileName)
                                                End Sub, MethodInvoker))
        Else
            formHandle.wbPrintHtml.Invoke(CType(Sub()
                                                    formHandle.wbPrintHtml.Navigate("file:///" & url.SourcePathFileName)
                                                End Sub, MethodInvoker))
        End If
        Dim wbReady As WebBrowserReadyState
        formHandle.wbPrintHtml.Invoke(CType(Sub()
                                                wbReady = formHandle.wbPrintHtml.ReadyState
                                            End Sub, MethodInvoker))
        While ((Not wbReady = WebBrowserReadyState.Complete))
            Application.DoEvents()
            formHandle.wbPrintHtml.Invoke(CType(Sub()
                                                    wbReady = formHandle.wbPrintHtml.ReadyState
                                                End Sub, MethodInvoker))
        End While
        Return wbReady = 4
    Else
        If url.SourcePathFileName = "about:blank" Then
            formHandle.wbPrintHtml.Navigate(url.SourcePathFileName)
        Else
            formHandle.wbPrintHtml.Navigate("file:///" & url.SourcePathFileName)
        End If
        While ((Not formHandle.wbPrintHtml.ReadyState = WebBrowserReadyState.Complete))
            Application.DoEvents()
        End While
        Return formHandle.wbPrintHtml.ReadyState = 4
    End If
End Function

Private Sub frmHTMLPrint_Load() Handles Me.Load
    InitializeComponent()
    Dim wbInitializer As New Reporter.ReportServer.PrintMessageType
    formHandle = Me
    wbInitializer.SourcePathFileName = "about:blank"
    setURL(wbInitializer)
End Sub

End Class

dcfyj
  • 121
  • 1
  • 10