For-Next loop not stopping - vba

This seems crazy basic to ask, but I can't figure out what I've done wrong. Working in Access VBA to loop through an array, but the FOR loop doesn't stop and I get a "Subscript out of range" error.
compStr = "[" & uniqueIDs(0) & "]=" & rs(uniqueIDs(0))
bnd = UBound(uniqueIDs)
For i = 1 To bnd
compStr = compStr & " and [" & uniqueIDs(i) & "]=" & rs(uniqueIDs(i))
Next i
In this example I'm using the loop to build compStr and bnd stores the array size. uniqueIDs holds 2 records (0-1) and I've confirmed that bnd and UBound(uniqueIDs) value=1. However the FOR statement continues until i=2 and of course I get an error when it executes uniqueIDs(2) on the 4th line. What am I missing? (I've pasted debug window images below)

I did a test but that successfully ran, see below implementation:
Regarding above comments on the data types: I understand why people use Variants in Access: this is because often table values are assigned to variables, and only Variant can pick up NULL. However, in your case something forced the string values to be Strings and not Variants - if I used the line commented out, it would yield Variant/Variant(0 to 1), so I used a typed dummy array - I don't know how otherwise get Variant/String(0 to 1).
Nevertheless, there was no error. Did you compile your code with Debug -> Compile? If not, could you do it and re-try? Sometimes this is necessary, otherwise some code segments newly added/removed are just not executing and the previous compiled version of the code (which is stored by Access) is making a total mess.
If it's still not working, I think you should add the rest of the code as others asked in comments to see how the variables are assigned values, and the version of Access you are using.

Related

Malicious macro warning on code that is doing exactly what I want it to

I am building an energy system model that is using a combination of VBA and Python (Pyomo). I am using a small piece of VBA code to call a command line, which in turn uses Pyomo. This code is looping to execute a separate Pyomo optimisation for each day.
The code is working perfectly, apart from the message in the image below being triggered. When I step through the code slowly, the message seems to be triggered by the following line of code which is calling a command line:
Call Shell("cmd.exe /S /c" & "pyomo solve --solver=glpk " & Pyomo_file & " " & DAT_file & " --save-results " & Results_file, vbHide)
I have also tried to run this with the string being simplified down into a single string variable, which I called "Command", but the message below was still triggered.
The hyperlink in the message box (https://support.microsoft.com/en-us/office/malicious-macros-were-found-9e461c61-69d1-4ea9-a386-9ad0deaccfdd?ns=excel&version=90&syslcid=1033&uilcid=1033&appver=zxl900&helpid=56779&ui=en-us&rs=en-us&ad=us) suggests that this issue is triggered by AMSI and that my computer thinks this code is malicious. It is not - it is doing exactly what I want it to!! Digging further I think (but may be wrong) that the virus checks are being triggered by the fact that this string is a combination of so many inputs - the virus checker thinks this is obfuscation.
A few further points:
This line of code is within the daily loop I mentioned above. The
error does not get triggered until the loop has already happily
executed this line >30 times.
Bizarrely it seems to get thrown out on
the same date each time I try to run the loop. If I skip that date, it falls over again at a later date...
I have set my macro security settings to "enable all macros" and this
still happens. I only have the one Office 365 licence in my
organisation (my own company) so I don't think there can be any odd admin
permissions causing this problem.
Finally, I have tried to define the folders I am using as 'safe locations' and this doesn't help either.
Ideally, I would like to just disable the AMSI software while this command line is being executed. But I don't know how to do this of whether it is even possible. If not, any alternative ideas?!
I tried to run the MpCmdRun.exe script suggested by MS in this post, and it seems to have worked. It's possible that is just fluke, but it is working for now! Fingers crossed!

Access 2010 + VBA: Parameterised append queries – one works, two don’t, but no error messages

This one has had me tearing my hair out for ages but the solution still eludes me. And my last 15 years before retiring were working with Access & VBA, so my pride is hurting even more than my hair!
The project:
A database to catalogue portraits recording for each artist name, sitter name, date painted (when known), brief details on content (may be none), and the portrait’s location (if known). In addition to the table for portrait details are a table for artist information and one for sitter information (plus various lookup & other tables that aren’t relevant to this issue).
My client was super-keen to get data entered so she uses the memo field on the artist form (the first one designed) to enter details of portraits painted by that artist – following a strict pattern set by me (and checked before attempting to process). That’s not the issue – my parsing routine correctly identifies the data to be processed for the creation of the new sitter records and portrait records: I know this to be true because my code is liberally scattered with message boxes showing the VBA’s interpretation of the data (vital for debugging!)
The process of parsing + posting is briefly as follows (the memo field is assigned to a variable which is then parsed one character at a time):
The sitter’s name is identified (always the first entry on a new line) and assigned to a variable, then (if relevant/if known) brief details, date painted & location;
once it’s reached the end of the details for the portrait it shows a message box giving that information as interpreted by the routine for me to confirm;
it then appends the sitter’s name to the sitters’ table and returns the ID of the new record. This works. It then appends (or, rather, it should append but doesn’t!) the relevant data, including the sitter ID (and the artist ID from the form) to the portraits table.
It then continues (on that line if there are more portraits for that sitter, otherwise on the next line, until it reaches the end of the memo).
Originally the two append processes were by dynamically-created SQL – building the SQL strings using the appropriate variables – using database.Execute. But when I found that the second append was failing – with no error messages – I spent several hours looking at various tech sites, and one message was coming through strongly – use parameterised queries! OK, hadn’t used these before (not in VBA – of course I’d used them on forms for select & other queries) so I set up a simple test database to mimic the process but without putting my client’s data at risk!
It took a while – and a bit more time online – before I got it right, but yay, it worked with all combinations of missing data. (Incidentally, as you’ll note from the table defs below, the “year painted” is an integer field, which of course doesn’t accept “Nulls”, and I don’t want a zero where there’s no date, so there are two append portrait queries, one omitting posting to the ‘year’ field.)
And now I’ve gone back to the live database – and the query that appends the sitter is working, but the other two aren’t, again with no error message. So it’s over to you, please!
TABLE DEFS (I’m only listing the relevant fields; no fields have Required set to Yes; zero-length strings allowed):
taArtists: arID – autonumber; arNotes – memo; (plus other fields)
taSitters_Sub: ssID – autonumber; ssFullname – text 70; (plus other fields)
xtaPortraits: xrID – autonumber; xrArtistRef – long integer (link to taArtists = arID);
xrSitterRef – long integer (link to taSitters_sub = ssID); xrPortraitName – text 25; xrLocationCode – text 20; (plus other fields)
PARAMETERISED QUERIES
qu_app_sitter: INSERT INTO taSitters_Sub ( ssFullname ) SELECT [par1] AS Expr1;
qu_app_portrait: INSERT INTO xtaPortraits ( xrArtistRef, xrSitterRef, xrYearPainted, xrPortraitName, xrLocationCode ) SELECT [par2] AS Expr1, [par3] AS Expr2, [par4] AS Expr3, [par5] AS Expr4, [par6] AS Expr5;
qu_app_portrait_NoYear: INSERT INTO xtaPortraits ( xrArtistRef, xrSitterRef, xrPortraitName, xrLocationCode ) SELECT [par2] AS Expr1, [par3] AS Expr2, [par5] AS Expr4, [par6] AS Expr5;
EXTRACTS OF VBA
(Were I to start again I’d probably feed the values for par2 to par6 to a separate subroutine but while that would reduce the amount of code I’m not sure it’d actually be more efficient! I am, of course, open to expert advice on that! Were my client to start again I’d get her to wait for the system to be complete before entering data – making all this redundant – or get her to enter the data in an Excel spreadsheet & I’d process it from there. One lives, one learns [hopefully!])
1 – declarations, setting database & querydefs:
[only showing relevant Dims:
Private Sub Command77_Click()
On Error GoTo myError
Dim myID As Long 'the ID for the painter
Dim myName As String 'the name of the sitter
Dim myDesc As String 'any text description **could include part-dates or other digits
Dim myLoc As String 'the location code
Dim myDate As Integer 'myNum converted from string if it's 4 digits
Dim Errline As Integer ‘used so error messages get me close to the problem
'**** Now the database stuff
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim mySitterID As Long, myNewRows As Integer
Dim qd1 As DAO.QueryDef, qd2 As DAO.QueryDef, qd3 As DAO.QueryDef
Dim par1 As DAO.Parameter, par2 As DAO.Parameter, par3 As DAO.Parameter, par4 As DAO.Parameter, par5 As DAO.Parameter, par6 As DAO.Parameter
'**** Set these once, and turn off in 'Leave'
Set db = CurrentDb()
Set qd1 = db.QueryDefs("qu_app_sitter")
Set qd2 = db.QueryDefs("qu_app_portrait")
Set qd3 = db.QueryDefs("qu_app_portrait_noyear")
2 showing routine calling the insert queries plus preceding message box
[there are 9 of these, similar but not identical, depending on missing information]
MsgBox "Got name & location only" & vbCrLf & vbCrLf & "Sitter: " & myName & vbCrLf & "Location: " & myLoc, vbInformation, "LINE " & Errline
qd1.Parameters("par1").Value = myName
qd1.Execute
myNewRows = qd1.RecordsAffected
MsgBox myNewRows & " record added", vbInformation, "NEW SITTER"
myNewRows = 0
qd1.Close
Set rs = db.OpenRecordset("SELECT ##IDENTITY AS LastID;")
mySitterID = rs!lastid
qd3.Parameters("par2").Value = myID
qd3.Parameters("par3").Value = mySitterID
qd3.Parameters("par5").Value = myDesc
qd3.Parameters("par6").Value = myLoc
qd3.Execute
myNewRows = qd3.RecordsAffected
MsgBox myNewRows & " record added", vbInformation, "NEW PORTRAIT (year not known)"
myNewRows = 0
qd3.close
Both queries should insert 1 row - the first always does, the second always reports 0 rows inserted (and indeed none is inserted)
I'm painfully aware that this post is much longer than many here but hopefully I've given all the info required.
I did a quick lookover of your code.. I suggest
are you getting the correct value of mySitterID ?
im not 100% sure but isnt the ##Identity actually Artistid??
Meir
OK, thanks partly to a prompt from Meir Rotfleisch, I dug deeper - running the query manually (omitting each field in turn) revealed that one was causing a key violation error. Then typing manually into that field pointed me to a need for a related entry in another table (which table wasn't visible on the relationship diagram!) Removing that link (which will be reinstated once the full system has been built & tested) resolved the problem.
One vital lesson I'd like to pass on - make sure your relationship diagram shows ALL tables!

How to Expand Existing My.Settings StringCollection Array

I'm a bit rusty and am updating a VB.Net program I wrote in 2013 using VS2017. I needed to expand a My.Settings StringCollection array by one entry. It was User scope, and I could not figure out how to enlarge the existing array, despite hours of online research and testing.
I tried using the .Add method, but was told "not a member of String." I tried deleting "user.config."
Finally I tried this, which worked:
1) At design time, in Settings, I added the extra placeholder value to my StringCollection variable.
2) Change the scope from User to Application.
3) Run the program.
4) Change the scope back to User.
I confirmed this worked by inspecting My.Settings at runtime.
What I want to know is:
Why did this work, and is there a more elegant way to have done this?
For index = 0 To 5 'Motor numbers
'Changing upper index limit from 4 to 5 throws an Exception
strCommand = "(0," & index.ToString & ")"
My.Settings.strMotor_Kp(index) = (Kpid(0, index))
strCommand = "(1," & index.ToString & ")"
My.Settings.strMotor_Ki(index) = (Kpid(1, index))
strCommand = "(2," & index.ToString & ")"
My.Settings.strMotor_Kd(index) = (Kpid(2, index))
Next
The StringCollection editor is quite a bit limited, you can append lines easily but insert one in the middle is not possible to my knowledge.
But you can also change the values in app.config, recompile the project and then open the settings, then VS is going to asks you
Value of setting 'Foo' was changed in the app.config file. ... Do you
want to update the value in the .settings file?
The whole thing should work without having to switch the setting to application- and back to user mode. Just compile it and then the changes are reflected in the app.config (the user.config is generated at runtime when method Save() is called).
Only if the project namespace has changed, then the old entries must be deleted from app.config prior to recompiling the project, or the namespace must be adjusted in the <[...].Properties.Settings> tag).

