VBA Automation Error - "server [not server application]" upon closing form - vba

I've tried creating a form in the object-oriented manner as shown in this answer: https://stackoverflow.com/a/38382104/4460023. Upon closing the form, I'd like to refer to the object property IsCancelled to check if the calling subroutine should continue executing. However, when I check this property outside of the form, I run into the following error:
"Run-time error '-2147418105': Automation error. The callee (server
[not server application]) is not available and disappeared; all
connections are invalid. The call may have executed."
I'm guessing this has something to do with the form being closed. As an alternative solution, I simply write to a global variable stored within the calling sub's module. Ideally though, I'd like to use the property within this form object. My code is included below:
Within the form:
Private cancelling As Boolean
Public Property Get IsCancelled() As Boolean
IsCancelled = cancelling
End Property
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
cancelling = True
End If
End Sub
And then within the calling subroutine:
Set frm = New ViewByWorkerForm
frm.Show
If frm.IsCancelled Then 'error happens here
Exit Sub
End If
Note that I have other string properties within the class that I can use when I do not close the form - it's only the form closing that triggers this problem.

To fix your code
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True
End If
Hide
cancelling = True
End Sub

For me, I don't have a "red x" (I hide it) and I didn't want to use Booleans. I call my UserForms like so via an Event:
Public Sub UserForm_Sub()
'Application.ScreenUpdating = False
Set UserFormUserForm = New UserFormUserForm
On Error GoTo ErrorHandler
UserFormUserForm.ShowUserFormUserForm
GoTo Continue
ErrorHandler:
ModelessFormShowing = False
Continue:
End Sub
It was a simple matter of utilizing Excel's Error Handler in my case. You might be find w/ On Error Resume Next, but in my case, I needed to set a ModelessFormShowing boolean to false if form errored during launch.

Related

DoCmd.CancelEvent / Cancel = True Usage

Can either of DoCmd.CancelEvent or Cancel = True be run with effect from a procedure called by an event procedure whose event can be cancelled? E.g.,
Private Sub mCbo_DblClick(Cancel As Integer)
DoThis
End Sub
Private Sub DoThis()
Foo
{ Cancel = True | DoCmd.CancelEvent }
Bar
End Sub
I.e., will the Cancel = True / DoCmd.CancelEvent statement in DoThis() cancel the mCbo.DblClick event or is the statement effective only in the event procedure itself? The former would seem to be in scope in a broad sense, at least, but this isn't clear.
Secondarily, wherever the statement must appear, will subsequent code execute? I.e., will DoThis() execute Bar or will the statement effectively operate as Exit Sub, immediately ceasing any further execution?
This would seem straightforward but an hour or two Googling around hasn't turned up anything on point. Many thanks for any constructive thoughts.
You can easily do this by passing the cancel parameter by reference:
Private Sub mCbo_DblClick(Cancel As Integer)
DoThis Cancel
End Sub
Private Sub DoThis(ByRef Cancel As Integer)
Foo
Cancel = True
Bar
End Sub
And, of course, Bar is still ran. You could use Exit Sub if you want to prevent it from running. The Cancel parameter is just an integer that's used after all event handlers have ran to determine if the default behaviour should be cancelled. Setting it in a procedure doesn't do anything immediate.

How to check if a userform is closed with "X" Windows button?

