msoFileDialogFilePicker not displaying - vba

I'm having issues with trying to display the file picker using the FileDialog property in MS Access 2016. I've tried both early and late binding but the file picker never displays. No errors are detected nor messages displayed. When I try to debug it line by line, the .show doesn't trigger the form. I've also tried If .show=-1 in lieu of .show but that does not work either. I've tried removing the library reference to Microsoft Office 16.0 Object Library, and even using late binding the window still does not display. Any ideas as to what is going wrong, and how to remedy it? I'm adding both early and late binding examples below.
Public Sub FP_EarlyBinding()
Dim fd As Office.FileDialog
Set fd = Application.FileDialog(msoFileDialogFilePicker)
With fd
.AllowMultiSelect = True
.Show
For Each vrtSelectedItem In .SelectedItems
Debug.Print vrtSelectedItem
Next vrtSelectedItem
End With
End Sub
Public Sub FP_LateBinding()
Const msoFileDialogFilePicker As Long = 3
Dim fd As Object
Set fd = Application.FileDialog(msoFileDialogFilePicker)
With fd
.AllowMultiSelect = True
.Show
For Each vrtSelectedItem In .SelectedItems
Debug.Print vrtSelectedItem
Next vrtSelectedItem
End With
End Sub
--------------EDIT-------------------------
Per comments, I was instructed to not to do this in a class module. I've since edited the code, as below. This still does not allow for the form to appear.
This is in a standard module:
Public Function filePicker() As Variant
Dim fd As FileDialog
Dim sFiles$
Dim vrtSelectedItem As Variant
Set fd = Application.FileDialog(msoFileDialogFilePicker)
With fd
.AllowMultiSelect = True
.Show
For Each vrtSelectedItem In .SelectedItems
sFiles = sFiles & vrtSelectedItem & ","
Next vrtSelectedItem '-----------loops again to attach other files
End With
filePicker = sFiles
End Function
I call this procedure from a class module:
Public Sub Test()
Dim vFileList As Variant, vFile As Variant
vFileList = Split(filePicker(), ",")
For Each vFile In vFileList
Attachments.Add vFile
Next vFile
End Sub
-------------FINAL EDIT---------------------
Turns out the problem here was the Access install...I went to the installation directory and found MSACCESS.EXE, right-clicked, and repaired.

What you have looks almost good.
I recommend you ALWAYS, but ALWAYS ALWAYS ALWAYS put option explicit t the start of your code modules.
eg this:
Option Compare Database
Option Explicit
With above, your code does not compile. I mean, after you type in your code, I assume you do from the code menu a debug->compile. I will do that 100's of time in a typical day (after editing code). so, make that a habit.
Also, to set the default for NEW code modules (setting does not effect existing), set this option in the VBA code editor: tools->Options
So, your code snip looks ok, but, with above option explicit, then you have to declare all variables (and bonus is compile will cast incorrect spellings or miss typed variable names).
So, your code like this should work fine:
Public Sub FP_LateBinding()
Const msoFileDialogFilePicker As Long = 3
Dim fd As Object
Set fd = Application.FileDialog(msoFileDialogFilePicker)
With fd
.AllowMultiSelect = True
.Show
Dim vrtSelectedItem As Variant
For Each vrtSelectedItem In .SelectedItems
Debug.Print vrtSelectedItem
Next vrtSelectedItem
End With
End Sub
Now, it also not clear how you are test/running the above. But, we assume you create a new code module (NOT class module)
You will then type/paste in above. Save it
do a debug->compile from the VBA editor menu - does it compile ok?
(and if other errors in other places - fix those first).
Now, to run it, place cursor anywhere inside of that code stub, hit f5.
Or you can type in the name of the above sub in the debug window like this:
FP_LateBinding
So your posted code - looks really nice! - all good. Try the above option explcit. And of course now in your example, add the declare for vrtSelectedItem
Edit: ============================================================
Now, of course if this is a class module, then just like code in a form/report, you can't just hit f5 in the code module, nor can you JUST type in the name of the sub.
In fact, if you place your cursor in the code and hit F5, then you get this:
So, it not that the code or file does not appear, you get the above - a big WHOPPER of a difference issue.
And if it is a class module as opposed to a regular code module?
Then to test or use the code, you have to write in test code into a REGULAR code module. You can't use the debug/VBA editor to JUST run a class.
This not different then creating any other object.
So, if we create a new class module, paste in above code, say we save the class code module as MyClassTest ?
Then you have to write code in another standard code module like this:
Sub Test1()
Dim clsTest As New MyClassTest
clsTest.FP_LateBinding
End Sub
So, you can't run class code (even code in a forms code behind module), or any custom class module, you have to FIRST create a instance.
Now it is "possbile" that you used VBA, VB5, VB6. And in fact used "early" versions of Access (before access 2000 - 21 years ago).
you will find by default, it is possbile to use + call class module code without first creating a instance of the class module. This ability was and is for keeping compatibility for pre access 2000 VBA, in which class code modules did not require to be delcared as a new instance. This so called "base" class instance thus was possbile.
it turns out, that if you been importing code for the last 20 years from previous verisons of Access? This flag setting - and option STILL exists in Access 2019 - over 20 years later!!!!
I doubt that you are/did import code from such older versions of Access, but it is in fact still possible to set a class module to NOT require a instance of the class to be created in code. However, I don't recommend doing this, despite the fact that this is still possible.

