Assigning an existing process to an object variable? - excel

I need to assign another application as an object and control it.
Typically, I would open a new instance of the application using CreateObject(), but in this instance I need an existing instance of the object that was already opened in Windows Explorer. The object is an Internet Explorer session that, for various reasons, I can't simply navigate to using web browser controls in Office.
Is there a way to grab this application session by its window handle or something so that I can control it using the APIs that VBA offer? It's also essential that the application stays on the page that it was on and that its entire DOM structure remains intact.

Try this in a sub, it works for me
Set objShell = CreateObject("Shell.Application")
IE_count = objShell.Windows.Count
For x = 0 To (IE_count - 1)
On Error Resume Next ' sometimes more web pages are counted than are open
my_url = objShell.Windows(x).Document.Location
my_title = objShell.Windows(x).Document.Title
If my_title Like "The Title You're Looking For" & "*" Then
Set ie = objShell.Windows(x)
Exit For
Else
End If
Next
Now do stuff with this instance of ie

Related

Force a COM server to remain open

I have a COM automation server hosted by a VB6 exe.
The COM API is used from Excel VBA:
Dim o as MyCOMAPI.MyCOMType
Set o = new MyCOMAPI.MyCOMType
o.DoSomething
When I create objects in VBA the exe is started along with COM automation and VBA can use the API.
But the exe is closed quickly and "randomly" by Excel, I guess when Excel decides it doesn't need the COM API anymore.
This behaviour is causing random errors.
The simple solution is to start the exe before running the VBA code ; in this case all is working fine as the exe won't stop running until closed by the user.
Have you some information/documentation about the way Excel VBA manages calls to hosted APIs?
Is there a way to avoid this behaviour and have the exe kept open until the VBA code decides to stop it?
This would be the default behavior for a COM automation server when the last object is dereferenced, meaning that the variable that points to the server is set to nothing.
Now, if your code looks something like this today:
Sub MyFunction()
...
Dim o as MyCOMAPI.MyCOMType
Set o = new MyCOMAPI.MyCOMType
o.DoSomething
End Sub
Then, the server life is connected to the life of the o variable. That variable gets set to nothing when the function is finished, and then the server will be shut down (unless there are other variables keeping it alive).
To make sure that your COM server is kept alive for a longer time, simply define the variable as a Public variable as in the sample below.
This sample will start and show Excel and keep it open until the ShutdownExcel function is called.
Public o As Excel.Application
Sub MakeSureExcelIsRunning()
If o Is Nothing Then
Set o = New Excel.Application
o.Visible = True
End If
End Sub
Sub ShutdownExcel()
Set o = Nothing
End Sub
From COM docs.
**Component Automation**
Shutting Down Objects
ActiveX objects must shut down in the following way:
If the object's application is visible, the object should shut down only in response to an explicit user command (for example, clicking Exit on the File menu) or the equivalent command from an ActiveX client.
If the object's application is not visible, the object should shut down only when the last external reference is gone.
If the object's application is visible and is controlled by an ActiveX client, it should become invisible when the user shuts it down (for example, clicking Exit on the File menu). This behavior allows the controller to continue to control the object. The controller should shut down only when the last external reference to the object has disappeared.
© Microsoft Corporation. All rights reserved.
When you write an COM server exe the first thing you do it take a reference to yourself when starting as a normal exe else the exe shuts down as soon as initialisation is over.

CreateObject randomly throws "A system shutdown has already been scheduled" error

I googled and SO'd, and nothing.
My job revolves around making my co-workers lives easier.
Currently, they are using very clunky spreadsheets designed 10+ years ago.
In the process of migrating their tools and reports to the local intranet using PHP, i have configured a spreadsheet that downloads that persons permissions based on their Application.Username
Then a little back and forth with the server to generate a session key, and then pop internet explorer opens up with the relevant tool they selected from a dropdown within the workbook - meaning their session and tools are then purely browser based.
All works great, however randomly, sometimes, when the sub to open the internet browser is triggered a very bizarre error message appears :-
Upon clicking Debug, the following function is shown, and you can see for yourself which line is highlighted in yellow.
I can confirm i do not have any tasks at all within my taskschedule. When i end this, and run it again, chances are it runs just fine.. it is just sometimes that this error pops up.
Please help! Thank in advance.
With errors this seemingly-unrelated and intermittent, I usually opt for either a bit of delay, catching the error and retrying or both.
Try the following (retry without a delay):
Function gogogo(sessKey)
On Error GoTo ErrHandler
reportId = Sheet2.Range("A" & (Sheet2.Range("B1").Value + 1)).Value
Set objIE = CreateObject("InternetExplorer.Application")
URL = "http://localinternetdomainhere/OnlineTools/" & reportId & "/access/" & sessKey
With objIE
.Visible = True
.navigate URL
End With
ThisWorkbook.Saved = True
ThisWorkbook.Close False
Exit Function
ErrHandler:
If Err.Number = &H800704A6 Then 'Put a breakpoint here to make sure this is the ACTUAL VBA error number and not the ActiveX one. You might need to check against the Err.LastDllError property
Resume
End If
Err.Raise Err.Number, Err.Source, Err.Description,err.HelpFile, err.HelpContext 'Reraise the error otherwise
End Function

Is it possible to embed the username/password into and ASP file to access files when anonymous access is disabled?