There is a sub, it creates a CourtForm userform and then takes a data from it. The problem appears when said form is closed prematurely, by pressing "X" window button and I get a runtime error somewhere later. For reference, this is what I'm talking about:
In my code I tried to make a check to exit sub:
Private Sub test()
'Create an exemplar of a form
Dim CourtForm As New FormSelectCourt
CourtForm.Show
'The form is terminated at this point
'Checking if the form is terminated. The check always fails. Form exists but without any data.
If CourtForm Is Nothing Then
Exit Sub
End If
'This code executes when the form proceeds as usual, recieves
'.CourtName and .CourtType variable data and then .hide itself.
CourtName = CourtForm.CourtName
CourtType = CourtForm.CourtType
Unload CourtForm
'Rest of the code, with the form closed a runtime error occurs here
End Sub
Apparently the exemplar of the form exists, but without any data. Here's a screenshot of the watch:
How do I make a proper check for the form if it's closed prematurely?
Add the following code to your userform
Private m_Cancelled As Boolean
' Returns the cancelled value to the calling procedure
Public Property Get Cancelled() As Boolean
Cancelled = m_Cancelled
End Property
Private Sub UserForm_QueryClose(Cancel As Integer _
, CloseMode As Integer)
' Prevent the form being unloaded
If CloseMode = vbFormControlMenu Then Cancel = True
' Hide the Userform and set cancelled to true
Hide
m_Cancelled = True
End Sub
Code taken from here. I would really recommend to have a read there as you will find a pretty good basic explanation how to use a userform.
One of the possible solutions is to pass a dictionary to the user form, and store all entered data into it. Here is the example:
User form module code:
' Add reference to Microsoft Scripting Runtime
' Assumed the userform with 2 listbox and button
Option Explicit
Public data As New Dictionary
Private Sub UserForm_Initialize()
Me.ListBox1.List = Array("item1", "item2", "item3")
Me.ListBox2.List = Array("item1", "item2", "item3")
End Sub
Private Sub CommandButton1_Click()
data("quit") = False
data("courtName") = Me.ListBox1.Value
data("courtType") = Me.ListBox2.Value
Unload Me
End Sub
Standard module code:
Option Explicit
Sub test()
Dim data As New Dictionary
data("quit") = True
Load UserForm1
Set UserForm1.data = data
UserForm1.Show
If data("quit") Then
MsgBox "Ввод данных отменен пользователем"
Exit Sub
End If
MsgBox data("courtName")
MsgBox data("courtType")
End Sub
Note the user form in that case can be closed (i. e. unloaded) right after all data is filled in and action button is clicked by user.
Another way is to check if the user form actually loaded:
Sub test()
UserForm1.Show
If Not isUserFormLoaded("UserForm1") Then
MsgBox "Ввод данных отменен пользователем"
Exit Sub
End If
End Sub
Function isUserFormLoaded(userFormName As String) As Boolean
Dim uf As Object
For Each uf In UserForms
If LCase(uf.Name) = LCase(userFormName) Then
isUserFormLoaded = True
Exit Function
End If
Next
End Function

How to pass boolean value from UserForm to Sub?

I am trying to close a UserForm if a person clicks the red x in the upper right hand corner. Here is my code so far.
Public Sub Worksheet_BeforeDoubleClick(ByVal target As Range, Cancel As Boolean)
If target.Column = 10 Then
UserForm2.Show
etc...
Now, the Form opens and I run this code...
Public Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = vbFormControlMenu Then
If Not ExitAsk = vbYes Then Cancel = True
End If
End Sub
Public Function ExitAsk() As VbMsgBoxResult
Dim Smsg As String
Smsg = "Are you really want to exit? Click Yes to terminate or No to Continue."
ExitAsk = MsgBox(Smsg, vbYesNo + vbDefaultButton2 + vbQuestion, "Exit!")
End Function
Then, focus goes back to the Sub, and the code continues to run through everything, which causes some problems for me. I want to click the red x and close the UserForm and exit the Sub. It seem like the Sub and UserForm don't communicate, even though both are declared a Public. I must be missing something simple, but I'm not sure what. Any ideas, anyone?
Thanks!
It seem like the Sub and UserForm don't communicate, even though both are declared a Public
Accessibility has nothing to do with whether a procedure communicates with a form. A form is an object, not very different from a Range or a Collection - except it has a designer and a default instance: it won't "communicate" with your procedure without you telling it how to do that.
First, stop using the default instance and treat the form as you would any other object: New it up!
With New UserForm2 'object instance starts existing here...
.Show 'vbModal is implicit
End With '...and dies here
Now if you want the calling code to know how the form was closed, you need to expose something that the calling code can access to know that.
That's best done with a property. You could also expose a public field, but then the calling code would be able to tamper with it and you don't want that - that's what encapsulation does:
Private isCancelled As Boolean
Public Property Get Cancelled() As Boolean
Cancelled = isCancelled
End Property
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = vbFormControlMenu Then
isCancelled = True
End If
Cancel = True
Me.Hide
End Sub
Notice Cancel = True and Me.Hide: without cancelling the close, the object gets destroyed immediately and you lose its state. So you want to Hide the form instead of unloading/destroying it.
Only the form's code-behind can access isCancelled, but the calling code can read the Cancelled property (but not write to it).
With New UserForm2 'object instance starts existing here...
.Show vbModal 'execution in this procedure will resume after form is closed
If .Cancelled Then
'form was X'd out
End If
End With '...and dies here
So... not sure what you're trying to achieve exactly, but you'll want something along these lines.
In UserForm you can define your own public Get-property e.g. CloseModeInfo which will return value of private member which can be set in UserForm_QueryClose. Value of this public property can be then tested later. According to value in this property the calling code will decide what to do. HTH
UserForm
Private m_closeModeInfo As Integer
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
m_closeModeInfo = CloseMode
If CloseMode = vbFormControlMenu Then
If Not ExitAsk = vbYes Then Cancel = True
End If
End Sub
Private Function ExitAsk() As VbMsgBoxResult
Dim Smsg As String
Smsg = "Are you really want to exit? Click Yes to terminate or No to Continue."
ExitAsk = MsgBox(Smsg, vbYesNo + vbDefaultButton2 + vbQuestion, "Exit!")
End Function
Public Property Get CloseModeInfo() As Integer
CloseModeInfo = m_closeModeInfo
End Property
Worksheet Code
Private Sub Worksheet_BeforeDoubleClick(ByVal target As Range, Cancel As Boolean)
If target.Column = 10 Then
Dim frm As UserForm2
Set frm = New UserForm2
UserForm2.Show
If frm.CloseModeInfo = vbFormControlMenu Then
Unload frm
' I want to click the red x and close the UserForm and exit the Sub:
Exit Sub
End If
End If
End Sub