Related

How to reference a global value without getting "object variable or with block not set" error?

I have a variable listvalues that is set as an arraylist. This variable is being populated in form1 with a list of file paths but can also be left blank. Then after clicking a button1, form1 gets closed and report1 opens. In this report i have button2 which is basically being used to export the report but also clears the listvalues and uses the file paths to append pdfs to the end of the report. However, i cannot seem to get passed the above error. I tried creating another global variable as an object called attachedfiles and do Set attachedfiles = listvalues in form1 so that i can instead refer to attachedfiles and clear it but it doesnt work. I currently have a module made called module2 with the global variables declared inside it as
Option Compare Database
Global listvalues As ArrayList
Global attachedfiles As Object
Basically i just need a way to transfer and make usable the arraylist of files selected in form1 but in another form called form2, id appreciate some direction on this!
Edit: here is the code for button1
Private Sub filebutton_Click()
Dim fDialog As filedialog
Dim selectedfile As Variant
'array create list, !!!!!!need to turn on .net framework 3.5 in system features
Set listvalues = New ArrayList
Set fDialog = Application.filedialog(msoFileDialogFilePicker)
With fDialog
'Existing filters must be cleared before adding new:
.Filters.Clear
.Filters.Add "PDFs", "*.pdf"
If fDialog.Show = -1 Then
For Each selectedfile In .SelectedItems
listvalues.Add selectedfile
Next
End If
End With
filedialog.ColumnCount = 1
Me.filedialog.RowSource = ""
For Each i In listvalues
Me.filedialog.AddItem (i)
Next
Set attachedfiles = listvalues
End Sub
And for button2 which generates the pdf appended with the attached file paths in attachedfiles
Private Sub pdfbutton_Click()
Dim oPDF As PdfCreatorObj
Set oPDF = New PdfCreatorObj
MsgBox (attachedfiles.Count)
If attachedfiles.Count > 0 Then
MsgBox ("attachedfiles")
Dim attacheditemcount As String
attacheditemcount = listvalues.Count
Idk if I have to reference the global variables in somewhere else instead of just a random module that doesnt get referenced or if i have to initialize the global variables before using them?
No need to use an ArrayList, the built-in Collection object should do just fine. To have global access, wrap it in a property in a Standard module.
Private m_collection As Collection
Public Property Get TheCollection() As Collection
If m_collection Is Nothing Then Set m_collection = New Collection
Set TheCollection = m_collection
End Property
To call it:
TheCollection.Count
Don't forget to clear it as the values will be retained.
You could also create a single instance class module, but it's a bit more advanced in terms that you need to export the module, change some of its attributes and import again.
There are a few examples on how to do that here in SO.

How to Edit a Read-Only Word Document (VBA)

