|
|
|||||||
Not be repetative, but I require info on how to create, read, and update an MS Access DB using the new features of Kix 2001 ONLY. I think it would be helpful for all to have an updated source for this information. (I have searched through the board, and have found that there is indeed a lot of information on this subject. However, a great deal of this info is sort of outdated or requires a ver of kix other than 2001. There are also several methods that accomplish this, making finding the right one difficult...) I also have a question regarding record/file locking. Using this newer method, can several people update the DB simultaneously? Are records locked or is the entire DB locked while updating? |
||||||||
|
|
|||||||
You should get a look here : Connect to any database with ADO To connect to an Access db, use this informations (ODBC is used) : I'm not sure this one will work. For the record locking, all user can update the DB simultaneously, only if they're : In fact, only the record(s) that had to be changed will be locked for write. all other will be available, and the locked one will only be here for read-only purposes |
||||||||
|
|
|||||||
Hey jtokach (what`s your first name ?) Guess we can start at the beginning, how to dynamically define and create a new MS Access table: 1) Go into MS Access and create a new database, call it DB1.MDB 2) Run the following script that will create a table called JTOKACH in DB1.MDB that contains like a small address book schema. Don`t forget to change $DATABASE to point this script to your new database. Hope this is the kind of information you were looking for.
-Shawn [ 16 January 2002: Message edited by: Shawn ] |
||||||||
|
|
|||||||
Shawn is there any quick way to define the Access workgroup to use. TIA |
||||||||
|
|
|||||||
First off, let me thank you for offering your help; I greatly appreciate it. I have tried to send this message to you several times but I don't know if you got it. My programming abilities are limited to kix and batch files. I am well advanced in both, however I trip when confronted with anything OLEish. With that in mind, I can most certainly lookup on the msdn site all of this information that I require, however, I don't know how to tie the two together. I would very much like to make this a BBS project as you offered! I believe that it would be a great service to myself and others to be able to follow the natural progression of learning this advanced topic from a beginners point of view. I would be happy to do homework and figure some things out on my own. With all of that in perspective, here it what I would like to accomplish. General Network Engineer/Admins like myself are not well pronounced in programming languages. So we need to make up for this deficiency by using a simpler tool to create the same end effect. Enter Kix. I would like to create a script that runs during the logon that creates, adds or updates records in a database. The primary key will be the hostname of the box. The DB will be flat as relational DBs confuse the hell out of me. We can modify this later I suppose with little impact. The fields will include info such as C: free space, last logon time, current DAT file, etc. We have about 20-30 fields we would like to capture and already have the code to capture them. The requirements are this: Must be a database. CSV and XLS are far to limited in their abilities. Must be able to be written to by several users at once. Record locking only, not file locking. Records should be updated, not overwritten? The updating process should be as quick as possible. Given all of that, I have tried your script and it worked flawlessly. However, I have no idea what the code is doing. Can you explain line by line what's happening so that I can learn from this and write my own stuff? Also, where can I find info on the parameters of the SQL command you used so that I can create more elaborate fields; like how to create a combo box instead of a text field or change the data type from CHAR to YES/NO or NUMBER?
|
||||||||
|
|
|||||||
There are really four things (diciplines) to this whole kix oledb thingy: 1) How to use kixtart to talk to COM objects (access,excel,ie,adsi,etc). 2) Understanding the OLEDB object model. Which isn't necessary tied to just Microsoft Access, you can talk SQL to Oracle, SQL7, Foxpro and a host of others. It's a generic object model for interfacing with any DB that provides a driver. 3) Understanding SQL. We use the OLEDB object to establish connections and retrieve recordsets, but formulating SQL queries is a huge subject in itself (not to mention any Access specific extentions to the standards). 4) Understanding how to Automate Microsoft Access (the application). It's easy to confuse the two (automating queries vs automating the app that services the queries). Automating the app (building forms and databases (not tables), changing settings and customizing the GUI is something I would suggest is out of scope and usually not required. One would normally just manually start the app and customize the gui that way. So having said all that, where would you like to start ? (Whew) -Shawn |
||||||||
|
|
|||||||
Shawn, Step 1 - Understanding COM objects and their usage. I assume this should build a foundataion from which to build upon varying object models. I really don't know how build the code or can't really understand the pre-written code to interpret its usage... -Jim |
||||||||
|
|
|||||||
Ok, that makes sense. Good place to start. Sometimes it helps to have an abstract mental picture of how things work. I'll tell you my mental picture in terms of how KiXtart COM works. I think of COM objects as sort of like smart DOS programs. For example, lets say you wanted to add a new user account to your local workstation. There are two ways to do it in kixtart: 1) Shell-out to the NET command So looking at the first method, we would code: shell "%comspec% /c net user john.doe /add" and what happens here ? net.exe is loaded into memory and run. net.exe then queries the command line for the parameters passed to it then simply creates the account. Then it exits, the process is unloaded from memory and your kixtart script carrys on. Pretty basic stuff eh ? If you wanted to create another user, you would have to shell out again and create another running instance of net.exe and so it goes, again and again. And the only way to communicate with these programs (specify what we want done) is with commandline parameters on the inbound side. And to extract information on the outbound side, use temporary textfiles and read them back into our script. You know the whole story With COM, things are a little different. Instead of shelling-out and running the program every time you need it, you create an instance of the running program that is always "running" in the background, waiting at all times to recieve (service) our requests. And sending information in and out of the "program" is handled much, much more eloquently. Oh yeah, and instead of calling them programs, we call them objects So with COM, instead of using the SHELL command, we use a kixtart function called CreateObject("progid"). For example, if I wanted to create a running instance of Internet Explorer, I would code this: $IE = CreateObject("InternetExplorer.Application") This one function does a whole lot. Here's the blow-by-blow play (might want to start regedit for this): 1) COM first looks in the registry for a key in HKEY_CLASSES_ROOT called "InternetExplorer.Application" (try it yourself manually to see if you can find it). If you crack open that key, there's a key called a CLSID which is basically an unique identifer for this application. Every COM object has one and they are all differnet from one another. They have to be or else COM couldn't locate your object. The string "InternetExplorer.Application" is just like a shortcut, or a friendly name for the object. 2) If you select this key into your clipboard, then go back up to the top and select HKEY_CLASSES_ROOT again, then do a find on this identifier, it should take you to a very strange place in the registry where all the identifiers are kept. Crack it open and look for LocalSever32 - whats the value there ? Look familiar ? 3) So then COM starts Internet Explorer but it puts it into a mode where it is continually running, in the background. If you look at taskmgr, you will see iexplore.exe running. So like using the SHELL command, CreateObject() just starts the program. But then here's where the fun starts. So now iexplore.exe is running in the background, along side your kix script and it's just waiting for requests (from your script). How does one talk to the object ? Well if you remember, the CreateObject() function returns a variable, here it is again: $ie = CreateObject("InternetExplorer.Application") What is $ie ? $ie is a pointer to your running instance of Internet Explorer that you just created. It's like a channel or a pipe to the process (proper term is an address to an interface). Sometimes for whatever reason, the object your trying to create doesn't get created. We can test for this by querying the value of the pointer like this: $ie = CreateObject("InternetExplorer.Application") CreateObject() will either return the address of the object, or return a 0 (zero) if it fails. Try creating an object that doesn't exist and see what i mean. So this object pointer or interface pointer, in simple terms, consists of the following two things: 1) Properties
Setting a property: $objectname.propertyname = 1 Querying a property: $kixvariable = $objectname.propertyname ; query a property So we can set these attributes and/or we can query them. For example, IE has a property called "Visible" that tells wether the browser window is actually visible to the user or not (by default, when you create an IE object, it is *not* visible). I tend to think of properties as simply variables maintained by the object, inside the object itself. And in order for us to get at them, we have to specifically ask the object to set or get them for us. So, to make IE visible we must "set the Visible property", in this case to TRUE (1) $ie = CreateObject("InternetExplorer.Application") So not only does IE set it's internal variable "Visible" to 1 (on our behalf), it actually went ahead and made itself visible. So now that its visible, we can query the value of this property (read it back in) to see if it really did set it. If $ie.Visible = 1 or, since the value 1 is "true", we could just as easily code it this way: If $ie.Visible Methods calls are simply function calls that you can invoke within the running instance of IE. The syntax of invoking a method call is like this: $objectname.methodname(parm1,parm2,...) So for example, continuing our IE script: $ie = CreateObject("InternetExplorer.Application") $ie.navigate("http://www.kixtart.org") so we have just told IE to navigate to the best little KiXtart board on the Internet. There just like any other function call, but they are implemented inside the object. And since the IE object is always running in the background, we need a way to stop it. We do that in kixtart like this: $ie.quit() ; another method call - gracefully closes the browser Just set the variable to 0 (zero). What that does is to stop the running instance of iexplore.exe and unload it from memory. So here's our little example script in it's full form: break on
-Shawn [ 18 January 2002: Message edited by: Shawn ] |
||||||||
|
|
|||||||
Shawn, You are AWESOME !! Please, please, please keep it up!!! I have learned more about this subject here (or at least Understood more...) than ever. Thanks again! |
||||||||
|
|
|||||||
And so God ... hem... Shawn speaks Shawn, put yours hands in cold water, after in hot one, and restart typing |
||||||||
|
|
|||||||
You should charge! That was the most informative 10 minutes of my tech career! And it was so simple... I can conclude that I can find the various properties and methods for any given application from a website, say MSDN. I have access to Visual Studio where I have seen these but I don't remember seeing an explanation of what they do. I think I can scrounge up a copy of the MSDN Library if that will help. Do you have any useful links? Again that was phenomenal. Lesson 2 here we come! Have a nice weekend all! |
||||||||
|
|
|||||||
For ADSI I use the ADSI SDK that you can get here 3rd option down. Bryce |
||||||||
|
|
|||||||
Good job, Shawn. If I were to recommend any one book on scripting, it would be "Windows Scripting Secrets" by Weltner. His approach is both practical and fun. While he primarily uses VBScript, with KiXtart's new features, most scripts translate pretty easily. Almost all his scripts are oriented toward either exploring resources or accomplishing real administrative tasks. One of the cooler things he explores is how to dump properties from objects. The nice thing about this is that any documentation on a particular object always seems to be a day late and a dollar short. By digging into the objects themselves with a few simple scripts, you can find out what features (say) IE 6.x has added. The core properties and methods will stay pretty much the same, but you will often find some very cool undocumented features -- and in this case I don't mean bugs. New Mexico Mark |
||||||||
|
|
|||||||
Evening, all. I've been lurking on the edges of this board for a few months now - so some rather belated thanks are due to Shawn, Bryce, CJ and others. Cheers! My first post has been occasioned by trying to get my head around COM/OLE with Kix2001 - I can see its caused others some stress as well , so I figured you were the guys to turn to. I'm basically trying to write logon info to an Access 2000 database. There are two separate tables, one for user-related info, and one for machine-related. Based on Shawn's code earlier in this thread, I'm simply executing two SQL statements like this: code:$SQL1 = "INSERT INTO TBL_USERS (USERNAME,FULLNAME,WORKSTATION,PRIVILEGELEVEL,HOMEDRIVE,LASTUPDATE) VALUES ('@USERID','@FULLNAME','@WKSTA','@PRIV','@HOMESHR','@DATE @TIME');" Now the first statement works fine. However the second produces the following error: Error: COM exception error "Execute" (Microsoft JET Database Engine - Syntax error in INSERT INTO statement.) [1/1] I've been banging my head against this for a couple of hours now, so any advice would be appreciated. Thanks in advance, |
||||||||
|
|
|||||||
Hmmm.... Well, I've resolved the issue, though I have no idea why it should have been a problem. After testing each field individually, it turned out that DOMAIN was causing the problem. Renaming that particular field to NTDOMAIN seems to have done the job. Maybe some kind of Access OLE naming constraint...? Glad I'm not a proper programmer...!! G'night, y'all. |
||||||||
|
|
|||||||
Breaker, This is waht I do when I get into a KiXtart Within Microsoft Access (2000): 1) Bring up the Dialog box that has the 2) Select the Queries "button" 3) Select "New" from the menu 4) Select "Design View" 5) Select your troublesome table 6) In the main MS Access menubar, 7) This is an emmediate mode SQL query This is a real handy way to formulate and -Shawn |
||||||||
|
|
|||||||
Breaker, I found the same problem with a column called DATE. I spent probably as long as you did until I put a "j" in front of all the column names in desperation. jDATE worked. I suspect that you are right in that there are reserved words that you can't use, like Domain and Date etc. cj p.s. Welcome to the board! |
||||||||
|
|
|||||||
CJ, Yep, that's right. You can go here for a full(ish!) list of the reserved words: Thank you for the response. Now I just have to figure out how to tie in the rest of this stuff my boss wants.... I suspect I'll be back again soon! |
||||||||
|
|
|||||||
Thanks Shawn... I have been writing these little scripts for a while now, and still had no idea what it was doing or how it worked (I apparently still believe in magic, or cut and paste ) Keep 'em coming. Perhaps this type of thread could be incorporated into the manual... RTFM only works if there is something in the FM to R |
||||||||
|
|
|||||||
Shawn, Seems to be a popular subject! Anyway, moving along... I think I'm ready to start scripting. How do I go about adding new records and updating old records? Is the SQL the same for both? How does the SQL know to update an existing record rather than write a new one? Your DB1.MDB should make for a fine example. Thanks! -Jim |
||||||||
|
|
|||||||
Question for Shawn, What are the constraints of the code you've demonstrated above? I can pretty much see how that works, but how much differentiation between my desktops would it take into account without any modification? I'm dealing with 95/98/Me/NT4.0/2000 on the desktop, most of which run Access 97 - but some will have Access 2000, and some won't have Access at all. Do I need a consistent set of components (ADO, Access, MDAC, etc) on each desktop? I ask because while testing the script on an NT4.0 box with Access 97 (I'm working on Win2k, Office2k) the $Connection.Open() method call fails. Should there be a different provider specified in the $DSN variable? Or am I being incredibly stupid? (It wouldn't be the first time...) Yours increasingly-frustratedly, Breaker |
||||||||
|
|
|||||||
Breaker Two things you might check for: 1) Check for the existence of the key "ADODB.Connection" in HKEY_CLASSES_ROOT 2) If yes, might have to create a DSN for the connection. Look in Control Panel for the ODBC Data Sources applet and have a quick boo. Let me know what you find. -Shawn |
||||||||
|
|
|||||||
Shawn, Wow! Quick draw response - thanks. In answer to your queries: 1. ADODB.Connection is present in the registry. 2. On my NT box there is no Access DSN specified. So I have created one - but I am still getting the same error. I'm not really sure where the problem might lie. Any ideas? Thanks in advance, Breaker |
||||||||
|
|
|||||||
What do you get when you display the error message to the console ? ... $=SetOption("wrapateol","on") ; see big msgs $Connection.Open() ?"@ERROR; @SERROR" -Shawn |
||||||||
|
|
|||||||
Hey Breaker, How about trying this $DSN $DSN="Driver={Microsoft Access Driver (*.mdb)}; DBQ=$DATABASE" Im in a bummer mood ... nothing to do with this thread ... -Shawn |
||||||||
|
|
|||||||
Shawn, The error message receieved when executing the Open() method is: I've checked the registry, and although I can find ADODB.Connection under HKCR, there's no sign of the Microsoft.Jet.OLEDB.4.0, only Microsoft.Jet.OLEDB.3.51. I think this is the difference between Access versions installed. Testing with the new value for the $DSN you suggested gives the error: COM exception error "Open" (Microsoft OLE DB Provider for ODBC Drivers - [Microsoft][ODBC Microsoft Access 97 Driver] Can't open database '(unknown)'. It may not be a database that your application recognises, or the file may be corrupt.)[1/1] However, upgrading the test system to Access 2000 allows both the suggested values for $DSN to work OK. I suspect the problem is that the native database format for Access 2000 is not recognised by Access 97. So, recreating the database in Access 97 and using the native Microsoft Access Driver DSN is doing the trick. At least now all my workstations (with Access installed) will write to the database. Now I need to test what components I should install on my non-Access workstations to allow them to write info as well, I'm not even sure if this will be possible. Before I came across this thread I was planning to run a scheduled script daily or weekly to parse the text files that my current production script generates, and import that into a database. Obviously I have been seduced by the COM/OLE Dark Side ("You don't know the power of the Dark Side!") and would prefer to write directly to the database and cut out the text files altogether, but this may require a degree of sophistication that I fear is beyond me. At least I'm getting somewhere now - many many thanks go to Shawn for his patience and persistence with us lesser mortals. When I finally get this finished, I'll post the whole thing here for all to see and deride at their leisure. Hopefully that'll be next week, if I don't get swamped by other stuff. A Bientot, mes amis! |
||||||||
|
|
|||||||
Well, it took long enough (the suspected swamping by other stuff did indeed take place!) but here is the final script that is now happily collecting info from my network and merrily keeping my inventory database right up-to-the-minute. In the end I decided to start playing with WMI as well to pull info, and credit goes to Radimus and Kent for the WMIQuery UDF, which gets a fair outing. I have since expanded the working version to pick up some other WMI info, but this is the basic code. Hopefully it'll save someone else from the same headbanging that a few people seem to have gone through in pursuit of a similar outcome... Shawn and others - many thanks for all your advice on this. I'm sure I'll be back soon - AD is creeping nearer! Breaker code:;WMI script to write info to database at logon Apologies if this isn't readable. |
||||||||
|
|
|||||||
You are more than welcome to code and I'm glad you could use it... Especially, since I was looking to convert my current inventory (in CSV format) into an access MDB format...
|
||||||||
|
|
|||||||
Rad, What you need to do now is create an ASP Page that queries this data back to your Intranet..
- Kent [ 05 February 2002: Message edited by: kdyer ] |
||||||||
|
|
|||||||
That actually reminds me of another way to collect login telemetry. Setup an asp or cgi script on a Web Server and use IE to send (post) telemetry data to the page during login ... Paul Berquam and (I think) CJ were working on such a scheme once ... nice thing about this approach is that minimal stuff (components) is required on the workstation - and no sticky issues surrounding privs and rights are involved. Just another way to skin this cat I guess. -Shawn |
||||||||
|
|
|||||||
Of course, for this code to work properly, you do need to have the requisite ADO/WMI components available on all your workstations. This isn't really a problem as such, though... |
||||||||
|
|
|||||||
I have a problem with this... It connects, does this... code: There are no errors, but also no data. I made a DB and pointed the script to the right place, made 2 tables; TBL_USERS and TBL_COMPUTERS, then made the columns as specified here code: and so for the computer table Are the com commands for access2000 different? |
||||||||
|
|
|||||||
Radimus, I've had no difference in behaviour between systems with Access 97 or 2000, or even without Access at all, since the "Microsoft Access Driver" File DSN is installed by default on all Win32 OSes (someone correct me on this if it's just a wild generalisation!) If you post the full code you're trying to use it might be easier to spot anything amiss... I'll try to help if I can. |
||||||||
|
|
|||||||
basically it is your code almost verbatim... with the DB name changed to protect the innocent. I figured once I make it work then I can customize it. code: |
||||||||
|
|
|||||||
You might want to chuck a few @SERROR's in there to try and get a feel for where it might be failing. If the connection is OK and the SQL query is returning a valid recordset object, then it may well be the $Recordset.AddNew() method that is failing... ...not sure why, though. MSDN says you can use the Supports() method to check whether your current Recordset object will support various methods, based on the cursor and lock types. You can check here for that. You'll need to look under ADO Reference -> ADO Methods. I'm off home now, so I'll print this off and rack my tiny brain a little. [ 05 February 2002: Message edited by: Breaker ] |
||||||||
|
|
|||||||
the @serror did the trick... One of the recordset objects was a NULL/zero length and bombed it... I changed the value, and away it goes... Thanks much. |
||||||||
|
|
|||||||
I ran your script above with no error? What value have you changed? |
||||||||
|
|
|||||||
My network doesn't specify a home drive, and it wouldn't write the @homedrive macro... I changed it to a variable that we do use and it worked fine. |
||||||||
|
|
|||||||
Whoa... didn't think of that one. Obviously I'm only grabbing info that would be of use to us here, but I figured that any NULL values would simply write empty fields to the database. I'm gonna have to be more careful when error handling in future... Glad you got it sorted. |
||||||||
|
|
|||||||
Breaker, You can use NULL values, but you need to explicitly allow it in the field |
||||||||
|
|
|||||||
anyone figure out how to script the build of the tables?? Like to have that automated to make it easier to archive the old ones and then the next logged on user can create the tables (and DB) before populating it. |
||||||||
|
|
|||||||
You can use the SQL statement "CREATE TABLE", and specify fields and data types within this, eg "CREATE TABLE SOFTWARE (SOFTWARE_ID INTEGER NOT NULL, SOFTWARE CHAR(40) NOT NULL, PUBLISHER CHAR(40), LICENCES INTEGER);". This would create a table called Software, with four fields - Software_ID, Software, Publisher, Licences. The type of the data allowed is specified as well - integer, text(CHAR) where the entry length is also specified, and the NOT NULL expression shows that certain fields must be populated. For more info on SQL functions look here, or try the Access docs if you'll be using Access. I would have thought, given the example above, that the most efficient way to perform any DB admin would be independent of the data-gathering process. I suppose it depends on the size and complexity of your network... HTH That might not be exactly what you meant. Whoops! Are you thinking more of the archival process and the rebuilding of new tables, identical to the archived data? If so, building the tables, as shown above is not such a problem. The trickier part would be identifying your criteria for archival, and actually performing this process. I think (no SQL guru, me!) that you can rename a table, so maybe it would be a case of deciding when there was enough data, either by: code:If $Recordset.RecordCount > 1000 ;too many records! Or parsing the Date field and deciding to archive when the data covers three months (or whatever!) HTH as well. [ 07 February 2002: Message edited by: Breaker ] |
||||||||
|
|
|||||||
You had it right the first time... periodically, net admin would collect the MDB and archive it for historical purposes. It would be convienient if the script would note that the MDB was absent and make a new mdb and recreate the table structure. |
||||||||
|
|
|||||||
Ups ... your script is exiting at the point "if $Connection" Any Ideas ? Thanx |
||||||||
|
|
|||||||
Exiting, as in bombing-out (ie script error) or exiting as in evaluating FALSE and circumventing the IF logic ? What version of Kixtart ? There was a change made with 4.10 that may impact this logic (default properties). |
||||||||
|
|
|||||||
I have a question: Here's what I want to do... The Sports teacher would like to poll the students with a number of questions regarding the out of school activities they are in to. Once the user has entered the details, which will be performed during login, it should not as them ever again. Question: How do I search the database to see if the user has already filled in the poll? I've got the rest sorted, however, this part is baffling me. |
||||||||
|
|
|||||||
It can depend on how your database is set up. I assume you require an answer for each question, so you can check to see if there is a value for the questions you asked, or you can create a new field that is set whenever information is written to the database for a certain user. Generically, the easiest way to check to see if someone has taken a poll is to check if their record exists (assuming you are generating a record when they take the poll.) Brian |
||||||||
|
|
|||||||
Ghost, If you read through the above scripts by Rad, you can see where he checks if the record exists and either creates a new or updates and existing record. Rather than the update code, your logic can exit. |
||||||||
|
|
|||||||
A couple of quick things.. first off, big thanks to Breaker! I ran into this script earlier today because someone asked for a script like this and he linked to this thread. I put this script in place w/ a few minor variations, and I thought I would add one that a lot of people might have a need for. Here goes.. I don't post much, so don't hate me if something goes wrong The majority of our PC's are Dell, and dell likes to use "Service Tags"... these tags weren't being displayed in the serial number field, so I added the following statements. Variables: $ServiceTag = WMIQuery("serialNumber","Win32_SystemEnclosure") $AssetTag = WMIQuery("SmBiosAssetTag","Win32_SystemEnclosure" Writes to the DB: $Recordset.Fields("ServiceTag").Value = $ServiceTag $Recordset.Fields("AssetTag").Value = $AssetTag At any rate, like I mentioned thanks a ton for the hard work it took to get this together Breaker! |
||||||||
|
|
|||||||
Good thing I found this thread! I'm just like jtokach, who started this thread. I often don't do things at all before I full understand how they work. Shawn, the description you did regarding objects kicked it. Short (relatively ), nothing missing for the understanding of the concepts, informative, and very educationalistic, thanks! |
||||||||
|
|
|||||||
It is recommended to use the DB...() UDFs as a database interface. See e.g. DBCommand() - Executes a SQL statement and returns a recordset if applicable |