VBA Modules and passing arguments - vba

I have a module that can be called from any part of my application to check if a particular font is installed on the users system, and if not - install the font and check again before continuing
Main Applicaton
if RequiredFont.Run(strFontName) = FALSE Then '(error message and exit sub)
Module "RequiredFont"
Public Function Run(Font As String) As Boolean
if check(Font) = FALSE Then
Run = FALSE
Install(Font)
if check(Font) = TRUE Then Run = TRUE
Else
Run = TRUE
End If
Private Function Check(Font as String) as Boolean
'code to check the font exists on the users localmachine, returns true/false
End Sub
Private Sub Install(Font as String)
'code to install the font on the users localmachine,
End Sub
My first question is:
Is the best way to make arguments available to all functions and subs, to pass them through each time I call? (as shown above).... or is there a simple way to declare an argument as variable to the whole module when Run() is called?
My Second Question is:
Is there a way I can avoid Run() alltogether and just call the module name "RequiredFont" directly, I remember that in other languages, calling a sub by a certain name would automatically run that sub when the module is called
Thank You
EDIT - This is how my code looks now:
Private FontName As String
Private FontFile As String
Public Function Run(strFontName As String, strFontFile As String) As Boolean
FontName = strFontName
FontFile = strFontFile
Run = False
If CheckFont() = False Then InstallFont
If CheckFont() = True Then
Run = True
Else
'message error"
End If
End Function
Private Function CheckFont() As Boolean
'code to check if the font is installed
On Error Resume Next
'Create a temporary StdFont object
With New StdFont
' Assign the proposed font name
.Name = FontName
' Return true if font assignment succeded
If (StrComp(FontName, .Name, vbTextCompare) = 0) = False Then
CheckFont = False
Else
CheckFont = True
End If
End With
End Function
Private Sub InstallFont()
' code to install the font
MsgBox "You need the following font installed to continue." _
& vbNewLine _
& vbNewLine & "'" & FontName & "'" _
& vbNewLine _
& vbNewLine & "Click OK to launch the font. Please click the INSTALL button at the top"
OpenFile (PATH_TO_FONTS & FontFile)
End Sub

Using function arguments is a good coding practise, that way you know exactly what goes in and what goes out a function.
You can however use a global variable, which would be set once when Run is called and still be accessible to the other functions.
'could also be Private to hide it from other modules
Public myFont As String
Public Function Run(Font As String) As Boolean
myFont = Font
'...
End Sub
Private Function Check() as Boolean
' you can access myFont here
End Sub
Private Sub Install()
'idem
End Sub
Regarding your second question, I don't think you can.

You can declare optional functions, and set defaults:
Public Function fxMyFunction _
(Optional lngProj As Variant, _
Optional strFruit As Variant = "banana", _
Optional booTest As Boolean = False) As String
'' IsMissing requires that lngProj be a Variant
booNoProject = IsMissing(lngProj)
fxMyFunction = strFruit
End Function
The Optional arguments must follow non-optional arguments.
About functions that "run on inclusion"
You do have to call functions and subs by name. There is no "self-running function" feature for a VBA standard module. VBA "includes" all modules on compiling.
VBA class modules are where you will find the equivalent of constructors. Investing in VBA's version of object-orientation doesn't seem helpful for your current need. If you do go that direction, some aspects will start looking familiar to you (though perhaps just enough to get frustrating, as OO remains a feature that was added later and looks the part).

As #z states, you can use a global variable, although this is bad practice.
Regarding question 2, you can give your function a unique name and omit naming your module to run it, e.g.
findOrInstallFont(Fontname)

Related

Looking for a better way to set properties for form controls