I am periodically getting Word documents from various clients and sometimes they send them to me in 'Read-Only' mode. While it isn't a big deal to go to 'View > Edit Document' manually, I cannot seem to find how to do this within my VBA code.
Either opening a document as editable or toggling it as editable once it is open would be sufficient for my needs.
Note that I cannot open the document with 'readOnly = false' as it looks like it is set to 'readOnly recommended' (based on my reading of the MS man page on Document.Open).
IN CONTEXT:
I was also hitting a problem with turning off 'read-mode' which the documents were opening as by default. I have posted this question and answer here.
The code below will change the ReadOnly attribute of a closed file, setting its ReadOnly attribute to either True or False depending on the argument supplied to the procedure.
Private Sub SetReadOnlyProperty(Fn As String, _
ByVal ReadOnly As Boolean)
' 21 Nov 2017
Dim Fso As Object
Dim Doc As Object
Set Fso = CreateObject("Scripting.FileSystemObject")
Set Doc = Fso.GetFile(Fn)
If (Doc.Attributes And vbReadOnly) <> Abs(Int(ReadOnly)) Then
Doc.Attributes = Doc.Attributes Xor vbReadOnly
End If
End Sub
This procedure requires access to the MS Scripting Runtime DLL. Enable this access by checking the box against Miscrosoft Scripting Runtime from Tools >References in the VBE window. Below is an example of how to call the function. Note that an error will result if the supplied file doesn't exist.
Private Sub TestReadOnly()
SetReadOnlyProperty "H:\Test Folder\Test File.docx", False
End Sub

How to Link a VBA code with opened ETABS OAPI?

a project on ETABS 2015 is open on my PC. I want to do some change and process on it by a VBA code automatically. but all the sample code that I found was like this:
Public Sub Example()
Dim SapModel As cSapModel
Dim EtabsObject As cOAPI
Dim FileName as String
Dim ret As Integer = -1
'create ETABS object
EtabsObject = CreateObject("CSI.ETABS.API.ETABSObject")
'start ETABS application
ret = EtabsObject.ApplicationStart()
'create SapModel object
SapModel = EtabsObject.SapModel
'initialize model
ret = SapModel.InitializeNewModel()
'open an existing file - If no file exists, run the Save example first.
FileName = "c:\CSI_API_temp\example.edb"
ret = SapModel.File.OpenFile(FileName)
This code just open a new ETABS in my PC. but my ETABS project is already running and I want to connect to it but I don't know how!
please help me.
Your sample code is basically and typically the one needed to open ETABS ITSELF and to initialize a new model,
ETABSObject = CreateObject("CSI.ETABS.API.ETABSObject") followed by the invocation of ApplicationStart method are the direct means for that.
If you refer to the ETABS API documentation file in the installation destination (e.g "C:\Program Files\Computers and Structures\ETABS 2015\CSi API ETABS 2015.chm" ) Under the section "Release Notes>Attaching to a manually started instance of ETABS", or if you see the same section from the link
http://docs.csiamerica.com/help-files/etabs-api-2015/html/3ceb8889-9028-4de3-9c87-69a12055ade7.htm
you'll find code (in VB.Net) close to what you aim to, but it will not work with vba, so, what you will simply do is this
'The ret variable, just for method invocation convenience
Dim ret As Integer
'Create an instance of the currently opened Program
Dim myETABSObject As ETABS2015.cOAPI
Set myETABSObject = GetObject(, "CSI.ETABS.API.ETABSObject")
'Create an instance to the model
Dim myETABSModel As ETABS2015.cSapModel
Set myETABSModel = myETABSObject.SapModel
'Now, after the "reference instances" are obtained
'Initialize the model (using the model's instance of course)
ret = myETABSModel.InitializeNewModel() 'default units in kip_in_F
'Set the model templete, which is "Blank"
ret = myETABSModel.File.NewBlank()
And You can normally proceed in your coding.
Note: 1- GetObject(CSI.ETABS.API.ETABSObject) method will raise an error if ETABS is not opened.
2- you still can use GetObject even if ETABS is not opened and if you want to open it from windows shell (like using command prompt to open programs instead of double clicking on their respective icons), but this of course needs some Error Handling and the use of "Windows Script Host Object Model".
3- The examples in the documentation (under the documented methods and properties) may not be complete in terms of initiating input data, in cases like this they are just to create an impression of how to use their respective methods and properties.
The code below does as you asked. It uses VBA to connect to a currently running instance of ETABS. Please note if you're running multiple instances of ETABS it may not attach to the one you want.
You'll need to add a reference to 'ETABSv1.tlb' to your VBA project. If you're using VBA Editor for Excel, on the Tools menu select References, then click Browse then add C:\Program Files\Computers and Structures\ETABS 19\ETABSv1.tlb
Sub Main()
'create API helper object
Dim myHelper As ETABSv1.cHelper
Set myHelper = New ETABSv1.Helper
'dimension the ETABS Object as cOAPI type
Dim myETABSObject As ETABSv1.cOAPI
Set myETABSObject = Nothing
'attach to a running instance of ETABS
'get the active ETABS object
Set myETABSObject = myHelper.GetObject("CSI.ETABS.API.ETABSObject")
'get a reference to cSapModel to access all API classes and functions
Dim mySapModel As ETABSv1.cSapModel
Set mySapModel = myETABSObject.SapModel
' DO YOUR WORK BELOW.
MsgBox "Do your work here."
' DO YOUR WORK ABOVE.
'clean up variables
Set mySapModel = Nothing
Set myETABSObject = Nothing
Exit Sub
ErrHandler:
MsgBox "Cannot run API script: " & Err.Description
End Sub
ETABS comes with API documentation that you can find in the installation folder. On my machine it is here: C:\Program Files\Computers and Structures\ETABS 19\CSA API ETABS v1.chm

