TheSwamp

Code Red => VB(A) => Topic started by: ML on November 30, 2007, 04:19:54 PM

Title: Code to create modules
Post 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
Title: Re: Code to create modules
Post by: MP on November 30, 2007, 04:58:23 PM
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 --

Code: [Select]
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.
Title: Re: Code to create modules
Post by: ML on November 30, 2007, 05:41:01 PM

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
Title: Re: Code to create modules
Post by: MP on November 30, 2007, 05:51:27 PM
If you load a dvd project that references libraries you don't have to re-reference said libraries; they be, ermmm, sticky.
Title: Re: Code to create modules
Post by: ML on November 30, 2007, 06:01:14 PM

That makes sense as there is now a reference to that particular library.
That is certainly a good piece of info

Thank you,

Mark



Title: Re: Code to create modules
Post by: MP on November 30, 2007, 06:06:46 PM
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.

:)
Title: Re: Code to create modules
Post by: Dnereb on December 02, 2007, 03:30:35 PM
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.
Title: Re: Code to create modules
Post by: ML on December 03, 2007, 09:36:52 AM

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
Title: Re: Code to create modules
Post by: MP on December 03, 2007, 09:46:47 AM
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".

:)
Title: Re: Data Storage and Retrieval
Post by: ML on December 03, 2007, 10:59:55 AM

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
 
Title: Re: Code to create modules
Post by: Bob Wahr on December 03, 2007, 11:13:40 AM
By "long term", do you mean later in the current session of AutoCAD, or three weeks from now?
Title: Re: Code to create modules
Post by: MP on December 03, 2007, 11:17:26 AM
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
Title: Re: Code to create modules
Post by: ML on December 03, 2007, 11:17:44 AM
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
Title: Re: Code to create modules
Post by: ML on December 03, 2007, 11:21:28 AM

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
Title: Re: Code to create modules
Post by: Dnereb on December 03, 2007, 12:01:45 PM
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....


Title: Re: Code to create modules
Post by: Bob Wahr on December 03, 2007, 12:22:32 PM
I would definitely write it to an external file then.
Title: Re: Code to create modules
Post by: MP on December 03, 2007, 12:31:43 PM
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
Title: Re: Code to create modules
Post by: ML on December 03, 2007, 05:30:29 PM

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

Quote
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

Title: Re: Code to create modules
Post by: ML on December 03, 2007, 05:44:09 PM

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

Code: [Select]
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.

Code: [Select]
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

Title: Re: Code to create modules
Post by: Bob Wahr on December 03, 2007, 10:54:08 PM
OK, my challenge for you now is to do exactly what you have there without VB Scripting.
Title: Re: Code to create modules
Post by: MP on December 04, 2007, 07:07:28 AM
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.

:)
Title: Re: Code to create modules
Post by: ML on December 04, 2007, 03:58:15 PM

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
Title: Re: Code to create modules
Post by: ML on December 07, 2007, 05:07:23 PM

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

Code: [Select]
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
Title: Re: Code to create modules
Post by: ML on December 07, 2007, 05:17:19 PM

With the above code:
Could anyone tell me how to create a Sub Macro2 () If Macro1 already exists? And so on..... ?

Thanks

Mark
Title: Pratical Example
Post by: ML on December 10, 2007, 10:58:14 AM

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

Code: [Select]
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