How to show userform 1 time only

In VBA for excel, I have a userform then I want this to show only for 1 instance. Even if the user re-open it, it won't open again. Is there any code for it? well, I'm also using this code for my login:
Private Sub UserForm_Initialize()
User.Caption = Environ("Username")
End Sub
I'm thinking if i can use this code in my problem. Hoping for a quick response. thanks guys, you're awesome!
Yes, it's possible.
You have to add new sheet. In a cell A1 type 0 (zero), then hide it. In a code which calls UserForm, use this:
Sub ShowMyForm()
If ThisWorkbook.Worksheets("HiddenSheet").Range("A1")=0 then MyUserForm.Show
End Sub
In a form:
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
ThisWorkbook.Worksheets("HiddenSheet").Range("A1")=1
ThisWorkbook.Save()
DoEvents
End Sub
If you don't want to add an extra sheet just to store one bool, you can set a custom document property like this:
Private Sub Workbook_Open()
On Error Resume Next
Dim test As Boolean
test = Me.CustomDocumentProperties("UserFormShown").Value
If Err.Number = 0 Then Exit Sub
UserForm1.Show
Me.CustomDocumentProperties.Add "UserFormShown", False, msoPropertyTypeBoolean, True
End Sub
If the property hasn't been set yet it will throw an error, so trapping an error lets you know if you've set the property (and shown the form).

Detect which save function the user chooses in Office2007 using Word VBA

I'm writing a custom Word template which does some processing upon saving the current document.
In Office2007, to save a document you can use the Save and the Save As functions, and handle the events with the FileSave and FileSaveAs macros.
But when the user hovers over the SaveAs option, other sub-options are displayed: Save as a document, as a Word template, as Word 97-2003 document, etc. These sub-options don't seem to have their own events, but I'd like to know when the user uses them.
So I came up with the idea to use the DocumentBeforeSave event, but then I still have to figure out if the save occured with the standard Save/SaveAs options or with the sub-options.
I thought about setting a variable to True in the Save/SaveAs functions, which the DocumentBeforeSave event would check to see if one of the normal save methods occured, then it would set the variable back to False.
But after experimenting with different methods, I can't figure out how I can pass the value of a variable between ThisDocument and the Class Module which has the BeforeSave event.
Any ideas? Thanks!
Edit: Example code that doesn't work:
ThisDocument:
Public pSSave As Boolean
Public Property Get SSave() As Boolean
SSave = pSSave
End Property
Public Property Let SSave(Value As Boolean)
pSSave = Value
End Property
Sub FileSave()
Me.SSave = True
If SSave = True Then
MsgBox "True"
End If
Application.ActiveDocument.Save
If SSave = True Then
MsgBox "True"
End If
End Sub
Class Module:
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
If Application.ActiveDocument.SSave = False Then
MsgBox "False"
End If
End Sub
The class module registration is done properly, I won't paste the code her.
The result displayed is True, False, True while theoretically, it should be True, True.
I still miss something in your logic. In comments I thought about different-reverse logic which would go this way. This code below is a mix of my way and code you presented.
Class Module
Public WithEvents App As Word.Application
Public pSSave As Boolean 'your class variable/property
Private Sub App_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
If pSSave = False Then
MsgBox pSSave
Else
MsgBox pSSave
End If
End Sub
Module1
'class initialization
Public wrdAPP As New myClass
Sub set_References()
Set wrdAPP.App = Application
End Sub
ThisDocument Module
Private Sub Document_Open()
'to initialize public variable when open
Call Module1.set_References
End Sub
Sub FileSave()
wrdAPP.pSSave = True
Application.ActiveDocument.Save
If wrdAPP.pSSave = True Then
MsgBox "True"
End If
End Sub
I don't know which way you are going to run FileSave sub. But after it is run it pass the value to class property which you could check in your event.
Hope it would help you anyway.