TheSwamp
Code Red => VB(A) => Topic started by: ML on November 30, 2007, 04:19:54 PM
-
I know this "may" sound a little bizarre but has anyone ever written code that creates new sub modules?
For instance, if I have the user do something, like pick points, I want module 1 to store those points in a variable, then possibly use that info to write a new module, module 2 and so on.
It is kind of weird but in a sense, you can have the user actually creating the code for you, depending on their input or event.
Anyone have any examples of this sort of thing?
Thanks
Mark
-
Self modifying code is a bit of a black art, and generally speaking, easier in scripting languages like lisp, python etc.
However, little is impossible, so I'd start by referencing the Microsoft Visual Basic for Applications Extensibility library (e.g. C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB) and then examine the object model (F2).
For example, you might end up playing with something like this --
Dim component As VBComponent, _
subDef As String, _
QT As String
QT = Chr$(34)
subDef = "Sub Woot ()" & vbCrLf & vbCrLf & _
vbTab & "Call Msgbox ( " & QT & "Woot!" & QT & ") " & vbCrLf & vbCrLf & _
"End Sub"
Set component = VBE.ActiveVBProject.VBComponents.Add(vbext_ct_StdModule)
component.CodeModule.AddFromString subDef
In the interests of brevity / laziness I'm glossing over MANY details, including but not limited to checking if you've already added a code module, module naming, invoking techniques ... yada.
Have fun but don't hurt any small animals.
-
Hi MP
I have accessed the extensibility library before for one other thing in the past but I don't think that type library can be checked on via code; therefore, that may already defeat the purpose.
I don't want to have to tell each user that they have to go check that type library on, nor do I want to have to do it for them.
So, that idea (unless the library can be checked on via code) would probably not work too well.
You are right, my boss did something very interesting in LISP using wtf, write to file
When I saw what it did, I then said WTF (not write to file :)
It was pretty cool.
May be in VBA it is not as simple.
Of course, using VB Scripting language (in vba), we can use the text stream methods to write out and read into VBA.
So, we get the user input, write it out, then we would need a (hopefully) simpler way to create a new module and from there read it in.
Mark
-
If you load a dvd project that references libraries you don't have to re-reference said libraries; they be, ermmm, sticky.
-
That makes sense as there is now a reference to that particular library.
That is certainly a good piece of info
Thank you,
Mark
-
I don't write self modifying VB code (SMVBC) myself (tangent: while interesting, I think alternative methods can achieve similar results as SMVBC) so I can't help you out much beyond this.
Best of luck -- and you're most welcome.
:)
-
My two cents...
I do not see why you need to create new modules to store data for creating new data.
A text file, Excel sheet, Access database, Sql express Database, Oracle database, MS SQl database and even a binary file or files should do the job just as well and in the case of the databases retrieving data should be much easier and faster as well.
If you want to store data in memory for later use collections, arrays, dictionary's ar ussually the most obvious way.
And after this rant a question.... Can you explain why storage in a module is really needed and what advantage it would bring.
-
Hi Dnerb,
You rant was actually a very useful for :)
I agree; I am sure there are better methods of storing data and retrieving it later. If you read my post, just above MP's, I did say that I could use The VBScript Textstream method to write out to text files, then to read in when needed. Also, I have never tried the dictionary object methods for anything in VBA, do you have an example of how that might be useful to store data and retreive data?
The reason I want to do something like this is not totally clear yet.
I just had a few thoughts.
I saw someone write a LISP routine using The (wtf) Write to File method, they were retrieving points from where the user picked and some other data, then writing that info to another (probably .txt) file and creating a new Lisp file with it.
So, in essence, the user was actually creating the Lisp routines (dynamically) for him.
I just thought that was very interesting and could be useful for somethings.
Again, Lisp is more ASCII based and might have been better equipped then VB to do these sort of things.
I just wanted to toss it out there and see what people have to say.
We also have methods of dynamic arrays and Redim etc. in VB
So, the whole idea of doing user created modules may just not be a good one at all.
Thoughts?
Thank you,
Mark
-
Guess I didn't clue in to the impetus for this thread. If what you're talking about is simply data persistence VB/A is more than capable of addressing a your needs.
In addition to stream (sequential), random and binary file I/O you can also exploit extrinsic libraries like ADO et al.
From your description a simple csv file should suffice.
Like I said earlier, "while interesting, I think alternative methods can achieve similar results as self modifying code".
:)
-
MP
So you are saying, stream the input to a csv file?
I guess to better summarize the need or question (and I think you guys are addressing it) is data collection and storage basically.
So, let's just use points as our example:
We collect picked point data from the user, then we realize that we may want to use that info at a later time; how can we hold that info until it is needed? Again, it could be streamed out to a csv file, or something like that but may be there is a method to which VBA can hold it long term; then we can call that variable or whatever is holding the info up later when needed. ?
Mark
-
By "long term", do you mean later in the current session of AutoCAD, or three weeks from now?
-
How many points Mark?
Does any associated data have to accompany the point data, e.g. descriptors, dates etc?
Is the data drawing centric?
User centric?
Project centric?
Client centric?
Do you expect the magnitude of data to grow in time?
By how much?
Will the data be used by other applications? e.g. data collectors, total stations etc. Read "Should the data format honor existing industry standards?"
Are you sure?
...
/curiosity killed the cat
-
Hi Bob,
Yes, I don't really have a set time but 3 weeks is a good time frame to use.
Definitely not in this session. Once we close ACAD, the variables are cleared, correct?
So, I would like a way to hold the data indefinitely.
Then later, I can say:
With Saved data
Let's use it in this current module or project.
Mark
-
Wo wo wo LOL
MP, you are getting way ahead of me :)
I am just looking for the best method right now.
I said that we can use points as an example but I don't care what we use.
Whatever the data is, I am just looking for the best method.
I guess what you are getting at is, depending on what the data is, will determine the best method to use, correct?
Again, I am even ahead of myself.
I don't have a specific example or immediate need.
I can see a future need for this sort of thing, that is why I am askking.
Mark
-
The main reason to avoid creating modules and (Selfre-)generating code is the fact that it is one off the most virus like behaviors a virus scanner can imagine.
The lisp loophole to create a text file with actual Lisp and load it afterwards is one of the reasons Lisp is concidered to be very unsafe. it was of course developed in a time The internet was merely a private network between university's.
I do not think you have malicious intensions but it's imagineble a user of your code creating utility is, for instance when he is fired. So think thrice before busting all security measurments....
-
I would definitely write it to an external file then.
-
MP, you are getting way ahead of me
It may appear that way Mark but I'm trying to underscore the importance of identifying these things before you commit anything to code. You do want to minimize code maintenance issues as much as you can from the onset and asking these kinds of questions before you start is a lot cheaper than code / data file rewrites down the road. To answer your question "What is the best method" ... well it depends largely upon those kinds of questions being asked and answered. I'm not trying to be funny, it's the nature of the beast.
Subtitle: Everything you do should be done to a spec. So, work with me, let's define a simple spec to define your requirements. I'll start.
1. Drawing centric.
We'll use the current drawing name (if named), replacing the ".dwg" extension with ".points"
Example:
MyDrawing.points
2. Format is Point_ID,Point_Type,Point_Descr,Point_X, Point_Y, Point_Z
Point_ID = long integer
Point_Type = string, 2 characters (a coded integer would be more efficient but less self documenting)
"BS" = beginning of segment / curve
"ES" = end of segment / curve
"PS" = point on curve / segment
"AP" = arbitrary point
Point_Desc = string, max 32 characters.
Point_X = double precision
Point_Y = double precision
Point_Z = double precision
Example record:
42,"AP","Sample",1280.2370,988.3768,0.0000
3. File format must support comments
We'll define any line that starts with at least one semi colon (;) as being a comment line, ignored by the data retrieval code.
Example:
;; the following is a sample data file, created 2007/11/03
42,"AP","Sample",1280.237,988.3768,0.0
43,"AP","Yada",1261.4934,979.4054,0.0
44,"AP","Yada",1279.1810,970.9617,0.0
4. Data retrieval code must
• ignore blank lines
• ignore extra white space between fields
• process point_type case agnosticly
• qualify data; sanity check on unique Point_IDs, report records with bogus data (but not crash) etc.
• etc.
5. etc.
Notice, I'm not writing any code yet. That's because the spec is likely going to change. For example, making the data file drawing centric is probably not a good idea. Once the data is committed to entities any drawing association is likely moot -- so what is a better idea?
/Back to you
-
Hi MP,
I really do appreciate you defining all of those types for me; I did not even know what drawing centric meant LOL
If I had to take a stab at it, I might have guessed it was at drawing level or drawing related.
I would agree with Bob and to where I initially started that writing to an external file or data source may be the best way to go.
I have done this before a bit with writing to excel and text files using The textstream method and I have also read the data back in; I guess the next step might have been trying to assign that external data to a new variable and doing something (significant) with it. I think I successfully (in the past) looped (int erated) through my layers collection, wrote it out to a text file and was able to read it back in, then return the layers via a message box, which then was really cool. So, with this method, I can do it, I guess I just need to expound on it.
Also, you did touch on that method in your last paragraph
4. Data retrieval code must
• ignore blank lines
• ignore extra white space between fields
• process point_type case agnosticly
• qualify data; sanity check on unique Point_IDs, report records with bogus data (but not crash) etc.
• etc.
Your first two lines can be done with The VBScript, textstream method.
Thanks,
Mark
-
OK, while this example is nothing mind blowing, this method (using VB Scripting code in VBA) can be very useful:
In this example I am simply writing my Support and Search Paths to a text file called Supportpaths.txt
Sub WriteToATextFile()
'Write Support Paths to a text File
Dim WshNetwork
Dim FSO, MyFile, Username
Dim Allpaths As String
Set WshNetwork = CreateObject("WScript.Network")
Set FSO = CreateObject("Scripting.FileSystemObject")
Allpaths = Preferences.Files.SupportPath
Username = WshNetwork.Username
Set MyFile = FSO.CreateTextFile("C:\Documents and Settings\" & Username & "\Desktop\SupportPaths.txt", True)
MyFile.WriteLine Replace(Allpaths, ";", vbCrLf)
End Sub
Now, with this method, I am reading from the same text file (located on the desktop) and assigning the (paths) strings of text to a variable called Textstr and displaying the paths (variable) with a message box.
Sub ReadTextFile()
Dim FSO As Variant
Dim Stream As Variant
Dim Username As Variant
Dim Textstr As String
Set FSO = CreateObject("Scripting.FileSystemObject")
Set WshNetwork = CreateObject("WScript.Network")
Username = WshNetwork.Username
Set Stream = FSO.OpenTextFile("C:\Documents and Settings\" & Username & "\Desktop\SupportPaths.txt")
'Set FSO = Nothing
Textstr = ""
'While there are lines to read, continue on to the end of the file.
'If the file is empty, once we reach the end of the file, the variable Textstr will be returned as null.
Do While Not Stream.AtEndOfStream
Textstr = Textstr & Stream.ReadLine & vbCrLf 'or VbNewline
Loop
'Set Stream = Nothing
If Textstr <> "" Then
MsgBox Textstr
Else
MsgBox "The file you selected is empty"
End If
End Sub
Mark
-
OK, my challenge for you now is to do exactly what you have there without VB Scripting.
-
Adding to what Bob said, using solely intrinsic functions (no external libraries).
PS: In my opinion you should explicitly close all files you open for read or write.
:)
-
OK, so then we put
Myfile.close
Or in the second example
Stream.close
So, is the bottom line that the metohd I posted the preferrable way to handle what I am asking?
Thank you,
Mark
-
Here we go:
The answer was right in the help screen; I just tweaked it a bit.
This is a very basic example , however with so creativity, this can be very useful.
Mark
Sub CreateSub()
'This example uses the VBA IDE extensibility model to dynamically create a VBA subroutine.
Dim VBEModel As Object
Dim SubName As String
Dim EndOfSub As String
Dim CodeLn As String
'Get the VBE object
Set VBEModel = VBE
'Define and create new subroutine
SubName = "Sub Macro1 ()" & vbCrLf
CodeLn = "MsgBox ""This is a test""" & vbCrLf
CodeLn = CodeLn & "MsgBox Preferences.Profiles.ActiveProfile, vbApplicationModal" & vbCrLf
EndOfSub = "End Sub"
'Insert New Subroutine
VBEModel.CodePanes(1).CodeModule.InsertLines 1, SubName & CodeLn & EndOfSub
MsgBox "A new subroutine called " & SubName & "was added"
End Sub
-
With the above code:
Could anyone tell me how to create a Sub Macro2 () If Macro1 already exists? And so on..... ?
Thanks
Mark
-
Here is an example that works and does something:
You will be prompted with an input box; type anything in it and watch what happens.
On the same token, you could retrieve info from a user and start building a new sub with it.
I am still not sure how to append to an existing module or how to create Macro2 if 1 exists and so on.
I suppose there is probably a way to loop though all subs in a project then
If Macro1 exists
Create Macro2
End if
Anyone want to weigh in?
Mark
Sub CreateSub()
'This example uses the VBA IDE extensibility model to dynamically create a VBA subroutine.
Dim VBEModel As Object
Dim SubName As String
Dim EndOfSub As String
Dim Ln As String
'Get the VBE object
Set VBEModel = VBE
'Define and create new subroutine
SubName = "Sub Macro1 ()" & vbCrLf
Ln = "Dim StrTxt as String" & vbCrLf
Ln = Ln & "StrTxt = " & """" & InputBox("Please type something") & "" & vbCrLf
Ln = Ln & "MsgBox StrTxt" & vbCrLf
EndOfSub = "End Sub"
'MsgBox Ln
'Insert New Subroutine
VBEModel.CodePanes(1).CodeModule.InsertLines 1, SubName & Ln & EndOfSub
'MsgBox "A new subroutine called " & SubName & "was added"
End Sub