I added a function to get the Active Directory user login, for an Access DB using VBA, but I'm not sure why I don't see my function listed in the Expression Builder
I defined the functions like in this question but I can not see the function in the Expression Builder. I plan to use this function to fill an invisible txtBox on my form and log it into db.
Public Function GetUser(Optional whatpart = "username")
Dim returnthis As String
If whatpart = "username" Then GetUser = Environ("USERNAME"): Exit Function
Set objSysInfo = CreateObject("ADSystemInfo")
Set objUser = GetObject("LDAP://" & objSysInfo.USERNAME)
Select Case whatpart
Case "fullname": returnthis = objUser.FullName
Case "firstname", "givenname": returnthis = objUser.givenName
Case "lastname": returnthis = objUser.LastName
Case Else: returnthis = Environ("USERNAME")
End Select
GetUser = returnthis
End Function
I think you're asking how to have custom functions show up in the Expression Builder? (and therefore unrelated to Active Directory or logins).
It's most likely a simple case of either:
- you haven't saved the module since pasting the functions in, or,
- the functions are not located in a public module.
To make sure they're saved:
1. Close Access completely (all open databases), and then,
2. Re-open the database, and,
3. Open your module (from the "Modules" section of the Navigation Bar:
...and confirm your code is there.
Then try the expression builder again.
If the module is not showing in the Modules section, then your code is not in a public module.
Note that you don't have to use the Expression Builder. I've actually never used it, but I just tried it and once the module with my functions was saved, they showed up under:
Functions → My database name → My module name.
This screenshot shows how my function wasn't listed i the Expression Builder until I saved it, and then how I found it in the Builder:
Related
I've prepared a standard module, MyPath, which contains a few useful functions. In this case standard module is better for me than a class module.
Much more comfortable to write:
Dim ext As String
ext = MyPath.getFileExtension("test.docx") ' returns "docx"
Instead of:
Dim Pth As MyPath
Set Pth = New MyPath
Dim ext As String
ext = Pth.getFileExtension("test.docx")
or something like
Dim ext As String
With New MyPath
ext = .getFileExtension("test.docx")
End With
The only problem with my "pseudo-static-class" module is its scope (and IntelliSense use).
Dim ext As String
ext = getFileExtension("test.docx") ' This works, but I don't want it to
What I would like to achieve is:
If one doesn't specify MyPath when calling a function, IntelliSense does not suggest the methods from the module and they cannot be used.
In order to use any methods from the MyPath module, one needs to type in MyPath., and after the dot IntelliSense should suggest methods from the module.
I've tried some combinations with Option Private Module, Private functions, but no one works the way I described.
I am preparing a lot of classes and similar modules and I would keep everything in order.
edit
In short, I would like to force the compiler to throw an error if I don't specify the "parent" module for the function or sub. At the same time, if I type the name of my module I would be able to use IntelliSense (no solutions with run/call, just simply type module name, dot and select the member).
Another solution, inspired by VBA's Err object, is to implement a function that returns a reference to your "static object"
Public Static Function MyPath() As PathObject
Dim result As PathObject
If result Is Nothing Then Set result = New PathObject
Set MyPath = result
End Function
Where PathObject is the class that contains all your pseudo static methods.
This approach has the added bonus that the caller cannot set their MyPath reference to Nothing - in case you decide your Static module should be stateful (perhaps for some costly initialisation routine)
PS I think the Static Function syntax is cute, but you could equally just declare result with the static keyword
In fact you could possibly make it even more concise with
Public Static Function MyPath() As PathObject
Dim result As New PathObject
Set MyPath = result
End Function
Not tested, but should have the behaviour that MyPath is instantiated only when used for the first time, but thereafter hangs around indefinitely
The behavior you describe is actually achievable via a Predeclared Class, but will require a little prep work.
To make a predeclared class, you can write up a class with whatever functions you want. Then export it to text (.cls) and edit it, setting the Attribute VB_PredeclaredId to True. Then re-import it. My Class is called PredeclaredClass and looks like this in the .cls file:
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "PredeclaredClass"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Attribute VB_Ext_KEY = "Rubberduck" ,"Predeclared Class Module"
Option Explicit
Public Sub ThisIsVisible()
End Sub
After you import this class, you can now use its methods only by referencing the class module (and no new instance needed).
You will not see it in intellisense nor be able to use its function without the module reference. The compiler will complain, provided you are using Option Explicit
By the way, Rubberduck makes this pretty easy to do this without the need to export/import modules.
I'm a beginner so I'll try to be as clear as I can.I want to know if it is possible to run a custom application and have it return and store a result in a boolean,int or string in another application(the one that calls it) ? Basically I want it to behave like a function that returns a value but to another program that calls it instead. I would like to do it in VB.net. Something like this when it comes to using boolean :
a = Process.Start("C:\path_to\myapp.exe")
if (a) then
'execute
end if
At the simplest level, no, the design of the process mechanism doesn't inherently afford the ability to do what you're suggesting. The closest you could get is for the called process to exit with a particular return value, but that requires use of a particular API (GetExitCodeProcess) - not the return from the Start method you've illustrated here.
You can do such things as capture the output of the new process, which isn't a very robust solution, or create a temporary file that contains a value the "calling" process can read, which is even less robust. The other extreme would be to investigate specific techniques for interprocess communication.
If you could expand a bit on your problem, a more specific set of possible solutions could be offered. If your value of interest is being generated by a library or some shared code, that might afford a much more suitable return mechanism.
Here's an example of ConsoleApplication :
Imports System.IO
Module Main
Sub Main()
Dim Output As Boolean = File.Exists(My.Computer.FileSystem.SpecialDirectories.Desktop & "\file.txt")
Console.WriteLine(Output)
End Sub
End Module
And how to read the output (stdout) of the previous console application (this function returns a Boolean) :
Public Function GetOutput(executable As String) As Boolean
Using p As New Process With {.StartInfo = New ProcessStartInfo With {
.CreateNoWindow = True,
.FileName = executable,
.RedirectStandardOutput = True,
.WindowStyle = ProcessWindowStyle.Hidden,
.UseShellExecute = False}}
p.Start()
Dim output As String = p.StandardOutput.ReadToEnd()
p.WaitForExit()
Return CBool(output.Trim)
End Using
End Function
You can now do what you wanted to do by using this :
a = GetOutput("C:\path_to\myapp.exe")
If a Then
'execute
End If
See also (in C#) : How to make a Main Method of a console based application to return a string type.
How to reference an object from Code(After of Page Setup, before References) ?
For example, given this class:
How can I use that object in Code pane, I tried this but it doesn't work:
Public Shared Function Test() As String
Test = "Hello " & m_Class1.SomeFunction()
End Function
I tried this too, not working too:
Public Shared Function Test() As String
Test = "Hello " & Code.m_Class1.SomeFunction()
End Function
Note, I don't have the assembly's source code, I cannot make my changes directly there
AFAIK you can't.
Whatever you write in the Code pane has to be static (Shared in vb), so you should wrap your "ClassLibrary2.Class1" in a static method and call it like ClassLibrary2.MyStaticClass.MyStaticMethod() (or in a new assembly, given that you can't recompile the original one)
Note that indeed you can call your instance m_Class1 instance from within the expression box of RS items like Code.m_Class1.SomeMethod()
In my previous question, How do I assign a value to a property where the property name is supplied at runtime in VBA?, I learned to use CallByName to set a property in a class at run time.
This time, however, I'm trying to figure out how to get an object at run time from a string.
For example, let's say I have a string with the following data: Worksheets("RAW DATA").Range("A1").QueryTable.
Here's what I might try to do where the data above is the input for strParam below:
Function GetObject(strParam As String) As Object
GetObject = SomeFunction(strParam)
End Function
In this case, GetObject should return a QueryTable when evaluated against Worksheets("RAW DATA").Range("A1").QueryTable. Is there anything in VBA that could take the place of SomeFunction from the example above?
Active Scripting Engine can help you. Instantiate ScriptControl ActiveX, use .AddObject() method to add reference to Excel's Application object to the script control's execution environment, set the third parameter to True to make all Application's members accessible too. Then just use .Eval() method to evaluate any property or method, which is the Application's member. The example below shows evaluation of Worksheets() property:
Sub TestQueryTable()
Dim objQueryTable As QueryTable
Dim strEvalContent As String
strEvalContent = "Worksheets(""RAW DATA"").Range(""A1"").QueryTable"
Set objQueryTable = EvalObject(strEvalContent)
objQueryTable.Refresh
MsgBox objQueryTable.Connection
End Sub
Function EvalObject(strEvalContent As String) As Object
With CreateObject("ScriptControl")
.Language = "VBScript"
.AddObject "app", Application, True
Set EvalObject = .Eval(strEvalContent)
End With
End Function
If you are on 64-bit Office, this answer may help you to get ScriptControl to work.
This time you're out of luck. There is no VBA equivalent of eval (not in Excel anyway...there is in Access VBA).
(Application.Evaluate() evaluates strings as Excel expressions, not as VBA code.)
There's the "Evaluate" method (or [ ] brackets). I don't think it will do exactly what you expect - as in run VBA code found in a string. You can look it up in the VBA help menu.
I have a VBA function that initializes a lotus notes session, creates a document and mails it. It also accepts as an optional parameter a NotesRichTextItem which I append to the body of the email.
However, I am getting the error message "All objects must be from the same session". How do I 'import' this NotesRichTextItem into my session?
Edit-Code added
Sub SendLotusMail(SubjTxt As String, _
BodyTxt As String, _
EmailTo As String, _
EmailCC As String, _
AutoSend As Boolean, _
Attach As String, _
ReportTitle As String, _
Optional AppendToBody As NotesRichTextItem = Null)
On Error GoTo EH
NtSession.Initialize
OpenMailDb ReportTitle
Set NtDoc = Ntdb.CreateDocument
NtDoc.AppendItemValue "Form", "Memo"
NtDoc.AppendItemValue "SendTo", EmailTo
NtDoc.AppendItemValue "CopyTo", EmailCC
NtDoc.AppendItemValue "Subject", SubjTxt
Set NtBodyRT = NtDoc.CreateRichTextItem("Body")
NtDoc.AppendItemValue "Body", NtBodyRT
If Attach <> "" Then NtBodyRT.EmbedObject EMBED_ATTACHMENT, "", Attach, "Attachment"
NtBodyRT.AppendText BodyTxt
'This next line throws the error "All objects must be from the same session"
NtBodyRT.AppendRTItem AppendToBody
Edit-Solution found
I don't like it very much, but I got around all these issues by passing the RichTextItem object, it's parent NotesDocument, and it's parent's parent NotesSession to this function. So, now I'm calling this procedure with 3 optional objects instead of 1. Hooray.
Edit-New Solution found
Well, the previous solution was causing me problems, so until I find (or someone suggests) a workaround, I'll just use some custom email procedures for the reports that require it. It does duplicate some code, but not significantly.
The issue may be the fact that the NtSession object is being re-initialized in your sub. If the calling routine sends in a rich text item, I am assuming it must have created and initialized a NotesSession as well. If that's the case, you would want your code to re-use that same session. It looks like NtSession is a global - if that's the case, you could:
Enforce that the calling routing always have initialized that global session;
Optionally pass in a NtSession object as an argument (and your code can check if that object is null before creating and initializing its own session); or
Before calling Initialize, check if NtSession already is initialized - to do that, you may be able to check an attribute and see if the object throws on error (non-tested code):
function isNotesSessionInitialized (ns)
on error goto err
dim sUser
sUser = ""
sUser = ns.commonUserName
err:
if (sUser = "") then
return false
else
return true
end if
end function
It would help to see some code here. I'll make a guess at what is happening, though.
In your VBA function, you'll need to create a new NotesRichTextItem object within your email. For instance:
Dim docMail as New NotesDocument(db)
Dim rtBody as New NotesRichTextItem(docMail, "Body")
Call rtBody.AppendRTItem(myRTparameter)
I imagine that should work without an error.
(I'm writing this to close out my question)
I've gotten around this issue by just having separate email procs for the reports that require custom setups. Yes, there is some duplication of code, but it's far better than the behemoth I was about to make.