I'm using the code below in many forms and several applications:
Private Sub EnableEdit(strFieldname As String, Optional bUseRed As Boolean = False)
Me.Controls(strFieldname).Enabled = True
Me.Controls(strFieldname).Locked = False
If Not Me.Controls(strFieldname).ControlType = acCheckBox Then
Me.Controls(strFieldname).BackStyle = 1
If bUseRed Then
Me.Controls(strFieldname).ForeColor = vbRed
Else
Me.Controls(strFieldname).ForeColor = vbBlack
End If
End If
End Sub
' and the calls e.g.
EnableEdit ("arHerstellerArtikelNr")
EnableEdit ("arEAN")
EnableEdit ("arArtikelNrIxxtraNice")
EnableEdit ("arSort")
A recent change showed me, that it would be much better to have the code NOT duplicated in each Class Object. First my problem doing so was, that the code contains 'Me.Controls(strFieldname)' to address the properties I want to manipulate. For this reason it had to be in the Class Object itself --> requiring duplication.
So I rewrote my code. The design goal was: a way to call 'EnableEdit' from the events of several forms, but supplying the code for this not in the forms module itself, but in a module or class module that is supplied just once for the application. I think the way access works, I need to pass an additional parameter to distinguish the forms. However if the name of the form needs to be passed as an additional parameter (e.g. EnableEdit ("frmInventoryUpdate", "arSort")) the code is less readable and more error prone. So I came up with the idea to pass the name as an invariant: 'EnableEdit (Me.FormName, "arSort")'. This led me to the following solution:
Public Sub EnableEdit2(strFormName As String, strFieldname As String, Optional bUseRed As Boolean = False)
Dim frmCurrentForm As Form
Set frmCurrentForm = Forms(strFormName)
frmCurrentForm.Controls(strFieldname).Enabled = True
frmCurrentForm.Controls(strFieldname).Enabled = True
frmCurrentForm.Controls(strFieldname).Locked = False
If Not frmCurrentForm.Controls(strFieldname).ControlType = acCheckBox Then
frmCurrentForm.Controls(strFieldname).BackStyle = 1
If bUseRed Then
frmCurrentForm.Controls(strFieldname).ForeColor = vbRed
Else
frmCurrentForm.Controls(strFieldname).ForeColor = vbBlack
End If
End If
End Sub
' and the calls e.g.
Call EnableEdit2(Me.FormName, "arHerstellerArtikelNr")
Call EnableEdit2(Me.FormName, "arEAN")
Call EnableEdit2(Me.FormName, "arArtikelNrIxxtraNice")
Call EnableEdit2(Me.FormName, "arSort")
The solution is acceptable for me, however I wonder if there is any way to suppress even the 'Me.FormName' in the call and thus making the calls nicer?
Never pass the form name, always pass the form:
Public Sub EnableEdit2(frmCurrentForm As Form, strFieldname As String, Optional bUseRed As Boolean = False)
frmCurrentForm.Controls(strFieldname).Enabled = True
frmCurrentForm.Controls(strFieldname).Enabled = True
frmCurrentForm.Controls(strFieldname).Locked = False
If Not frmCurrentForm.Controls(strFieldname).ControlType = acCheckBox Then
frmCurrentForm.Controls(strFieldname).BackStyle = 1
If bUseRed Then
frmCurrentForm.Controls(strFieldname).ForeColor = vbRed
Else
frmCurrentForm.Controls(strFieldname).ForeColor = vbBlack
End If
End If
End Sub
'Call it:
EnableEdit2 Me, "arHerstellerArtikelNr"
'Never use Call, that's a relic of very old code and not needed in modern code
Passing the form name causes additional overhead, goes wrong when your form is a subform, and goes wrong when you want to allow multiple instances of the same form (that all have the same name of course).
You always want to pass the form directly, both because it avoids these problems and because it's more concise.
For a more thorough rework, I'd avoid passing the field name, and would use the tag property instead to determine which fields need this enabling/disabling, given that fields don't need to be disabled or enabled independently.

Why is object reference destroyed prematurely?