SSIS Element cannot be found in a collection (but I have them all listed!)

I'm getting a persistent error:
The element cannot be found in a collection.
This error happens when you try to retrieve an element from a collection on a container during execution of the package and the element is not there.
I've checked, double and triple-checked my variable listings in the Read-Only and Read-Write variables in my Script task.
I've debugged it to death and gotten input from another programmer here who couldn't spot the issue either.
I've also researched to no end.
Does anyone see anything wrong with my code?
Script Task code:
Public Sub Main()
Dts.Variables("User::strMailBody").Value = "Thank you for submission. For your convenience, we are including the last four of the HICN# and the Name on the application(s) we have received* from you." _
& vbNewLine & vbNewLine & "Here are the following: " & vbNewLine & vbNewLine
Dts.Variables("User::strMailBody").Value = Dts.Variables("User::strMailbody").Value.ToString() & vbNewLine & Dts.Variables("User::strListing").Value.ToString()
Dts.Variables("User::strMailBody").Value = Dts.Variables("User::strMailBody").Value.ToString() & vbNewLine & vbNewLine & Dts.Variables("User::strFooter").Value.ToString()
If Left(Dts.Variables("User::strAgentID").Value, 2) = "TX" Then
Dts.Variables("User::strSubject").Value = "ACME Health Plans Confirmation: Total "
Else
Dts.Variables("User::strSubject").Value = "ACME2 Baptist Health Plans Confirmation: Total "
End If
Dts.Variables("User::strSubject").Value = Dts.Variables("User::strSubject").Value.ToString() & Dts.Variables("User::lngCountAgent").Value.ToString() & " " & "[RESTRICTED: CONFIDENTIAL]"
Dts.Variables("User::DateSent").Value = Now()
Dts.Variables("User::UserSent").Value = "SSIS"
Dts.TaskResult = ScriptResults.Success
End Sub
For anybody else struggling with this issue the resolution for me was as follows: (note I am NOT using User:: when getting variable values within my script task)
On the package Properties I hadn't included the variables as ReadOnlyVariables
You'll need to set your newly added variables as follows:
Right click on the package and select Edit
In the Script section click on ReadOnlyVariables or ReadWriteVariables (depending on your how you want your variables behave)
Check the check-box beside the variables you wish to use in your script task
Click Ok to save your changes
Hope this helps
I just had the same issue and unable to find the problem for ages. I found that the reason for the error was that I had missed one of the colons between "User" and the variable name.
I had this (which caused the error):
string FileName = UserVariables["User:CurrentFileName"].Value.ToString();
when I should have had this:
string FileName = UserVariables["User::CurrentFileName"].Value.ToString();
just in case anyone else has the same problem :)
Ohhh.........man. It's amazing how you can stare at this stuff and miss something stupid, for hours.
strFooter was missing in the listing.
ALL SET NOW. Sincere thanks to those who looked and commented. Eric, thanks, I'll remember that as sometimes I will probably need to use C insatead of VB (haven't yet but will).
Had a similar issue, after a lot of debugging, realized that the variable naming convention should be User::varname and NOT USER::varname
I guess c# is very case sensitive.
Hope this helps and saves you lot of your valuable time :-)
a) Right click on the script task and choose edit
b) Locate the Read or Read/Write variables properties in the list.
c) Click on the property and the variable you wish to access in the script task.
Another variation on "have been staring at the screen for too long to see the typo". In my case, I got the same error by mixing up the syntax between Project Params and User variables, and adding a $ sign in front of User.
error :
string varA = (string)Dts.Variables["$Project::ParamA"].Value
string varB = (string)Dts.Variables["$User::ParamB"].Value
corrected :
string varA = (string)Dts.Variables["$Project::ParamA"].Value
string varB = (string)Dts.Variables["User::ParamB"].Value

Access. Alternative for left function

i am trying to write a query using left function in access to take only the first 3 characters of a field.
Is there any alternative method for performing the same process without using left
Usage of left function shows a compile. error all of a sudden without any reason. If i copy the table and query to a new database it works fine for a while before the error comes again. This happens only on the usage of Left function.
you can use mid function:
Mid (Field, 1, 3)
The compile error is showing up because you have a missing reference. Open any module and check the References.
You can always try Mid
Mid([field1],1,3)
It sounds very like you have a problem with your references. Look for any references marked "MISSING". Also try to delete Visual Basic for Applications, it won't allow this, but it sometimes corrects the problem. Finally, check the details of Visual Basic for Applications and make sure that is available in the stated location. Any alternative to Left will be affected by this problem.
This problem is frequently associated with a missing reference that you would not think had anything to do with Left.
Run the following code and report back as to the results. Also tell us what version of Access you are running.
Sub ViewReferenceDetails()
Dim ref As Reference
For Each ref In Access.References
Debug.Print ref.Name & " - " & ref.Major & "." & ref.Minor & " - " & ref.FullPath
Next ref
End Sub