I'm redesigned a login system where I work and currently if someone know the path to a file they may be able to access it even if they are not logged in at all. So far I've researched and come up with 2 ways to prevent this.
Disable anonymous access and have the file brought in by the webpage. This is what I would prefer to do for now since it wouldn't require moving the files around and it seems to be relatively easy to implement. The problem with this is that if I disable anonymous access the file trying to access the document they requires the username/password. Is it possible to have to username/password as part of the ASP file to eliminate this?
The other option is to move the files outside of the website and elsewhere on the server and have the webpage bring in the file similar the first option. I want to eventually get to this method, currently it would take much more time to do this though since we would have to move the files for all of our users as well as change the programs that generate the different reports to output to their new locations.
You could do this with a rewrite rule in IIS.
http://www.iis.net/downloads/microsoft/url-rewrite
You can also do this with a Request Filtering rule in IIS.
http://www.iis.net/configreference/system.webserver/security/requestfiltering/filteringrules/filteringrule/appliesto
"Is it possible to have to username/password as part of the ASP file to eliminate this?"
Yes, if you only want to restrict access to .asp files Session variables tend to be overused in Classic ASP but this is one situation where it is completely appropriate to use one. At the start of all your restricted pages you could have something like
<% If Session("loggedin") <> 1 then Response.Redirect("login.asp") End If %>
Then you need to find a classic asp login script which sets a session variable. Google or Bing should come up with plenty, but here are a couple of links for you
https://web.archive.org/web/20211020121723/https://www.4guysfromrolla.com/webtech/050499-1.shtml
https://web.archive.org/web/20210323190338/http://www.4guysfromrolla.com/webtech/021600-1.shtml
Edit - http server request code. I haven't tested this
Set xml = Server.CreateObject("Msxml2.ServerXMLHTTP.6.0")
xml.open "GET","http://fullurlto/yourpdffile.pdf", false, "yourusername", "yourpassword"
xml.send
Response.ContentType = "application/pdf"
Response.write xml.responseText
I found a basis for the code elsewhere on this site but it didn't quite work the way I needed it to.
How To Raise a "File Download" in ASP and prevent hotlink
Dim curUser, curDir, curtype
curUser = Request.QueryString("user")
curDir = Request.QueryString("dir")
curType = Request.QueryString("type")
If curUser = Session("homefolder") Then
Set adoStream = CreateObject("ADODB.Stream")
adoStream.Type = 1
adoStream.Open()
adoStream.LoadFromFile "C:/path/to/" & curUser & curDir
Response.Buffer = true
Response.CharSet = "UTF-8"
Response.Clear
If curType = ".TXT" Then
Response.ContentType = "text/plain"
Else
Response.ContentType = "application/pdf"
End If
Response.flush
Do While Not adoStream.eos
Response.BinaryWrite adoStream.Read(1024 * 8)
Response.flush
Loop
Response.End()
adoStream.close
Set adoStream=nothing
Else
Response.Redirect "denied.asp"
End If
The file is brought in and displayed within the browser. If the user tries to see another user's files they are simply redirected. I'm currently only dealing with PDF and TXT file but it will be easy to add new files types if needed.

VBA Excel WebBrowser how to reset ReadyState before .Click from the code?

I have similar code in Excel VBA with WebBrowser control inserted in a form:
basePath = "http://www.amazon.co.uk/"
' Open main website (Home page)
WebBrowser.Navigate basePath
PauseTime = 30 ' Set duration to wait in seconds.
Start = Timer ' Set start time.
Do ' While Timer < Start + PauseTime
DoEvents ' allow other processes to work (the browser to have time to load the webpage)
Loop Until WebBrowser.ReadyState = READYSTATE_COMPLETE Or Timer > Start + PauseTime
This way I'm opening the specified URL.
Then I put bar-code value in the search field in the HTML and invoke .Click method on the "submit" button (HTML input element) for the same form.
I successfully open the search results page with what I'm searching for, but the browser control is already in WebBrowser.ReadyState = READYSTATE_COMPLETE status and I cannot track when actually the browser control finished the second load - the search page in this case.
Is there a way to RESET the ReadyState property in order to know when the second load has finished?
Because DocumentComplete it not reliable (thank you Alex for the suggestion!) - sometimes it fires before the page is fully loaded resulting in missing part I need for my job, I ended using this:
Set MyObj = Nothing
Do
DoEvents
Set MyObj = WebBrowser.Document.all("myobjID")
Loop Until Not MyObj is Nothing
But it's temporary solution - i'm not sure i'll always have this myobjID in the DOM!
Use the DocumentComplete event which is raised when a document is fully loaded so should fire for every call to .Navigate.
(This includes frames so if any are present you will need to filter them using the URL argument the event receives)

Classic ASP/IIS6: How to search the server’s mime map?

This is the same question as this but I'm looking for a classic ASP solution.
I have a third party control to provide secure downloads but it expects you to provide the response.contenttype value. I'm trying to have the browser prompt with the following:
Response.AddHeader "Content-Disposition", "attachment;filename=""" & strFileName & """"
However Safari doesn't like any of the suggested content types (does odd things with the file name - like add ".exe" to the end).
application/x-msdownload
application/force-download
So I'd either like to query IIS for the correct content type or find a generic content type that would let the browser figure it out in a somewhat reliable fashion.
Typically the mimemap being used by the site is stored at server level and you can get into permission issues trying to read it. It requires some nasty ADSI code.
Have you tried the standard application/octet-stream as a mime type?
From Reading the server mimemap:
Public Function GetMimeType(ByVal Extension)
Dim oMimeMap
Dim vntMimeType
Dim avntMap()
Set oMimeMap = GetObject("IIS://LocalHost/MimeMap")
If Left(Extension, 1) <> "." Then Extension = "." & Extension
avntMap() = oMimeMap.MimeMap
For Each vntMimeType In avntMap
If vntMimeType.Extension = Extension Then
GetMimeType = vntMimeType.MimeType
Exit For
End If
Next
If GetMimeType = "" Then GetMimeType = "application/octet-stream"
End Function
Note: the code calling GetObject is required to be an Operator in WWW Service Master properties.

Resources