I am trying to create a image slideshow by changing the Image control .picture property by looping through all images in a predefined folder
C:\Images
The code I am using:
Public pixpaths As Collection
Public pix_path As String
Public pixnum As Integer
Public fs As YtoFileSearch
Public k As Integer
Public Sub Image_set()
Set pixpaths = New Collection
pix_path = "C:\Images"
Set fs = New YtoFileSearch
With fs
.NewSearch
.LookIn = pix_path
.fileName = "*.jpg"
If fs.Execute() > 0 Then
For k = 1 To .FoundFiles.Count
pixpaths.Add Item:=.FoundFiles(k)
Next k
Else
MsgBox "No files found!"
DoCmd.OpenForm "Fr_Sketchpad" ' If no images found in folder the set image from another form 'Sketchpad' image control
Forms!Fr_Sketchpad.Visible = False
Forms!Fr_Main!imgPixHolder.Picture = "" 'Forms!Fr_Sketchpad!Img_Std.Picture Was getting another error here so commented this
pixnum = 0
Exit Sub
End If
End With
'load first pix
Forms!Fr_Main.imgPixHolder.Picture = pixpaths(1)
pixnum = 1
End Sub
Public Sub Image_loop()
If pixnum = pixpaths.Count Then
pixnum = 1
ElseIf pixnum = 0 Then
Exit Sub
Else
pixnum = pixnum + 1
Forms!Fr_Main!imgPixHolder.Picture = pixpaths(pixnum)
End If
End Sub
Private Sub Form_Open(Cancel As Integer)
Call Image_set
End Sub
Private Sub Form_Timer()
Call Image_loop
End Sub
The Image_Set(), Image_loop() and variables are in one module and are called in Form_open and Form_timer events
The code is working fine for one loop cycle but for the next loop cycle it is showing an error:
Error 91 object variable or with block variable not set
on
If pixnum = pixpaths.Count Then
In debug mode when I check value for pixnum it is 0
[Update]
Class Module YtoFileSearch
Option Compare Database
Option Explicit
' How this is not another proof that doing VBA is a bad idea?
' Nevertheless, we'll try to make the scripts relying on Application.FileSearch works again.
' The interface of this YtoFileSearch class aims to stick to the original
' Application.FileSearch class interface.
' Cf is https://msdn.microsoft.com/en-us/library/office/aa219847(v=office.11).aspx
' For now it do not handle recursive search and only search for files.
' More precisely the following filters are not implemented:
' * SearchSubFolders
' * MatchTextExactly
' * FileType
' If that's something you need, please create an issue so we have a look at it.
' Our class attributes.
Private pDirectoryPath As String
Private pFileNameFilter As String
Private pFoundFiles As Collection
' Set the directory in which we will search.
Public Property Let LookIn(directoryPath As String)
pDirectoryPath = directoryPath
End Property
' Allow to filter by file name.
Public Property Let fileName(fileName As String)
pFileNameFilter = fileName
End Property
'Property to get all the found files.
Public Property Get FoundFiles() As Collection
Set FoundFiles = pFoundFiles
End Property
' Reset the FileSearch object for a new search.
Public Sub NewSearch()
'Reset the found files object.
Set pFoundFiles = New Collection
' and the search criterions.
pDirectoryPath = ""
pFileNameFilter = ""
End Sub
' Launch the search and return the number of occurrences.
Public Function Execute() As Long
'Lance la recherche
doSearch
Execute = pFoundFiles.Count
End Function
' Do the nasty work here.
Private Sub doSearch()
Dim directoryPath As String
Dim currentFile As String
Dim filter As String
directoryPath = pDirectoryPath
If InStr(Len(pDirectoryPath), pDirectoryPath, "\") = 0 Then
directoryPath = directoryPath & "\"
End If
' If no directory is specified, abort the search.
If Len(directoryPath) = 0 Then
Exit Sub
End If
' Check that directoryPath is a valid directory path.
' http://stackoverflow.com/questions/15480389/excel-vba-check-if-directory-exists-error
If Dir(directoryPath, vbDirectory) = "" Then
Debug.Print "Directory " & directoryPath & " does not exists"
Exit Sub
Else
If (GetAttr(directoryPath) And vbDirectory) <> vbDirectory Then
Debug.Print directoryPath & " is not a directory"
Exit Sub
End If
End If
' We rely on the Dir() function for the search.
' cf https://msdn.microsoft.com/fr-fr/library/dk008ty4(v=vs.90).aspx
' Create the filter used with the Dir() function.
filter = directoryPath
If Len(pFileNameFilter) > 0 Then
' Add the file name filter.
filter = filter & "*" & pFileNameFilter & "*"
End If
' Start to search.
currentFile = Dir(filter)
Do While currentFile <> ""
' Use bitwise comparison to make sure currentFile is not a directory.
If (GetAttr(directoryPath & currentFile) And vbDirectory) <> vbDirectory Then
' Add the entry to the list of found files.
pFoundFiles.Add directoryPath & currentFile
End If
' Get next entry.
currentFile = Dir()
Loop
End Sub
Please advice how to resolve!
I have to answer your comment question you had for me here. This may not solve your issue, but it may help you find it, especially if the error is from you setting pixpaths = nothing in another function as #dbmitch suggested.
You would refer to .FoundFiles in Image_Set the same way you would pixpath, the collection gets populated by the doSearch sub from the .Execute function so the following code should work the same. Also, unless you are using your arguments in another module, you may want to consider making them Private like I did here.
Private pix_path As String
Private pixnum As Integer
Private fs As YtoFileSearch
Public Sub Image_set()
pix_path = "C:\Images"
Set fs = New YtoFileSearch
With fs
.NewSearch
.LookIn = pix_path
.fileName = "*.jpg"
If fs.Execute() > 0 Then
'load first pix
Forms!Fr_Main.imgPixHolder.Picture = .FoundFiles(1)
pixnum = 1
Else
MsgBox "No files found!"
DoCmd.OpenForm "Fr_Sketchpad" ' If no images found in folder the set image from another form 'Sketchpad' image control
Forms!Fr_Sketchpad.Visible = False
Forms!Fr_Main!imgPixHolder.Picture = ""
'Forms!Fr_Sketchpad!Img_Std.Picture Was getting another error here so commented this
pixnum = 0
End If
End With
End Sub
Public Sub Image_loop()
With fs
If pixnum = .FoundFiles.Count Then
pixnum = 1
ElseIf pixnum <> 0 Then
pixnum = pixnum + 1
Forms!Fr_Main!imgPixHolder.Picture = .FoundFiles(pixnum)
End If
End With
End Sub
Related
I have some combo boxes with code for adding new items to the source table with a form when it doesn't exist.
The code will replace Chr(47) / and Chr(92) \ with Chr(45) - if present. This is done because a file name is created using concatenation later.
The problem is if a character is replaced, I get an Access error that the item is not in the list. This does not happen if a character is not replaced. In both instances the correct items are added to the corresponding tables.
I have tried replacing the character before passing it to OpenArgs, AfterUpdate, on the form after it opens, etc. The error does not break so the program is working, I just want to eliminate a unnecessary pop-up message.
Any help is greatly appreciated.
Private Sub cboManual_NotInList(NewData As String, Response As Integer)
Dim MyMessage As String
Dim myButtons As Integer
Dim myTitle As String
Dim strSQL As String
On Error GoTo ErrHandler
MyMessage = "This Manual does not exist. Create it?"
myButtons = vbYesNo + vbDefaultButton1 + vbQuestion + vbApplicationModal
myTitle = "Add Manual?"
MyChoice = MsgBox(MyMessage, myButtons, myTitle)
If MyChoice = 6 Then
If Not DBAuthority = "Admin" And Not DBAuthority = "Data Entry" Then
Response = acDataErrContinue
MsgBox "Sorry, authorized access only", _
vbOKOnly, "Important Information"
Exit Sub
Else
Response = acDataErrAdded
CallerField = "Manual"
CallerForm = "NewDocument"
NewData = Replace(NewData, Chr(47), Chr(45))
NewData = Replace(NewData, Chr(92), Chr(45))
DoCmd.OpenForm "AddManual", windowmode:=acDialog, OpenArgs:=NewData
Me.cboManual.RowSource = Me.cboManual.RowSource
Me.cboManual.value = strAddManual
strManual = Me.cboManual.value
strAddManual = vbNullString
Me.cboSection.value = strAddSection
strSection = Me.cboSection.value
strAddSection = vbNullString
Me.cboEngine.value = strAddEngine
strEngine = Me.cboEngine.value
strAddEngine = vbNullString
End If
ElseIf MyChoice = 7 Then
Response = acDataErrContinue
MsgBox "Select Manual from list.", vbOKOnly, "Select Manual"
Me.cboManual.Undo
Me.cboManual.SetFocus
Exit Sub
End If
Exit Sub
ErrHandler:
If Err = 20 Then
Response = acDataErrContinue
ElseIf Err = 94 Then
Response = acDataErrContinue
Resume Next
ElseIf Err = 2237 Then
Response = acDataErrContinue
Resume Next
ElseIf Err = 0 Then
Response = acDataErrContinue
Else
MsgBox "cboManual.NotInList Err = " & Err.Number & " :" & Err.Description
Exit Sub
End If
Exit Sub
End Sub
Option one
Replace while typing
Select Case KeyCode
Case vbKeyDown
Me![cboNewPart].Dropdown
Case 220, 191 ' / and \
KeyCode = 189 ' with -
Case Else
End Select
Option two
after adding the new value to the table. do
me.combo.undo, me.combo.requery. me.combo.value = newValue
followed by acDataErrContinue
this way you won't get error message but the list will flicker a and it's purely a hack.
Try using a different variable name (other than NewData) to store the modified version of the value passed to the NewData argument, i.e.:
Dim NewString as String
NewString = NewData
NewString = Replace(NewString, Chr(47), Chr(45))
NewString = Replace(NewString, Chr(92), Chr(45))
DoCmd.OpenForm "AddManual", windowmode:=acDialog, OpenArgs:=NewString
Since VBA arguments are passed ByRef unless otherwise stated, any modification to the argument value will be modifying the original value passed to your cboManual_NotInList event handler.
Given the above, you could alternatively try changing the NewData argument to be passed by value (ByVal):
Private Sub cboManual_NotInList(ByVal NewData As String, Response As Integer)
I'm designing a database that will incorporate many images so I've decided to link to external files by storing their paths and bounding an image control to that field. Here is the code that allows me to select the file and store it was a string:
Public Function ShowFileDialog() As String
Dim objFD As Object
Dim strOut As String
strOut = vbNullString
Set objFD = Application.FileDialog(msoFileDialogOpen)
If objFD.Show = -1 Then
strOut = objFD.SelectedItems(1)
End If
Set objFD = Nothing
ShowFileDialog = strOut
End Function
which is then called by a control button:
Private Sub Command128_Click()
Dim strChoice As String
strChoice = ShowFileDialog
If Len(strChoice) > 0 Then
Me.Path = strChoice
Else
'bleh
End If
End Sub
This stores the absolute directory of the selected file, however I recently realized that I need to store relative paths so that when the database and its associated directories are moved onto a new computer (which is very likely to happen) these links will be maintained.
UPDATE: The helpful tips provided by Hans Up enabled me to get this to work. Here is my revised and tidied up code.
Public Function GetPath()
Dim objFD As Object
Dim strOut As String
Dim strAbsolute As String
Dim strFolder As String
Dim strRelativePath As String
strOut = vbNullString
Set objFD = Application.FileDialog(msoFileDialogOpen)
If objFD.Show = -1 Then
strOut = objFD.SelectedItems(1)
End If
Set objFD = Nothing
strAbsolute = strOut
strFolder = CurrentProject.Path & "\"
strRelativePath = Mid(strAbsolute, Len(strFolder) + 1)
If Len(strRelativePath) > 0 Then
Me.Path = strRelativePath
Else
'bleh
End If
End Function
Private Sub Command128_Click()
GetPath
End Sub
Here is an Immediate window session which demonstrates techniques you can use to determine the relative path of your selected file ...
' folder where db file resides ...
? CurrentProject.Path
C:\share\Access
strFolder = CurrentProject.Path & Chr(92)
? strFolder
C:\share\Access\
' strChoice is the file path from your code sample;
' use Mid() to get the piece from that string which follows strFolder ...
strChoice = "C:\share\Access\image\foo.png"
strRelativePath = Mid(strChoice, Len(strFolder) + 1)
? strRelativePath
image\foo.png
' combine base folder and relative path again just to confirm we got the right pieces ...
? strFolder & strRelativePath
C:\share\Access\image\foo.png
I have checked Google, and the suggested answers here, but have had no luck unfortunately.
The last thing I need to do is have an email read the rateNbr variable into the email body, but it just comes up empty.
I tried to make Public Function FuncRateCheckFile read as Public Function FuncRateCheckFile(ByVal rateNbr As String), to try and enable it to be called outside the function, but this then breaks the function when it is called elsewhere. :(
Here is the code, with comments as to where I am referring:
Public Function FuncRateCheckFile()
Dim blnContinue As Boolean
Dim strLine As String
Dim strSearchFor, strSearchWrd, LineCount, objFSO, objTextFile, arrLines
Dim dteNow As Date
Dim newDate As String
'//==============================================================================================
'// DECLARED
Dim rateNbr As String
'//==============================================================================================
FuncRateCheckFile = False
blnContinue = True
If blnContinue Then
Const ForReading = 1
'Get todays date and reformat it
dteNow = DateValue(Now)
newDate = Format(dteNow, "dd/MM/yy")
strSearchWrd = newDate
'Read the whole file
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile(m_RateCheckFile, ForReading)
LineCount = 0
Do Until objTextFile.AtEndOfStream
strLine = objTextFile.ReadLine()
If InStr(strLine, strSearchWrd) <> 0 Then
arrLines = Split(strLine, vbCrLf)
LineCount = LineCount + 1
End If
Loop
'Log a message to state how many lines have todays day, and if there are none, log an error
If LineCount <> 0 Then
'//==============================================================================================
'// "rateNbr" IS WHAT I AM TRYING TO GET TO PUT IN THE EMAIL
LogMessage "Rate file date is correct"
rateNbr = "Number of rates for " & newDate & " in the file recieved on " & newDate & " is " & LineCount
LogMessage rateNbr
EmailAdvice2
objTextFile.Close
'//==============================================================================================
Else
blnContinue = False
LogError "Failed to retrieve Current Rate date, please check rate file.."
EmailAdvice
objTextFile.Close
End If
End If
FuncRateCheckFile = blnContinue
LogMessage "Completed Check Rate file"
End Function
Private Function EmailAdvice2()
Dim strSMTPFrom As String
Dim strSMTPTo As String
Dim strSMTPRelay As String
Dim strTextBody As String
Dim strSubject As String
Dim oMessage As Object
'//==============================================================================================
'// DECLARED AGAIN
Dim rateNbr As String
'//==============================================================================================
Set oMessage = CreateObject("CDO.Message")
strSMTPFrom = "no-reply#work.com.au"
strSMTPTo = "me#work.com.au"
strSMTPRelay = "smtp.relay.com"
'//==============================================================================================
'// THIS MAKES THE TEXT BODY BLANK, BUT THE EMAIL STILL SENDS
strTextBody = rateNbr
'//==============================================================================================
strSubject = "Todays rates"
'strAttachment = "full UNC path of file"
oMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
oMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = strSMTPRelay
oMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
oMessage.Configuration.Fields.Update
oMessage.Subject = strSubject
oMessage.From = strSMTPFrom
oMessage.To = strSMTPTo
oMessage.textbody = strTextBody
'oMessage.AddAttachment strAttachment
oMessage.Send
End Function
I am positive that it is blank because I have declared rateNbr under EmailAdvice2() and then not given it anything to fill the variable with. But I don't know how to make it call the variable under FuncRateCheckFile().
Thanks to all for any assistance.
As Plutonix stated, this is a scope issue.
Move the declaration of your 'rateNbr' variable out to class level, and remove the local declarations inside your functions:
Dim rateNbr As String ' <-- out at class level it will be accessible from both functions
Public Function FuncRateCheckFile()
...
' REMOVE both the decalarations of "rateNbr" that are INSIDE your functions
...
End Function
Private Function EmailAdvice2()
...
' REMOVE both the decalarations of "rateNbr" that are INSIDE your functions
...
End Function
i have the below script , modify from this http://www.vbforums.com/showthread.php?613400-Loop-through-folders-subfolders
Private Sub Command1_Click()
Dim fld As Folder
Dim searchString As String
Dim ResultFilePath As String
Set fso = New FileSystemObject
Set fld = fso.GetFolder("C:\Users\janedoe\Desktop\jane")
searchString = "ClaimSheet.xlsx"
ResultFilePath = RecursiveSearch(fld, searchString)
Set fld = Nothing
Set fso = Nothing
If ResultFilePath = "" Then
MsgBox ("We could not find the file " & searchString)
Else
MsgBox ("We found it, its at " & ResultFilePath)
End If
End Sub
Function RecursiveSearch(fld As Folder, search As String) As String
Dim tfold As Folder
Dim tfil As File
For Each tfold In fld.SubFolders
Debug.Print "looking in the " & tfold & " folder"
RecursiveSearch tfold, search
If RecursiveSearch = search Then
Exit Function
End If
Next
Debug.Assert InStr(tfil, search) = 0
If InStr(tfil.Name, search) Then
RecursiveSearch = tfil.Path
Exit function
End If
Next
End Function
What I would like the RecursiveSearch function to do is, search the folder for the searchString file, once found, stop searching and return the file path.
The problem is, I can't exit function without losing the value at the line
RecursiveSearch = tfil.Path
I think it does that because the function might have gone out of scope when returning the upper level .
Any help would be appreciated,
Your recursion line should be:
RecursiveSearch = RecursiveSearch(tfold, search)
This will allow each level of the recursion to pass it's result back up the chain.
Also, the following line seems to be missing from the code in your question:
For Each tfil In fld.Files
I have an SSIS package that is now failing with an error of "index was outside the bounds of an array." It is occuring in the script component task, but I can't see any further information in the log about what line it failed. Here is the text to my Script Component, can anyone see anything wrong with this? I haven't change anything in the package and it just started failing all of a sudden.
Imports System
Imports System.Data
Imports System.Math
Imports Microsoft.SqlServer.Dts.Pipeline.Wrapper
Imports Microsoft.SqlServer.Dts.Runtime.Wrapper
<Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute> _
<CLSCompliant(False)> _
Public Class ScriptMain
Inherits UserComponent
' Wrapper for web service
'Private CurrencyInfo As RetrieveExchangeRatesOutputMessageContract
' wrapper for Response from web service
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
Try
Dim ws As New CurrencyExchangeWS.CurrencyExchangeWS
Dim inContract As New CurrencyExchangeWS.ExchangeRateInputDataContract
Dim msg As New CurrencyExchangeWS.RetrieveExchangeRatesInputMessageContract
Dim inContracts(1) As CurrencyExchangeWS.ExchangeRateInputDataContract
'Dim dt As Date = Now
'dt = dt.AddDays(-1)
inContract.RequestAsOfDate = Now
'inContract.RequestAsOfDate = "2011-07-18"
' IMPORTANT: You need to specify SourceCurrencyIdSpecified and TargetCurrencyIdSpecified or the service will assume these values are null.
inContract.SourceCurrencyIdSpecified = True
inContract.SourceCurrencyId = Row.CurrencyTypeId
inContract.TargetCurrencyIdSpecified = True
inContract.TargetCurrencyId = 47
inContracts(0) = inContract
msg.ExchangeRateInputCollection = inContracts
ws.Credentials = System.Net.CredentialCache.DefaultCredentials
Dim outMsg As CurrencyExchangeWS.RetrieveExchangeRatesOutputMessageContract = ws.RetrieveExchangeRates(msg)
'Dim rate =outMsg.ExchangeRateInfoCollection(0).Rate
Dim rate As Decimal = outMsg.ExchangeRateInfoCollection(0).Rate
'Dim edate = outMsg.ExchangeRateInfoCollection(0).RequestAsOfDate
Dim edate = outMsg.ExchangeRateInfoCollection(0).RateAsOfDate
'Dim edate As DateTime
'edate = outMsg.ExchangeRateInfoCollection(0).RateAsOfDate
Row.date = edate
Row.rate = Convert.ToDecimal(1.0 / rate)
'Dim currency = outMsg.ExchangeRateInfoCollection(0).TargetCurrencyId
Catch ex As Exception
ComponentMetaData.FireError(1, ComponentMetaData.Name, ex.Message, String.Empty, 0, True)
End Try
End Sub
Public Overrides Sub PreExecute()
MyBase.PreExecute()
'
' Add your code here for preprocessing or remove if not needed
''
End Sub
Public Overrides Sub PostExecute()
MyBase.PostExecute()
'
' Add your code here for postprocessing or remove if not needed
' You can set read/write variables here, for example:
' Me.Variables.MyIntVar = 100
''
End Sub
'Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
'
' Add your code here
'
'End Sub
End Class
At first glance, the most likely culprit is that outMsg.ExchangeRateInfoCollection contains zero items. Unfortunately, we don't have enough information to say that for sure. One possible way to get that additional information is to declare a progress string variable outside your Try ... Catch block, update it before each step in your code, and include it in the FireError message:
Public Overrides Sub Input0_ProcessInputRow(ByVal Row As Input0Buffer)
Dim progress As String
Try
Dim ws As New CurrencyExchangeWS.CurrencyExchangeWS
Dim inContract As New CurrencyExchangeWS.ExchangeRateInputDataContract
Dim msg As New CurrencyExchangeWS.RetrieveExchangeRatesInputMessageContract
Dim inContracts(1) As CurrencyExchangeWS.ExchangeRateInputDataContract
progress = "Setting inContract.RequestAsOfDate"
inContract.RequestAsOfDate = Now
progress = "Setting inContract.SourceCurrencyIdSpecified"
inContract.SourceCurrencyIdSpecified = True
' etc, etc, etc.
Dim outMsg As CurrencyExchangeWS.RetrieveExchangeRatesOutputMessageContract
progress = "Setting outMsg"
outMsg = ws.RetrieveExchangeRates(msg)
progress = String.Format("Setting rate; outMsg.ExchangeRateInfoCollection.Length is {0}", outMsg.ExchangeRateInfoCollection.Length)
' This is likely the place where the IndexOutOfRangeException is being thrown
Dim rate As Decimal = outMsg.ExchangeRateInfoCollection(0).Rate
' rest of code
Catch ex As Exception
ComponentMetaData.FireError(1, ComponentMetaData.Name, progress + ": " + ex.Message, String.Empty, 0, True)
End Try
End Sub