Download URL Contents Directly into String (VB6) WITHOUT Saving to Disk

Basically, I want to download the contents of a particular URL (basically, just HTML codes in the form of a String) into my VB6 String variable. However, there are some conditions.
I know about the URLDownloadToFile Function - however, this requires that you save the downloaded file/HTML onto a file location on disk before you can read it into a String variable, this is not an option for me and I do not want to do this.
The other thing is, if I need to use an external library, it must already come with all versions of Windows from XP and onwards, I cannot use a control or library that I am required to ship, package and distribute even if it is free, this is not an option and I do not want to do this. So, I cannot use the MSINET.OCX (Internet Transfer) Control's .OpenURL() function (which simply returns contents into a String), as it does not come with Windows.
Is there a way to be able to do this with the Windows API, URLMON or something else that is pre-loaded into or comes with Windows, or a way to do it in VB6 (SP6) entirely?
If so, I would appreciate direction, because even after one hour of googling, the only examples I've found are references to URLDownloadToFile (which requires saving on disk before being ale to place into a String) and MsInet.OpenURL (which requires that I ship and distribute MSINET.OCX, which I cannot and don't want to do).
Surely there has got to be an elegant way to be able to do this? I can do it in VB.NET without an issue, but obviously don't have the luxury of the .NET framework in VB6 - any ideas?
Update:
I have found this: http://www.freevbcode.com/ShowCode.asp?ID=1252
however it says that the displayed function may not return the entire
page and links to a Microsoft bug report or kb article explaining
this. Also, I understand this is based off wininet.dll - and I'm
wondering which versions of Windows does WinInet.dll come packaged
with? Windows XP & beyond? Does it come with Windows 7 and/or Windows
8?
This is how I did it with VB6 a few years ago:
Private Function GetHTMLSource(ByVal sURL As String) As String
Dim xmlHttp As Object
Set xmlHttp = CreateObject("MSXML2.XmlHttp")
xmlHttp.Open "GET", sURL, False
xmlHttp.send
GetHTMLSource = xmlHttp.responseText
Set xmlHttp = Nothing
End Function
If you want to do this with pure VB, and no IE, then you can take advantage of a little-used features of the VB UserControl - async properties.
Create a new UserControl, and call it something like UrlDownloader. Set the InvisibleAtRuntime property to True. Add the following code to it:
Option Explicit
Private Const m_ksProp_Data As String = "Data"
Private m_bAsync As Boolean
Private m_sURL As String
Public Event AsyncReadProgress(ByRef the_abytData() As Byte)
Public Event AsyncReadComplete(ByRef the_abytData() As Byte)
Public Property Let Async(ByVal the_bValue As Boolean)
m_bAsync = the_bValue
End Property
Public Property Get Async() As Boolean
Async = m_bAsync
End Property
Public Property Let URL(ByVal the_sValue As String)
m_sURL = the_sValue
End Property
Public Property Get URL() As String
URL = m_sURL
End Property
Public Sub Download()
UserControl.AsyncRead m_sURL, vbAsyncTypeByteArray, m_ksProp_Data, IIf(m_bAsync, 0&, vbAsyncReadSynchronousDownload)
End Sub
Private Sub UserControl_AsyncReadComplete(AsyncProp As AsyncProperty)
If AsyncProp.PropertyName = m_ksProp_Data Then
RaiseEvent AsyncReadComplete(AsyncProp.Value)
End If
End Sub
Private Sub UserControl_AsyncReadProgress(AsyncProp As AsyncProperty)
If AsyncProp.PropertyName = m_ksProp_Data Then
Select Case AsyncProp.StatusCode
Case vbAsyncStatusCodeBeginDownloadData, vbAsyncStatusCodeDownloadingData, vbAsyncStatusCodeEndDownloadData
RaiseEvent AsyncReadProgress(AsyncProp.Value)
End Select
End If
End Sub
To use this control, stick it on a form and use the following code:
Option Explicit
Private Sub Command1_Click()
XDownload1.Async = False
XDownload1.URL = "http://www.google.co.uk"
XDownload1.Download
End Sub
Private Sub XDownload1_AsyncReadProgress(the_abytData() As Byte)
Debug.Print StrConv(the_abytData(), vbUnicode)
End Sub
Suffice to say, you can customise this to your hearts content. It can tell (using the AyncProp object) whether the file is cached, and other useful information. It even has a special mode in which you can download GIF, JPG and BMP files and return them as a StdPicture object!
One alternative is using Internet Explorer.
Dim ex As InternetExplorer
Dim hd As HTMLDocument
Dim s As String
Set ex = New InternetExplorer
With ex
.Navigate "http://donttrack.us/"
.Visible = 1
Set hd = .Document
s = hd.body.innerText ' assuming you just want the text
's = hd.body.innerHTML ' if you want the HTML
End With
EDIT: For the above early binding to work you need to set references to "Microsoft Internet Controls" and "Microsoft HTML Object Library" (Tools > References). You could also use late binding, but to be honest, I forget what the proper class names are; maybe someone smart will edit this answer :-)