I have created a simple VBA class with a parameterized constructor. The class has the VB_PredeclaredID=True. This development is being done on a Mac in Office 365. The code is below. (The code is not bulletproof. I created this simple example to show the problem that showed up in a more complex class.) When the 5th line of the Make procedure is executed, the Class_Terminate handler is invoked for the object created in the 2nd line, i.e., the one controlling the "with" block. Class_Terminate crashes on exit with an overflow error. (On my more complex example, the error is "with without end.") I've planted debug so I know the Birthday property is never called in line 5. Can someone explain to me what in my code is causing the system to want to destroy the object reference when it is still in use, and how I can work around it? Thanks.
Sub TestClass()
Dim cl As CTest
Set cl = CTest.Make(DateValue("12/6/1946"))
Debug.Print "TestClass", IIf(Not cl Is Nothing, cl.Birthday, "Nothing")
End Sub
Private m_birthday As Date
Private m_otherdata As Variant
Private Sub Class_Initialize()
Debug.Print "Enter Initialize"
If Me Is CTest Then
m_birthday = DateValue("1/1/1800")
Else
m_birthday = Now()
End If
Debug.Print "Exit Initialize", m_birthday
End Sub
Private Sub Class_Terminate()
End Sub
Public Function Make(varparam As Variant) As CTest
If Me Is CTest Then
With New CTest
Select Case VarType(varparam)
Case vbDate:
.Birthday = varparam
Case vbObject:
.Birthday = varparam.Birthday
End Select
Set Make = .Self
End With
ElseIf varparam Is Nothing Then
With New CTest
.Birthday = Me.Birthday
If (VarType(Me.OtherData)) = vbObject Then
Set .OtherData = Me.OtherData
Else
.OtherData = Me.OtherData
End If
Set Make = .Self
End With
Else
Set Make = Nothing
End If
End Function
Public Property Get Self() As CTest
Set Self = Me
End Property
Public Property Get Birthday() As Date
Birthday = m_birthday
End Property
Public Property Let Birthday(val As Date)
m_birthday = val
End Property
Public Property Get OtherData() As Variant
OtherData = m_otherdata
End Property
Public Property Let OtherData(val As Variant)
m_otherdata = val
End Property
Public Property Set OtherData(val As Variant)
Set m_otherdata = val
End Property
I created this simple example to show the problem that showed up in a more complex class
What's missing is code that consumes the class, and the code that actually reproduces the problem, but I wrote a lot of articles on this subject, so let's dig anyway.
Private Sub Class_Initialize()
Debug.Print "Enter Initialize"
If Me Is CTest Then
m_birthday = DateValue("1/1/1800")
Else
m_birthday = Now()
End If
Debug.Print "Exit Initialize", m_birthday
End Sub
A useful piece of information that you're not outputting, is whether the initializing instance is the default instance. Consider:
Debug.Print "Initializing " & TypeName(Me) & IIf(Me Is CTest, " (default instance)", vbNullString)
One problem is this:
If Me Is CTest Then
m_birthday = DateValue("1/1/1800") '<~
Else
m_birthday = Now()
End If
If the current instance is the class' default instance, the internal state is useless. Keeping the default instance stateless is key, in fact: m_birthday is an implementation detail as far as the class' default interface (CTest) is concerned. This would be a better guard clause:
If Me Is CTest Then Exit Sub
m_birthday = Now()
No more nesting, m_birthday is only assigned on a non-default instance, and the intent of keeping the default instance stateless is much more explicitly expressed.
Now, if you type this in the immediate pane:
Set a = New CTest
You'll get this output:
Initializing CTest (default instance)
Initializing CTest
You're missing this trace:
Private Sub Class_Terminate()
Debug.Print "Terminating " & TypeName(Me) & IIf(Me Is CTest, " (default instance)", vbNullString)
End Sub
In the Make factory method, you actually want an even stronger bail-out:
Public Function Make(varparam As Variant) As CTest
If Me Is CTest Then
'...
Consider:
Public Function Make(varparam As Variant) As CTest
If Not Me Is CTest Then Err.Raise 5, TypeName(Me), "Member call is only valid from default/predeclared instance."
And that removes a branch in the conditional path. It also makes me wonder about this:
ElseIf varparam Is Nothing Then
That condition gets evaluated when Me Is CTest is False, i.e. when the factory method is invoked from a user instance... and that should not be allowed to happen.
This is another problem:
Select Case VarType(varparam)
Case vbDate:
.Birthday = varparam
Case vbObject:
.Birthday = varparam.Birthday
vbObject means varparam is an Object reference - not that it's a CTest object: because we're working with a Variant, the member call is late-bound, so if the object doesn't have a Birthday member, we have run-time error 438 raised here. We can keep the member call late-bound but still validate the type:
Case vbObject:
If TypeOf varparam Is CTest Then .Birthday = varparam.Birthday
Or you can get compile-time validation by introducing a variable:
Case vbObject:
Dim typedParam As CTest
If TypeOf varparam Is CTest Then
Set typedParam = varparam
.Birthday = typedParam.Birthday '<~ early-bound member call now
End If
This not only helps the compiler pick up typos (even Option Explicit can't save you from a typo in a late-bound call), it also helps static code analysis tooling like Rubberduck, that now "see" the member call: if the member is renamed, refactoring tools can now update this call site - that's not easily possible with late-bound code.
Public Property Get Self() As CTest
Set Self = Me
End Property
That's syntax sugar that works nicely when there's an explicit interface involved, to cleanly separate the stateless CTest default instance from the ICTest explicit client interface (which could include a Property Get for the birthday, but no Let accessor).
Better syntax sugar that doesn't affect your classes' public interfaces and dramatically cleans up the locals toolwindow in class modules, is shoving the instance state into a Private Type:
Private Type TState
Birthday As Date
OtherData As Variant '<~ note: this breaks strong-typing and gets you back into late-bound land.
End Type
Private this As TState
This Private this instance (module-level) variable replaces all m_-prefixed variables, and now the Birthday property reads like this:
Public Property Get Birthday() As Date
Birthday = this.Birthday
End Property
Public Property Let Birthday(ByVal val As Date)
this.Birthday = val
End Property
...
So, the only convoluted piece of code that looks suspect, is the Make function, which is responsible for too many things.
Write a separate private function that works off a Date, another that works off a CTest object, and conditionally invoke the appropriate one from Make.
With functions that do fewer things, fewer things can go wrong.
Guard your methods - if a method involves instance state, prohibit invoking it from the default/predeclared instance. If a method is supposed to be invoked from the default instance, prohibit calling it from other instances.
See this article for a refresher on the pattern, and this one to see it in action with real code.
I have to doff my cap to #MathieuGuindon and the other chaps and chapesses at Rubberduck as my understanding of VBA has progressed immensely through reading the Rubberduck blogs.
I too have been through some interesting times using the PredeclaredId and therfore offer some of my thoughts on how the OP code should be constructed. As I am still developing my understanding of OOP in VBA folks should feel free to shoot me down in flames if I am wrong or misunderstanding things.
There are two things that I have developed from ideas presented in the rubberduck blogs.
This
I differentiate 'this' into p,s,b and u representing Type definitions of Properties, State,BaseInstance and Using.
Self
I take the construction of a Class instance a step further and pass the Make parameters to the Self method call. In this way the parameters can be used to set up private members of the new instance without the need for public properties.
Option Explicit
Sub TestCTest()
Dim myCTest As CTest
' no errors
Set myCTest = CTest.Make(DateValue("4/6/2020"))
Debug.Print myCTest.Birthday
On Error Resume Next
' Gives "CTest: Expecting Variant/Date or Variant/CTest: Found String"
Set myCTest = CTest.Make("4/6/2020")
Debug.Print Err.Description
On Error GoTo 0
On Error Resume Next
Dim myCtest2 As CTest
'Gives "CTest: Make should only be used with the PredeclaredId"
Set myCtest2 = myCTest.Make(DateValue("4/6/2020"))
Debug.Print Err.Description
On Error GoTo 0
On Error Resume Next
' Gives "New is not permitted outside of the Make Method" error
Dim myCtest3 As CTest
Set myCtest3 = New CTest
Debug.Print Err.Description
On Error GoTo 0
End Sub
Class CTest
Option Explicit
'#PredeclaredId
' Variables used as the private repositories for public properties are located here
Private Type Properties
Birthday As Date
OtherData As Variant ' OP may have a specific type in mind
' NewIsAllowed appears in every instance but we will only ever use
' the value in the predeclared Id to toggle if new is or is not allowed
' via the AllowNew property
NewIsAllowed As Boolean
End Type
Private p As Properties
' If any were present the State type would be used for variables representing
' the state of the instance but which are not intended to be made public through Properties
' Private Type State
' StateVar1 as Typename
' End Type
'
' Private s As State
'
' Used only for PredeclaredId to allow boilerplate code to be written
Private Type BaseInstance
PredeclaredId As CTest
End Type
Private b As BaseInstance
Private Sub Class_Initialize()
' This method runs the **first** time the **PredecalredID** is used in an expression
' and for every subsequent use of New. Therefore managing what happens for the PredeclaredId
' vs instances can become a bit Eulerish.
' Declaring b.predeclaredId allows us to boilerplate code elsewhere
' as it means that the only places that the actual class name is used
' is here ,the Type declaration above and other method declarations.
Set b.PredeclaredId = CTest
' The code to exit on the first use of the PredeclaredID in an expression
If Me Is b.PredeclaredId Then Exit Sub
' Trap the use of New when not used by the Make Function
' the code below means that bad code will be detected at testing time
If Not AllowNew Then
Err.Raise 445 + vbObjectError, TypeName(Me), TypeName(Me) & ": New is not permitted outside of the Make method"
End If
End Sub
Public Function Make(ByVal varparam As Variant) As CTest
' From the OP code we are expecting varparam to be either
' a Date , a CTest object or nothing
If InStr("Date,CTest,Empty,Null,Nothing", TypeName(varparam)) = 0 Then
Err.Raise 13 + vbObjectError, TypeName(Me), TypeName(Me) & ": Expecting Variant/Date or Variant/CTest: Found " & TypeName(varparam)
End If
' In the OP code it is not clear if the OP has
' restricted the use of the Make function to CTest.Make or
' allows the use of <instance>.Make.
' Both uses are legal as Make is a public method but
' in the spirit of declaring a PredecalredId it is
' preferable to restrict the use of Make to CTest.Make
' Thus the code below detects the use of Make by an instance.
If Not Me Is b.PredeclaredId Then
Err.Raise 445 + vbObjectError, TypeName(Me), TypeName(Me) & ": Make should only be used with the PredeclaredId"
End If
' Instruct the PredeclaredId that New is allowed
AllowNew = True
With New CTest
Set Make = .Self(varparam)
End With
' Instruct the PredeclaredId to disallow the use of new
AllowNew = False
End Function
Public Function Self(ByVal varparam As Variant) As CTest
' This code is inside the new instance that is being constructed.
' Therefore there is free access to the private variables of the
' instance 'under construction'
' Its a little difficult to untangle the OP logic for what constitutes
' the birthday so the Case statement below may well be incorrect
Select Case TypeName(varparam)
Case "Empty", "Null", "Nothing"
' The Me in the OP code occurs in the Make function and
' consequently refers to the instance of which Make was called.
' IF make was used as discussed above this implies that Me is b.PredecalredId
' only if the OP has adhered to CTest.Make
' If this is the case????
p.Birthday = DateValue("1/1/1800")
' The OP assigns otherdata in the case of nothing
' using Me.Otherdata. The Me will now refer to the
' the instance under construction so it is likely that a second
' parameter will be required for the Make function
'
Case "Date"
p.Birthday = CDate(varparam)
Case "CTest"
Dim myCTest As CTest
Set myCTest = varparam
p.Birthday = myCTest.Birthday
Case Else
Err.Raise 13 + vbObjectError, TypeName(Me), TypeName(Me) & ": Expecting Variant/Date or Variant/CTest: Found " & TypeName(varparam)
End Select
Set Self = Me
End Function
' The alternative to the AllowNew property is to have a public AllowNew field.
' but as the code below is bolerplate and can be copied to new classes without issue
' I'm happy to use the code below.
' Due to the differentiation of p,s,b
' we have an easily identifiable warning to check if we
' see anything but the p. prefix in Property declarations.
Public Property Get AllowNew() As Boolean
If Me Is b.PredeclaredId Then
AllowNew = p.NewIsAllowed
Else
AllowNew = b.PredeclaredId.AllowNew
End If
End Property
Public Property Let AllowNew(ByVal Value As Boolean)
If Me Is b.PredeclaredId Then
p.NewIsAllowed = Value
Else
b.PredeclaredId.AllowNew = Value
End If
End Property
Public Property Get Birthday() As Date
Birthday = p.Birthday
End Property
Public Property Let Birthday(ByVal val As Date)
p.Birthday = val
End Property
Public Property Get OtherData() As Variant
OtherData = p.OtherData
End Property
Public Property Let OtherData(ByVal val As Variant)
p.OtherData = val
End Property
Public Property Set OtherData(ByVal val As Variant)
Set p.OtherData = val
End Property

How do I create a new form instance whose name I have in a string? [duplicate]

Code to create new form instance of a closed form using form name
I want to replace the long Select Case list with a variable.
Full code of module
In Access 2010 I have a VBA function that opens a new instance of a form when given a string containing the form's name. By adding a form variable "frm" to a collection:
mcolFormInstances.Add Item:=frm, Key:=CStr(frm.Hwnd)
The only way I can figure out to open "frm" is with a Select Case statement that I've manually entered.
Select Case strFormName
Case "frmCustomer"
Set frm = New Form_frmCustomer
Case "frmProduct"
Set frm = New Form_frmProduct
... etc ... !
End Select
I want it to do it automatically, somewhat like this (although this doesn't work):
Set frm = New Eval("Form_" & strFormName)
Or through some code:
For Each obj In CurrentProject.AllForms 'or AllModules, neither work
If obj.Name = strFormName Then
Set FormObject = obj.AccessClassObject 'or something
End If
Next
Set frm = New FormObject
I just want to avoid listing out every single form in my project and having to keep the list updated as new forms are added.
I've also done some testing of my own and some reading online about this. As near as I can tell, it isn't possible to create a new form object and set it to an instance of an existing form using a string that represents the name of that form without using DoCmd.OpenForm.
In other words, unless someone else can prove me wrong, what you are trying to do cannot be done.
I think you are looking for something like this MS-Access 2010 function. (The GetForm sub is just for testing):
Function SelectForm(ByVal FormName As String, ByRef FormExists As Boolean) As Form
For Each f In Application.Forms
If f.Name = FormName Then
Set SelectForm = f
FormExists = True
Exit Function
End If
Next
FormExists = False
End Function
Sub GetForm(ByVal FormName As String)
Dim f As New Form
Dim FormExists As Boolean
Set f = SelectForm(FormName, FormExists)
If FormExists Then
MsgBox ("Form Found: " & f.Caption)
Else
MsgBox ("Form '" & FormName & "' not found.")
End If
End Sub
Here's an ugly hack I found:
DoCmd.SelectObject <acObjectType>, <YourObjectsName>, True
DoCmd.RunCommand acCmdNewObjectForm
The RunCommand step doesn't give you programmatic control of the object, you'll have to Dim a Form variable and Set using Forms.Item(). I usually close the form after DoCmd.RunCommand, then DoCmd.Rename with something useful (my users don't like Form1, Form2, etc.).
Hope that helps.

Why am I getting an error when I try to print the contents of a file I am searching for?

Can you help me with searching for and printing a file specified by text in textbox1? I have the following code but textbox1 shows me an error. I don't know if the code is correctly written and functioning right.
First class:
Public Class tisk
'print
Public Shared Function printers()
Dim printThis
Dim strDir As String
Dim strFile As String
Dim Textbox1 As String
strDir = "C:\_Montix a.s. - cloud\iMontix\Testy"
strFile = "C:\_Montix a.s. - cloud\iMontix\Testy\" & Textbox1.text & ".lbe"
If Not fileexprint.FileExists Then
MsgBox("Soubor neexistuje")
printers = False
Else
fileprint.PrintThisfile()
printers = True
End If
End Function
End Class
Second class:
Public Class fileprint
Public Shared Function PrintThisfile()
Dim formname As Long
Dim FileName As String
On Error Resume Next
Dim X As Long
X = Shell(formname, "Print", FileName, 0&)
End Function
End Class
Third class:
Public Class fileexprint
Public Shared Function FileExists()
Dim fname As Boolean
' Returns TRUE if the file exists
Dim X As String
X = Dir(fname)
If X <> "" Then FileExists = True _
Else FileExists = False
End Function
End Class
When I fill a textbox with text, how can I search for a file in the computer using this text and print this file?
Your "Textbox1" is a variable and not actually getting the value from a textbox. If I'm not wrong, I believe you intend to get the value of a textbox and concatenate to form your directory url. You'll first need to add a text box to your windows/web form, give that textbox an id then call that id in your code behind. E.g. You add a text box with id "textbox001", in your code behind you'll do something like "textbox001.text". In your case it'll now be: strFile = "C:_Montix a.s. - cloud\iMontix\Testy\" & textbox001.text & ".lbe". Hope this helps.
Not sure this will fix your issue, but there are some poor practices in that code that are addressed below. This will certainly get you closer than what you have right now.
Public Class tisk
'print
Public Shared Function printers(ByVal fileName As String) As Boolean
Dim basePath As String = "C:\_Montix a.s. - cloud\iMontix\Testy"
Dim filePath As String = IO.Path.Combine(basePath, fileName & ".lbe")
If IO.File.Exists(filePath) Then
fileprint.PrintThisfile(filePath)
Return True
End If
'Don't show a message box here. Do it in the calling code
Return False
End Function
End Class
Public Class fileprint
Public Shared Sub PrintThisfile(ByVal fileName As String)
'Not sure how well this will work, but it has better chances than the original
Dim p As New Process()
p.StartInfo.FileName = fileName
p.StartInfo.Verb = "Print"
p.Start()
End Sub
End Class
One additional comment on the File.Exists() check. It's actually poor practice to check if the file exists here at all. The file system is volatile. It's possible for things to change in the short time between when you check and when you try to use the file. In addition, whether the file exists is only one thing you need to look at. There's also access permissions and whether the file is locked or in use. The better practice is to just try to do whatever you need with the file, and then handle the exception if it fails.

"User defined type not defined" when trying to define a new "process"

I am trying to redirect command line output to a list box in a vba macro, and I've found some code that I think might point me in the right direction, but I keep on getting the same error. When I use this code
Function ReadCmdOutput(ByVal applicationName As String,
Optional ByVal applicationArgs As String = "", Optional ByVal
workingDirectory As String = "", Optional ByVal showWindow As Boolean
= False) As String
Try
Dim processObj As New Process
processObj.StartInfo.UseShellExecute = False
processObj.StartInfo.RedirectStandardOutput = True
processObj.StartInfo.FileName = applicationName
processObj.StartInfo.Arguments = applicationArgs
processObj.StartInfo.WorkingDirectory = workingDirectory
If showWindow = True Then
processObj.StartInfo.CreateNoWindow = False
Else
processObj.StartInfo.CreateNoWindow = True
End If
processObj.Start()
processObj.WaitForExit()
Return processObj.StandardOutput.ReadToEnd
Catch ex As Exception
Return ""
End Try
End Function
It gives me the error in the title and highlights the first declaration line.
Question: What does it take to define a new "process".
Bonus points: Helping me with command line output redirect!
This can't be VBA since there is no Try/Catch in VBA or return statements. This looks like VB.NET. Either way, the compiler is telling you that there is currently no reference to any dll that contains the process object.
If this is VB.NET, you need to add Imports System.Diagnostics