I am hoping someone can help me possibly debug the below code, as I am confused as to why it is happening.
I have a fairly simple VBScript that runs when a user logs onto a server/PC, that will create some signatures based on their active directory details. I have decided to move each section of the signature creation into a function, in order to make things a bit easier when creating new signatures.
Here is the function I am having issues with:
'Function to add job title and company
Function AddTitle
objSelection.Font.Name = "Calibri"
objSelection.Font.Bold = False
objSelection.Font.Italic = False
objSelection.Font.Size = "11"
objSelection.Font.Color = RGB(0,0,0)
If(strTitle) Then
objSelection.TypeText strTitle & Chr(11)
End If
objSelection.TypeText strCompany & Chr (11)
End Function
Now, when calling the function later on using:
'Add job title and company
AddTitle
It ignores the section within the If statement. I know that some variables need to be defined globally for them to work in a function, and strTitle is definitely defined at the beginning of my script.
Am I missing something totally obvious, as the section inside the if statement functions correctly if taken out of the if statement. Likewise, If I were to add the if statement to my script inline, and take it out of the function, it works correctly.
It is only when running from the Function.
Apparently you have a global On Error Resume Next somewhere in your script, which suppresses the "Type Mismatch" error that the line If(strTitle) Then normally would raise.
strTitle probably contains a string value, so you can't use it like a boolean value in a conditional. Change the line to something like this:
If Trim(strTitle) <> "" Then
and your code should work as expected.
Related
This is kind of a follow up question to this post: Access VBA recordset string comparison not working with wildcard but I don't have the rep to answer/comment on it to ask it in house. What I'm curious about is this line of code specifically:
If ![ACOD] = "*_X" Then '"$ICP_X" works
Debug.Print ![ACOD] 'For testing
'.Delete
End If
I want to know if this can be modified so that on a button click, it looks at all fields in a form with the field name of *_New (with the hope to catch all fields where the name ends in _New) and if they are not Null then confirm that the user wanted to make a the change indicated in the field. I was thinking of something along the lines like this:
If Not isNull(*_New.value) Then
If Msgbox ("Do you want to make these changes?",vbOKCancel, "Confirm Changes") = 1 Then
'### Do something with the record ###
End If
End If
EDIT
As of posting the above information, I did not have the Microsoft VBScript Regular Expressions Reference installed, currently I have version 5.5 (it was the latest version). With that installed (referenced?) and seeing the information from this site MS Access with VBA Regex, I'm wondering if it's better to do something like this:
Dim re As RegExp
Set re = New RegExp
re.IgnoreCase = True
re.Global = True
re.Pattern = "*_New"
If ##Not sure on syntax to match all fields## Then
Msgbox(##Same stuff as above MsgBox##)
End If
EDIT 2
Here's a sample case for my form I'm working on. Each of the fields to the right have names that end in _New. What I want to do is on the button click, to check and see what fields on the right have been filled in and ask the user if they want to confirm the changes to the record.
Not sure what you are trying to achieve but there is a way to access the control collection in a form. Here is a public function where you can loop through all controls and check its name.
Public Function FN_CONFIRM_CHANGES(iSender As Form)
Dim mCtl As control
For Each mCtl In iSender
If VBA.Right(mCtl.name, 4) = "_New" Then
Debug.Print mCtl.name & " is a match and its a " & VBA.TypeName(mCtl)
End If
Next mCtl
End Function
Call this function like
FN_CONFIRM_CHANGES Me 'Where me is referencing the form you are in.
You can modify the above code to return a boolean value to stop further execution if user decided not to save your changes or whatever logic you are trying to implement.
I am curious to understand why the following throws and compile error stating it is expecting a function or variable
Dim frm as Form
set frm = DoCmd.OpenForm(FormName:=mstr_FORM_NAME, View:=acDesign, WindowMode:=acHidden)
but I can do this
DoCmd.OpenForm FormName:=mstr_FORM_NAME, View:=acDesign, WindowMode:=acHidden
set frm=Forms(mstr_FORM_NAME)
I have no issue with doing what works, I just want to understand what is going on with the former statement.
Thank you,
Fred
DoCmd.OpenForm is a method which doesn't return a value. In the second code snippet you are accessing the Forms Collection, which contains the form, after it has been opened by DoCmd.OpenForm. When you call a method, you must not specify braces.
The OpenForm method (doc) is not returning anything, just opening the form (not returning it).
So, you're trying to cast Nothing into a variable defined As Form.
Instead, frm (having the Set frm = statement) is expecting to "become something", it is expecting a function or variable - a value, to be clear).
I have a form called Form1. In Form1, I have the following code -
Dim details As clsDetails
Set details = getDetials(1) ' This fails. It doesn't assign a value.
The getDetails function is declared in a separate module as follows-
Public Function getDetials(detailNumber As Integer) As clsDetails
Dim details As clsDetails
Select Case detailNumber
Case "1"
Debug.Print "Inside case1"
Set details = getDetail1()
Debug.Print details.comment ' This prints correctly.
End Select
Set getDetails = details
End Function
However, when I execute the above code, somehow, the details variable in Form1 doesn't get set, even though the getDetails function is called and prints the details correctly inside it. How to rectify this?
Do you have the Option Explicit keyword defined?
It looks like you might have a typo. Your function is called getDetials, but the variable you're setting the result to is getDetails, so the return value is not getting set.
I fixed the typo and everything works as expected on my end. Using the Option Explicit keyword will catch these types of errors.
I think this may be a case of not having Option Explicit On
Check you have Option Explicit at the top of your form (and in fact everywhere)
Your type mistake declaring the function getDetials but then setting an object called getDetails to the newly created class is actually creating a new object and the function return is not being set at all.
Insert Option Explicit and you will see that it won't compile!
When you have added Option Explicit everywhere got to Tools>Options>Editor tab and tick the box that says Require variable declaration - that will make sure it is added every time you add a new code file to your project.
Preface
I have a need to create multiple copies of a search form (in Access 2010) that returns a value to the calling (which is also the form that created the instance of the form).
As mentioned these forms could and will have multiple copies running at the same time for example a user could want to add a company to something so they:
Click "select company" and open an instance of the company search screen
Then open the company editor (leaving the original company search/selection screen open) as they notice the company has a parent company that hasn't been added yet.
They then click the "Select Parent Company" button that opens ANOTHER instance of the search and select screen
They find the parent company
Select it which closes the second search screen and the parent company is added to the first company.
The user then selects the modified company using the original search screen which again closes the original search screen and returns the selected company to what ever form they originally initialised the first search...
This all allows the users to update and correct data as and when they find error which reduces the likelihood of them forgetting and makes it much quicker!
MOST of this is fine now but I have had a lot of problems with instances of a form not being able to open as a "acDialog" thus stopping the calling code running until the search was done (see this question for more info) and the solution I have gone with is to simulate the pausing of the calling code by using a endless loop and checking if the search screen instant is still visible. Then when the user selects something on the search screen instant it puts the value in a hidden field in the search screen and hides it's self (not closed). The calling function then sees it's hidden grabs the value from the hidden field and unloads the instant.
Problem
I can check if the form is hidden using FormInstant.Visable but if the user closes the form this causes an error and the code I would normally use to check if the form exists requires a form name and as it's an instant of a form all the forms have the same name! I do have a reference to the form as it is stored in a local "form" object... The code I would normally use is:
CurrentProject.AllForms("FormName").IsLoaded
So how can I check for a instant of a form being loaded still?
An old question but here's what experience taught me: If One, Two, ... instances of FormDefn opened then user closes One (Master which is the only one that can be designed), Forms(FormName) gives an error, Forms(Form) gives wrong object,
but Forms(NumberIndex) does still exist with .Name = FormName!
OpenForm creates the Forms(FormName) object. Once closed Forms(FormName) gives an error. Any "Set xForm = New Form_xxx" creates forms in the forms collection that can only be accessed by the collection number index, and cant be designed.
So to later find a multi-instance form use something like:
Dim FormIdx As Integer
Dim lForm As Access.Form
For FormIdx = 0 To Application.Forms.Count - 1
Set lForm = AccessFunc.Appl.Forms(FormIdx)
If lForm.Name = pFormName Then
IsFormOpened = True
Set rForm = lForm
GoTo IsFormOpened_Exit
End If
Next
LOL I just realised while re-reading my msg that I can likely trap the error to work out if the form is open or not!
I have quickly written this and it seems to work fine:
Public Function IsFormLoaded(ByRef FormToTest As Form, _
Optional ByRef bIsVisable As Boolean = False) As Boolean
Dim lErrorNum As Long
bIsVisable = False
On Error Resume Next
bIsVisable = NewFormClone.Visible
lErrorNum = Err.Number
On Error GoTo 0
If (lErrorNum = 0) Then
IsFormLoaded = True
Else
IsFormLoaded = False
End If
End Function
Guess it doesn't really mater who answers the question as long as it is answered and the next guy/gal can use it! :)
I will leave this open for a bit and if nobody finds a better answer I will mark this as it...
I like your answer. As for the loop/wait idea? A better way is to always include a reference in each form. I useally declare form module variable called frmPrevious.
Create instance of form
Instance.frmPrevious = me
So now we have the form "call" some code when the form is closed in place of some "visible" + looping code setting.
So in the close code of the form we have:
frmPrevious.FunctionCodeToRun
The above solves a good many issues, but one is you don't need dialog (which as you note cannot use) And you also dump the need for writing "loop + wait" code from the calling code.
This does however mean that your code continues in a new function in the calling form. I thus usually place that function right below the calling code in the calling form. I also tend to use a standard name for that function. I find this trade off worth it as opposed to loop/wait and continuing in the same code routine (I do agree this "continue" in code is often preferable, but then again having to write looping and wait code is not really that clean).
Try this also
Function IsLoaded(strFrmName As String) As Boolean
' Determines if a form is loaded.
Const conFormDesign = 0
Dim intX As Integer
IsLoaded = False
For intX = 0 To Forms.Count - 1
If Forms(intX).FormName = strFrmName Then
If Forms(intX).CurrentView <> conFormDesign Then
IsLoaded = True
Exit Function ' Quit function once form has been found.
End If
End If
Next
End Function
You can call the above function it in your project like so
If Not isLoaded("MyForm") Then
MsgBox "MyForm is Not Loaded"
End If
First off, I'm using Access 2000 and DAO. I have code that executes a simple INSERT INTO query that I call using db.Execute. This code works fine inside an mdb. However, if I compile into an mde then I get
error 5 - Invalid procedure call or argument
on this line and the record does not get inserted. However, if I change from db.Execute to DoCmd.RunSQL using the exact same SQL statement the record is inserted with no problems. Does anyone know why the DAO Execute method of the DAO database object would suddenly stop working once I compile into an MDE?
Note: I only get the error if I specify the dbFailOnError option of the .Execute method. If I leave that option off, I get no error but the record is still not inserted.
EDIT:
This line fails in the MDE (but works fine in the MDB):
App.db.Execute InsertSQL, dbFailOnError
From the immediate window with a breakpoint on the above line of code:
?InsertSQL
INSERT INTO Changes
(PropertyID, FieldID, [Which], [When], [Before], Reason, ReportChange)
VALUES (1, 2, "M", #2/19/2010 4:51:44 PM#, "Suite 2; 430 W KING ST; ABBOTTSTOWN, PA 17301-9771", "Per Owner", True)
(I have an entire class module dedicated to building and executing SQL statements, so it's not really practical to show exactly how I built the InsertSQL string variable. However, I really don't think that is relevant.)
This line works everywhere:
DoCmd.RunSQL InsertSQL
EDIT: App.db definition (note that there is no reference in my project to ADO, only DAO):
Public App As New clsApp
clsApp class module (relevant lines only):
Private m_objDB As Database
Public Property Get db() As Database
Set db = m_objDB
End Property
Private Sub Class_Initialize()
Set m_objDB = CurrentDb
End Sub
Private Sub Class_Terminate()
Set m_objDB = Nothing
End Sub
If you are curious, I use App.db rather than CurrentDB for two main reasons: 1) slight performance gain by not having to call the CurrentDB function repeatedly (call it once then just refer to the object it returns) and 2) properties of the database object like .RecordsAffected always return relevant information. Plus, it's faster to type. And I'm a programmer, so I'm inherently lazy.
EDIT: Let me first apologize to those who have been following this thread and trying to help me. It seems I may have left out the critical details of my problem. The App.db.Execute call takes place inside a class module (clsSQL) and it references a global variable named App which is itself an instance of a different class module (clsApp). I'm guessing the problem is that I am referring to an instance of a class module from within another class module. Can anyone confirm if calling one class module from within another is something that is supported by MDBs and not MDEs? [It is not an issue. I was way over-thinking this. See my answer for the full story.]
Chances are you are using a forms control reference inside the string you are passing to the Execute statement. That is me!ControlName is inside the quotes. Change it so it's outside the quotes. That is change ".... SELECT me!ControlName As Field1, Field2, Field3 ..." to ".... SELECT " & me!ControlName & " As Field1, Field2, Field3 ..."
Thus VBA can convert the value of the control name properly. Docmd.RunSQL does this for you. Execute does not.
Firstly, my sincere apologies to those who tried to help me with this. As I find so frustrating when others ask questions, I left out the key piece of information because I did not think it was relevant. Here was the full initialize procedure for my clsApp class module:
Private Sub Class_Initialize()
Application.Echo True
m_bEcho = True
Set m_objDB = CurrentDb
m_sUser = GetUserName
Set m_objStatus = New clsStatus
m_sPgmName = Application.CurrentProject.Name
m_sPgmName = Left(m_sPgmName, InStr(m_sPgmName, ".mdb") - 1)
End Sub
The key line is that last one. When I compiled into an ".mde", the string ".mdb" was no longer present in the CurrentProject.Name. As a result, the call to InStr() returned 0. And 0 - 1 = -1. So I was passing a negative number as the Length parameter for the Left() function. However, the length parameter cannot be negative. It is most definitely an "Invalid procedure call or argument."
My simple fix was to remove the "mdb" from ".mdb" and just check for the location of the "." This also works correctly with the ".accdb" and ".accde" 2007 extensions.
Again, my apologies.
What version of DAO do you have in your references? It might be that the older version is working fine on the mdb but fails on the mde. Try setting this to the latest version and compile it again