Close modal form in Access and access module code

I've inherited a Access database that is used to import some data in SQL. The MDB opens with a form, in a modal mode: no Access menubar or buttons are visible. I can view the tables with content by using the Visual Studio 'Data Connection' tool, but I cannot see the module's code.
I've looked at this question here, but the answers there aren't really what I need. Is there a way to either force the form to close (and access the modules) or to extract the VBA code ?
[EDIT] I am using Access 2007, not sure what the original developer used.
Hold down the shift key when you open the database. This will prevent it from loading the automatic script it's running and allow you to gain access to the tables, queries, and VBA scripts.
This is a rather long addendum to, or comment on, Michael Todd's reply in response to edosoft's comment.
Rather than choosing to enable the content, check the start-up options (either (file->options->current database->display form) or (tools->start up->display form)) and remove the name of the form, having taken a note, and ensure that Allow Full Menus (same page) is ticked. You may also like to press Alt+F11 to display code, and check that for start-up code, finally, see if there is an AutoRun macro and rename it.
EDIT re Comments
You do not have to open an mdb to change the start-up form, for example, code such as this could be run from another mdb using the full name and path of the mdb you wish to change.
Sub SetStartForm(DBFile As String)
Dim prp As Object
Dim db As Database
Const PROPERTY_NOT_FOUND As Integer = 3270
Set db = OpenDatabase(DBFile)
db.Properties("StartupForm") = "(none)"
If Err.Number > 0 Then
If Err.Number = PROPERTY_NOT_FOUND Then
'' Create the new property, but this is not relevant in this case
End If
End If
db.Close
Set db = Nothing
Set prp = Nothing
End Sub
Button close with function:
Private sub cmdClose_Click()
CloseForm(YourFormName)
End sub
Public Sub CloseForm(ByVal strFormName As String)
Dim iCounter As Integer
For Each frm In Forms
With frm
If (.Name = strFormName) Then
iCounter = iCounter + 1
End If
End With
Next
If (iCounter > 0) Then
For i = 1 To iCounter
DoCmd.Close acForm, strFormName, acSaveNo
Next
End If
End Sub