Sunday, October 08, 2006

Insert Code from Live Writer Gallery

 

Below is a sample of a code paste into the "Insert Code" module from Omar Shahine.  As you can see it dies a nice jobe of formatting VBS code. 

 Two issues here:

1.  Code is not contained in a wrappable DIV the same as the Steve Dunn attempt.

2. Inserted HTML does not limit itself.  All future formatting is set to the last style set either before the insert or after.

While this is trivial and cosmetic it would be best if it were fixed.  I also found that running into the top of the block and accideentally deletin the opening tag will remove all formatting.

The code cannot be limited via scroll bars which is needed to limit the display area of the code in the blog entry.

Again - it would be nice to have a link that would paste the raw code to the clipboard so it sould be copied.

This code formatter cannot be easily changed by editing the HTML and adding a DIV wrapper due ti the use of PRE etc.  It took a painful amount of time to force the formatted code into the hand coded DIV.  Steve Dunn's formatter is already in a DIV and only requires the insertion of a style or adding a class tag.  This formatter can't display it's style when inside the DIV although it does show the formatiing when in Live Writer.

Both formatters are a good start.  A couple of fixes and both would be excellent additions to Live Writer.

Suggestion: Use external style sheet global to weblog. 

'==========================================================================
'
' VBScript Source File -- Created with SAPIEN Technologies PrimalScript 3.1
'
' NAME: 
'
' AUTHOR: James Vierra , Designed Systems & Services
' DATE  : 5/18/2005
'
' COMMENT: 
'
'==========================================================================

on Error Resume Next

Dim strComputer, strInput
Dim sProgram, sProgram2, sProgram3
Dim objFS, objXL, objTS
Const wbemFlagReturnImmediately = &h10
Const wbemFlagForwardOnly = &h20

intRow = 3
Set objXL = WScript.CreateObject("Excel.Application")
Set objShell = WScript.CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")

sProgram = UCASE("Adobe")
sProgram2 = UCASE("Reader")
sProgram3 = UCASE("Update")

Call CollectInput()
Call BuildSpreadSheet()

Set objTS = objFS.OpenTextFile(strInput)

Do Until objTS.AtEndOfStream
  strComputer = objTS.ReadLine

'strComputer = Array("OMEGA")
For Each strComputer In objTS  
WScript.echo strComputer
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\CIMV2")
Set colItems = objWMIService.ExecQuery("SELECT * FROM Win32Reg_AddRemovePrograms", "WQL", _
wbemFlagReturnImmediately + wbemFlagForwardOnly)

For Each objItem In colItems
sString = UCase(objItem.DisplayName)
sFound = InStr(sString, sProgram)

If sFound > 0 Then
sFound2 = InStr(sString, sProgram2)
sFound3 = InStr(sString, sProgram3)
If sFound2 = 0 and sFound3 = 0 Then

objXL.Cells(intRow, 1) = UCase (strComputer)
    objXL.Cells(intRow, 2) = objItem.DisplayName
    objXL.Cells(intRow, 3) = objItem.Version
    intRow = intRow + 1
     End If
   End if
  Next
 Next
Loop 

Sub CollectInput()
    Dim intDoIt

Welcome_MsgBox_Message_Text = "This script will collect Adobe Acrobat Versions (*NOT READER*) Installed on Computers."
Welcome_MsgBox_Title_Text = "Collecting Adobe Acrobat Versions"

    intDoIt =  MsgBox(Welcome_MsgBox_Message_Text, _
                      vbOKCancel + vbInformation,    _
                      Welcome_MsgBox_Title_Text )
    If intDoIt = vbCancel Then
        WScript.Quit
      End If
    
    strInput = InputBox ("Enter the TextFile for input? ")

If strInput = "" Then
   WScript.Echo "No FileName provided, The Script will Terminate"
   WScript.Quit
    End If
End Sub

Sub BuildSpreadSheet()

    objXL.Visible = True
    objXL.WorkBooks.Add()
    objXL.Sheets("Sheet1").Select()
    objXL.Sheets("Sheet1").Name = "Adobe Acrobat Inventory"
    objXL.Sheets("Sheet2").Select()
    objXL.ActiveWindow.SelectedSheets.Delete
    objXL.Sheets("Sheet3").Select()
    objXL.ActiveWindow.SelectedSheets.Delete
    objXL.Caption = "Collecting Adobe Acrobat Inventory"
    
    objXL.Rows(1).RowHeight = 30

    objXL.Columns(1).ColumnWidth = 30
    objXL.Columns(2).ColumnWidth = 30
    objXL.Columns(3).ColumnWidth = 30

    objXL.Range("A1:C1").Select
    objXL.Selection.Font.Bold = True
    objXL.Selection.Interior.ColorIndex = 11
    objXL.Selection.Interior.Pattern = 1 'xlSolid
    objXL.Selection.Font.ColorIndex = 2
    objXL.Selection.WrapText = True
    objXL.Selection.HorizontalAlignment = 3 'xlCenter
        
    objXL.Cells(1, 1).Value = "Computer"
    objXL.Cells(1, 2).Value = "Program Name"
    objXL.Cells(1, 3).Value = "Program Version"
    
End Sub





 

Wednesday, October 04, 2006

Redmond | Column: Intelligent Transfer

 Don Jones column this month is very useful started for BITS download automation.  Read it here: Intelligent Transfer

I compiled some resources for anyone interested in going further.  I have also added a link the teh Resouce Kit which containds the BitsAdmin Tool.

BITS Extensions:  Remember that BITS can do uploads too.  BITS is manageble via AD.  BITS is fully accessible with dotNET and PowerShell.

Resources:

BitsAdmin is here:   Windows XP Service Pack 2 Support Tools

BitsAdmin Tool Help: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/bits/bits/bitsadmin_tool.asp

Coding Examples: http://www.microsoft.com/downloads/details.aspx?FamilyID=874cde91-e95f-47df-9c75-778f63a4f5cf&DisplayLang=en

BITS 2.0 Home Page: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/bits/bits/bits_start_page.asp

Scripting: <undocumented at Microsoft> <coming>

Tuesday, October 03, 2006

Live Writer - Steve Dunn's Code Formatter

Here is an example of the Live Writer PlugIn from Steve Dunn.  As you can see I have added height and width restrictions to the DIV style alongwith a "autoflow:auto ".  This causes the scrollbars to be turned on whenever the textflow exceeds the DIV size.  Adding these choices to the control by retrieveing the heghth and width of the formatter control and allowing a checkbox to turn on scrollbars would make this Writer AddIn awesome.

1 Option Explicit 2 Const ForReading = 1 3 Const ForAppending = 8 4 Const sLogFileName = "d:\log.txt" 5 Const sServerFileName = "d:\servers.txt" 6 'On Error Resume Next 7 Dim objDictionary, objFSO, objOU, objComputer, strComputer 8 Dim colNetcards, objWMIService, objNetcard, arrWINSServers 9 Dim i, objItem, sName, objTextFile, strNextLine 10 11 Set objDictionary = CreateObject("Scripting.Dictionary") 12 Set objFSO = CreateObject("Scripting.FileSystemObject") 13 Set objTextFile = objFSO.OpenTextFile(sServerFileName, ForReading) 14 i = 0 15 Do Until objTextFile.AtEndOfStream 16 strNextLine = objTextFile.Readline 17 objDictionary.Add i, strNextLine 18 i = i + 1 19 Loop 20 objTextFile.close 21 22 Dim oLogFile 23 Set oLogFile = objFSO.OpenTextFile(sLogFileName, ForAppending) 24 For Each objItem in objDictionary 25 26 StrComputer = objDictionary.Item(objItem) 27 WScript.Echo "Begin pinging:" & strComputer 28 If TestPing(strComputer) = False Then 29 WScript.Echo now() & " Couldn't reach " & strComputer 30 oLogFile.WriteLine now() & " Couldn't reach " & strComputer 31 Else 32 WScript.Echo "Ping successful!" 33 Set objWMIService = GetObject("winmgmts:\\" & strComputer& "\root\cimv2") 34 Set colNetCards = objWMIService.ExecQuery("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled = TRUE AND NOT Description LIKE '%ISCSI'") 35 For Each objNetCard In colNetcards 36 WScript.Echo objNetCard.Description 37 arrWINSServers = Array("Null", "Null") 38 'objNetCard.SetWINSServerSearchOrder(arrWINSServers) 39 Next 40 End If 41 42 Next 43 44 oLogFile.Writeline "" 45 oLogFile.Close 46 WScript.echo "Script Finished" 47 48 Function TestPing(sName) 49 TestPing = false 50 If sName = "" Then 51 WScript.Echo "Bad computer name string!" 52 else 53 Dim cPingResults, oPingResult 54 Set cPingResults = GetObject("winmgmts://./root/cimv2").ExecQuery("SELECT * FROM Win32_PingStatus WHERE Address = '" & sName & "'") 55 For Each oPingResult In cPingResults 56 If oPingResult.StatusCode = 0 Then 57 TestPing = True 58 End If 59 Next 60 End if 61 End Function 62 63

Live Writer - Steve Dunn's Code Formatter

Here is an example of the Live Writer PlugIn from Steve Dunn.  As you can see it does not add scrollbars or truncate lines within the control as could be expected. 

The formatting is excellent for this example although the control is sluggish in Writer due to screen paints not be suppressed during the scroll.  This seems to be an artifact of the control itself. Once the control is de-selected the scroll problems go away. Sometimes, when the control is highlighted,  The insertion point gets stuck in the control and the UI becomes completely unresponsive.

I like the formatter an hope that it will improve with time.

Notice that the lines are rendered across the whole page outside of the controls area. The control needs to contain everything inside of a DIV and allow styling  to turn on the scroll bars.  This would allow the code to be viewed without annoying linewraps.  It would also allow it to be copied and pasted without errors.

1 Option Explicit
2 Const ForReading = 1
3 Const ForAppending = 8
4 Const sLogFileName = "d:\log.txt"
5 Const sServerFileName = "d:\servers.txt"
6 'On Error Resume Next
7 Dim objDictionary, objFSO, objOU, objComputer, strComputer
8 Dim colNetcards, objWMIService, objNetcard, arrWINSServers
9 Dim i, objItem, sName, objTextFile, strNextLine
10
11 Set objDictionary = CreateObject("Scripting.Dictionary")
12 Set objFSO = CreateObject("Scripting.FileSystemObject")
13 Set objTextFile = objFSO.OpenTextFile(sServerFileName, ForReading)
14 i = 0
15 Do Until objTextFile.AtEndOfStream
16 strNextLine = objTextFile.Readline
17 objDictionary.Add i, strNextLine
18 i = i + 1
19 Loop
20 objTextFile.close
21
22 Dim oLogFile
23 Set oLogFile = objFSO.OpenTextFile(sLogFileName, ForAppending)
24 For Each objItem in objDictionary
25
26 StrComputer = objDictionary.Item(objItem)
27 WScript.Echo "Begin pinging:" & strComputer
28 If TestPing(strComputer) = False Then
29 WScript.Echo now() & " Couldn't reach " & strComputer
30 oLogFile.WriteLine now() & " Couldn't reach " & strComputer
31 Else
32 WScript.Echo "Ping successful!"
33 Set objWMIService = GetObject("winmgmts:\\" & strComputer& "\root\cimv2")
34 Set colNetCards = objWMIService.ExecQuery("Select * From Win32_NetworkAdapterConfiguration Where IPEnabled = TRUE AND NOT Description LIKE '%ISCSI'")
35 For Each objNetCard In colNetcards
36 WScript.Echo objNetCard.Description
37 arrWINSServers = Array("Null", "Null")
38 'objNetCard.SetWINSServerSearchOrder(arrWINSServers)
39 Next
40 End If
41
42 Next
43
44 oLogFile.Writeline ""
45 oLogFile.Close
46 WScript.echo "Script Finished"
47
48 Function TestPing(sName)
49 TestPing = false
50 If sName = "" Then
51 WScript.Echo "Bad computer name string!"
52 else
53 Dim cPingResults, oPingResult
54 Set cPingResults = GetObject("winmgmts://./root/cimv2").ExecQuery("SELECT * FROM Win32_PingStatus WHERE Address = '" & sName & "'")
55 For Each oPingResult In cPingResults
56 If oPingResult.StatusCode = 0 Then
57 TestPing = True
58 End If
59 Next
60 End if
61 End Function
62
63

Saturday, September 30, 2006

Tim Mintner : Sample Powershell Active Directory Phonebook Script

Tim published an excellent PowerShell script using the Directory Searcher.  Check it out.

Link to Tim Mintner : Sample Powershell Active Directory Phonebook Script

PowerShell 1.0 RC2

The following are some experiments with the ADSI support in PowerShell RC2.  I have tested them to some degree in a small production WS2003 AD domain.  Most of the information was gleened from teh PowerShell Team Blog and the Microsoft Windows Powershell Newsgroup.  My apologies for any errors. I chose to post this because very little documentation exists and I thought others would like a "bootstrap" to save time.

PowerShell RC2 is looking very good.  It still has a couple of glitches and will definitely change before RTM but I don't believe the following will be much afected.  If anything, changes may make the code more direct than it is currently.

For a more in-depth look into using PowerShel with Active Directory see Mows blog at http://mow001.blogspot.com/.  MOW has spent considerable time developing methods and code to leverage AD in PowerShell.  If you need to perform large and complex tasks MOWs the got the code.

Get the root domain object for any domain.

 

The easy one

$root=[adsi]""

The old way

$root=[adsi]"LDAP://rootDSE"
$root|gm$ldappath="LDAP://"+$root.defaultNamingContext
$ldappath$domain=[adsi]$ldappath
$domain|gm

Alternate method for connecting to AD domain object:

$ad=[adsi]"LDAP://$env:userdomain"
$ad

Get current user object using WinNT provider

$user=[adsi]"WinNT://asp/$env:username,user"
$user|gm
# use DN to get full ADSI User object
[string]$ldappath="LDAP://"+$user.distinguishedName
$user=[adsi]$ldappath
$user

Referencing containers and the contained objects.

Getting the contained objects of ADSI can be done in the following ways.  Other variations are possible.  The use of PsBase is key.

Example 1

# get default root and connect to domain
$root=[adsi]"LDAP://rootDSE"
$ldappath="LDAP://"+$root.defaultNamingContext
$domain=[adsi]$ldappath
# Reference PsBase to access the child list
$ad_objects=([adsi]$ldappath).PsBase
$ad_objects.children

The above lists the objects (children) contained at any level.

Example 2

$ref=[adsi]"LDAP://CN=Users,DC=mydomain,DC=com"

$container=$ref.PsBase.children

$container | foreach -process {$_.objectClass}

The above lists the children of the "Users" container.  Any valid LDAP path to a container object will work similarly.

Example 3

Putting it all together - this is a no change script.  It should execute on any host in any AD domain.

$root=[adsi]"LDAP://rootDSE"

$ldappath="LDAP://"+ "CN=Users,"+$root.defaultNamingContext

$domain=[adsi]$ldappath

$container=$domain.PsBase.children

# enumerate the containers objects and hand to a "process" block for

# further processing.

$container | foreach -process {$_.objectClass}

Using    

    Where-Object
    Compare-Object
    Group-Object
    Select-Object
    Sort-Object

Should make access and management of AD objects fairly easy assuming an operational and basic structural knowledge of the AD schema.

Search Active Directory

Return user object given an OU and account name.  This is  a simple demo function.  It does not guard for errors but assumes the OU string and Sam account name  are valid.  I am posting it to show how PS can search for items in AD.  Hopefully the final version of PS will expose the directory search class so LDAP queries can be made accross the whole of AD.  This code can be easily modified to specify the type of object to retrieve.  Adding a recursive layer would allow the search to penetrate AD effectively.

function find-account( $OU, $acctname )
# usage: find-account
# returns account object
{
      $root=[adsi]"LDAP://rootDSE"
      [string]$ldappath="LDAP://" + "OU=" + $OU + "," + $root.defaultNamingContext
      $adsi=[adsi]$ldappath
      $container=$adsi.psbase.children
      $account=$container|where {$_.samAccountName -eq $acctname }
    $account
}

Using the Directory Object FInd Method

The following is most likely to get wrapped in a cmdlet by MS before RTM but here is a method for searching AD.

$root=[adsi]"LDAP://rootDSE"
[string]$ldappath="LDAP://" + "OU=" + $OU + "," + $root.defaultNamingContext
$domain=[adsi]$ldappath

$domain.psbase.get_Children().Find("CN=Users").psbase.children

# search for an OU

$domain.psbase.get_Children().Find("OU=MysubOU,CN=MyOU").psbase.children

Using the Directory Searcher with an LDAP query would be much better.

microsoft.public.windows.powershell NewsGroup

PowerShell RC2 and Active Directory

http://blogs.technet.com/tmintner/archive/2006/06/26/438935.aspx

Thursday, August 17, 2006

Blogging with Live Writer Beta

Setup was easy.  One click to add a webpage reference - or self-reference in this case.

The publish function works well and more seamlessly than most.

Now for the big test.  Does Live Writer publish to BlogSpot well?

Well it works just great. I started this entry on Live and just repointed to tech-coments and duplicated it here in one click.  Now let's see if we can switch back....

PS - we get spelling and proper WYSIWYG.

It's also very nice to be able to edit right in the template and not have to do previews.  Editing right in the whole page would be even better.  Template editing would be the cat's pjs...

The editing back and forth between blogs is very nice.  It would be nicer if we could publish to multiple target simultaneously.

 PROBLEM:

 Switching back and forth between blogs causes duplicate entries forcing me to delete the older entry.  Apparently Live Writer doesn't honor the post ID when publishing.

Friday, June 09, 2006

WSH Tips for Information Management

I was recently asked for some "Tips" on something which got me to thinking about messages that I had posted to various forums and newsgroups. Out of that came this idea to put some of them up as a blog. I will begin this and add some useful links and the beginning of a general discussion of the "Tips". As I gather code examples and new links I will try to update and extend the discussion. It's only a blog so I don't want to get too wordy and detailed about each item. I LL try and add links to code examples and demonstrations of usage so the blog entry will not get to far out of control.


The issue of gathering and formatting information in Windows Script Host (WSH) technologies has come up in various ways over the last couple of weeks. Along with this I have been playing with the new Microsoft PowerShell scripting environment which has a well formalized concept of formatted output. Since I have always promoted OOP concepts for scripting this sounded great to me. The questions I was fielding became a prompt for me to see what could be done to help admins understand the various techniques available for collecting complex information sets and formatting the output post-collection. Here are some of the elements available that can make collection and output easier while allowing us to decouple the collection code from the output formatting. All of these data collection objects have counterparts in PowerShell that work mostly in an identical way.

  • Collecting Data
    · File
    · Array
    · Dictionary
    · Recordset
    · Dataset
    · XML
  • Formatting Output
    • Text
    • Excel
    • XML
    • HTML

File: Using a file to collect information is very easy and useful only if the information has a flat schema such as a list or a table. Lists can be read and written with files using the Scripting.FileSystemObject (FSO) or the WShell redirectors StdIn, StdOut, StdErr. For many scripts this is all that is required and can be written pretty much in line. Use of the WShell.Echo command can enable file output by adding a file redirector to the command line when the script is launched. Most scripters have managed to learn this technique and some of the FSO and redirector techniques.

The problem with file IO is that is is limited to "flat" output unless we want to end up writing complicated code to manage a more complex information schema.

Array: The VBScript Array type is good and can be enlisted into complex hierarchical data arrangements. Array element can store other arrays and objects. Most scripters become skilled with arrays quickly up to a point. When the arrays are more complex or contain more than two dimensions the code complexity become a limiting factor. The plus of arrays is that the data, information, can be loaded into an array and formatted for output in multiple ways. This lends the array collection method to easy "encapsulation" in a function or class. The function/class can be reformatted as needed perhaps with multiple output formats selectable by the operator without having to disturb the data collection code. Arrays are good aid in creating "modularity" in script.

The array is a powerful tool within it's limits but it lacks a flexible indexing system. The next technique (Tip) overcomes this limitation in many ways.

Collecting Data

Tip #1 Use Dictionaries to collect your data.

Dictionary: The Scripting.Dictionary object is a an example of a "keyed" collection. Items are stored in the dictionary as key:item pairs. The key can be any type but is usually a string or a number. The key is used to retrieve the item stored at the key's location. Keys can be used to store contact information using the contacts phone number or name but not both. Dictionaries have a single index. For multiple indexes you need to use either arrays or Recordsets. Dictionaries can store complex objects at the key location. The items stored can be of different types but the keys should always be of the same data type such as String, Int, Double. Since VBScript always wraps all type in a Variant this can cause problems so good code design is required if you are using other than strings as keys.

The Dictionary object is more usable than an array because it is easier to manage the information stored in a Dictionary object. We can add a new item at any time and not be concerned about expanding the storage and we can remove an item at any time without having to shrink the storage. This overcomes one of the big headaches of using arrays.

The power and flexibility of the Dictionary object becomes even more apparent when we use a Dictionary to store other Dictionaries. We can collect a number of related items and save them with keys that we know like "name", "address" and Phoneno". We can then save this dictionary in a master dictionary using the name as it's key.

Example:

We need to gather certain user information from Active Directory. We want to be able to format this information in multiple ways depending on the output requested. We know we need the output as text and in Excel but we could be asked to provide the output as a web page or HTML file.

---- We will enumerate the users in the domain and pass the Active Directory path to a function along with our Master Dictionary. The function will create a
Dictionary object and add a list of the items we need to the dictionary. Finally the new dictionary will be added to out master dictionary with the users
SamAccountName as the key.

     Function AddUserToDict( aDSPath, oMasterDict )          Set oUser = GetObject( aDSPath )          Set oUdict = CreateObject("Scripting.Dictionary")          oUDict.Add "Address", oUser.Address          oUDict.Add "City", oUser.City          oMasterDict.Add oUser.sAMAccountName, oUDict       End Function    

The example uses the sAMAccountName as the key because it is guaranteed to be a unique value withing the Domain. The user name and other elements of the user object may not be unique. We could also use the GUID of the user or the Kerberos name which are also unique. The exact approach to choosing the key will depend on the type and source of the information being stored into the Dictionary.

Tip #2 For Complex Typed Data Use a Recordset Object

The Recordset object and it's associates are not used much in scripting for storing complex data but it is available and can be designed to store data in very usable ways. For instance we can maintain the exact type of the data in the recordset so a number remains a number under most circumstances. The other great advantage of Recordsets is that they can be sorted in complex ways with relative efficiency. We can add to and delete from a Recordset. We can apply multiple indexes to a Recordset and we can update a Recordset using SQL or ADO.

Recordsets can be "persisted". This means that we can save a Recordset to permanent storage like a file. One form of storage that is very useful when our data needs to be formatted in multiple ways is an XML file.

Tip #3 For Very Complex Relational Data Use a DataSet Class (System.Data)

(Note: to use the DataSet Class you need to set up .NET Interop to make the .NET Classes available under Windows Script Host)

Tip #4 For Complex Hierarchical Data Use An XML File

Formatting Output

Tip #5 Design Output to Handle Logical Steps

Out put designed to handle all of the logical steps of producing a docukment lends itself to format changes more easily than just proceeding to output according to the simple structure you want in teh current document.

A good document output code structure should have all of the following and possibly others.

  • StartOfDocument
  • EndOfDocument
  • DocumentElement
  • Document Page Header (these two can be blank functions but by including them in the flow we save on restructuring later.)
  • Document Page Footer

If we are using XMLDOM or the HTTPDOM the above are implied in the structure of the DOM and the XSL that will convert it.




Technorati : , , ,

Time to play catch-up....

I haven't been able to organize my time for a regular posting here. Too many interruptions. I have numerous entries started but just can't seem to get a big enough block of time to finish any.

I am going to post one that has been brewing for a week or so. Let's see if I can finish it without interruptions. Hopefully it will be up in a few hours.

Subject: WSH Tips for Information Management

Oh no! The sun is finally coming out. Wow - we haven't seen any sunshine to speak of in days. Now that could get in my way.



Tuesday, May 16, 2006

Net 2.0 COM Visibilty Part II

Net 2.0 COM Visibilty Part I - The Idea

Net 2.0 COM Visibilty Part II - The Project

Net 2.0 COM Visibilty Part III - Hooking it all up (docs and stuff)

Net 2.0 COM Visibilty Part IV - Deploying

The Project

Here is a picture of the nearly final test harness to give you an idea of what we need to accomplish. Firast a little background.

I felt it was necessary to build a complete and user friendly test harness. This would allow users in teh "scripting" community to test and learn the use of a new object with a visual interface and at the same time give me a means to test this COM control in it's many forms. As long as I have to write the test code then why not make it part of teh deliverable. Scripters are, in most cases, not programmers. We shouldn't make them guess at how or utilities work. To accomplish this we need to provide good documentation and example coed that will work with no modifications. For this project this is quite easy to do. For projects that operate against a domain this can become more complicated than the utility itself.
Test Harness
TestHarness.JPG
The test harness has two initial buttons that let us do something to see if the COM control is registered properly. Then we have the ususal. A funny icon that performs an unknown function; our get me out-of-here "Quit" button and som "Help" A normal Windows Forms Application.


To drive the various flavors of 'Box" functions I have chosen to use a Tab control. This helps to arrange things in a highly visible fashion and eliminates the need for a menu. Each tab has controls for filling in the argument list for the function along with ReeadOnly controls for displaying the results.


<more to come>

Technorati : , , ,

Net 2.0 COM Visibilty Part I

(This entry is a work in progress and will change in the next few days. Code will be available by 5/17.)

The Idea

NET 2.0 greatly simplifies all aspects of COM deployment. In this discussion I will show how easy it is to build and deploy a COM callable class using Visual Studio 2005. I will also explain how to make this callable from scripting languages like VBScript, JScript, VBA and ASP. Before I am done with this series you will have the code and a working COM callable NET 2.0 class library along with a companion setup project that is able to install and register this NET library with only a few mouse clicks.


The idea for this project started when numerous posters to the SriptingAnswers Forum, a web site built and run by Don Jones, an author, scripter and Microsoft MVP, asked if there was a way to have the "MessageBox" and "InputBox" Windows Script Host objects timeout if no input was entered. This is one oversight by the Microsoft designers of administrative scripting tools and is a potentially useful item.

Don Jones had created a replacement for these functions that can also be used to create simple dialogs from scripting languages. His "SuperInpuBox, available at http://www.ScriptingAnswers.com has also left out the timeout function. I originally though I would just go into Don's code and add a timeout but that turned out to be more complex than I thought it would be. SuperInputBox was written in VB6. Mostly VB6 projects will convert painlessly to VB.NET using the Visual Studio Migration Wizard. Not so for this code. VB.NET had issues with the Scripting Dictionary object. Rather than spend the time solving this puzzle I decided it would be faster and easier to create a separate VB.NET DLL with InputBox, MessageBox and PasswordBox functions. Here is a sample of Step 1 of the invention.


Inputbox1.JPG

The image at the left shows the InputBox functionality. In it's final form I want it to very closely copy the normal behavior of the VB InpuBox dialog. I have given the user a caption, a prompt and an optional set of buttons to use for terminating the dialog. Here is the call method that creates this dialog.


return = oUtils.InputBox( "My Prompt", "My Caption", ,vbYesNoCancel, 30 )
response - oUtils.Answer

The return value will be set to the values VB uses for the MessageBox function plus a value of 99 to indicate that the InputBox has timed out. The call template looks like this:

retVal = InputBox ( [prompt As String],[caption As String],[default As String],[vbMBType As Integer],[seconds As Integer]).

All of the arguments should be optional. This will prevent exceptions when the scripter forgets something. The InputBox will display some text that will make it clear that the argument has not been satisfied. The defaults will be no timeout progress bar and no timeout, An OK button, a default caption and a default prompt. It will look very much like the standard VB MessageBox function.

The other Box types; PasswordBox and MessageBox, will be preset variations on the InputBox delivered as separate functions for convenience.

The power that NET 2.0 provides us makes extending and modifying this library a cinch. One thing we will be able to easily do is to add encryption to the PasswordBox function. This will allow the user to grab the encrypted password to save to a file. We could also extend the PasswordBox function to accept Windows credentials and return them as a Windows Credentials object for passing to other objects like WMI. The possibles seem limitless.

WSHUtilities can be downloaded here.

The Code

Building this in Visual Studio 2005 was trivial except for my dislike of VB as a language. To my surprise I found VB.NET 3005 to be very nice to code in. All those little things that C programmers like to do are mostly available. Putting parens on subs and functions is now possible so I can type the way I am used to. Here is the project step by step.

  1. Start by creating a project for a class library. I called in DotNetUtils but name it how you like. Add to the project a Windows Form. Go into the project properties and set the library to be COM Visible. Rename the default class in the class module to something useful; in my case I called it WSHUtils. Rename the class file and form if you like. Preface the class name with "<ComClass()> on the same line in front of the Class declaration. That's it. You now have a COM callable class library. Build the project to be sure you have not broken anything before we begin to add the code.
  2. Now add a Public function to the class named InpuBox and give it the following argument list:
    <past here>

    Place a call to the system MessageBox inside of the function. We are just going to use this for a simple "hook-up" test.
    Build the project again to look for errors.
  3. Now add a new Windows Forms project to the solution and call it InputBoxTest. This will be our simple test harness.. Add a reference to the previously created class library to this project. Add a button to the default form and double click it to get to the event code. In the event code add the following:

    System.Windows.Forms.MessageBox.Show( "Hello World!")
  4. Build and run the project. Push the button. You should see a system messagebox with our "Hello World" message.

In the next installment I Will step through the coding rerquired to implement the described functionality and build the setup project. This will get us to a Script callable COM library setup file that can be installed on any Windows XP or later system. To make this work on Windows 2000 we will have to add some registry hacks to teh setup file before we are done.

Technorati : , , ,


Friday, May 12, 2006

Support for Office Document Creation Under IIS 6.

This question has come up over-and-over for years. See David Wang's blog here of why this can't be made to work well if at all. David is an MSDN blogger and IIS a member development team. His answer should be the final word on this issue. My additions follow: ( so much for the final of anything) For years web developers have tried to automate MS Office from web applications. In my first web project it was the first thing I tried to do with ASP. After all, we can instantiate a Word session from a C or VB application so why not do it in a web session. Seems like a no-brainer for extending Office to the web. So then why don't we see every web site generating Word, Excel and Powerpoint documents for us? First of all I don't believe MS Office is licensed for this kind of use. Secondly see Davids blog for why this is not technically feasible. There are third party objects that can create and edit Office documents without an Interactive Session. This is a fairly large after market for ISVs. I am absolutely not sure but I believe that VSTO also allows for a certain amount of document creation and management without an Interactive session. SQL Server Report Server can generate Excel and Word documents on an IIS application instance. In a user group, N3UG, this question was posed to Peter Laudati (MSDE) as regards PowerPoint support in VSTO. The answer returned (via Miguel Castro MVP) was that there are no plans for PP support in VSTO in this or the next release. I guess we are stuck with third party solutions. If Office 12, which will have support for XML everything, supplies a DTD with PP then we should be able to use this to create and modify PP to some degree. My guess is that PP will not have complete XML support.

Thursday, May 11, 2006

Good NET 1.1 to 2.0 Conversion Resources

NET 1.1 to .NET 2.0 Migration From Peter Laudati's blog (Microsoft Developer Evangelist) on MSDN. Peter's research at Microsoft as a result of questions posed at he N3UG Meeting on 5/9/2006

Monday, May 08, 2006

How to use a script library with VBScript - Part III

[[[note: This is a work in progress. I will update it as I have pieces completed. It is made visible in case the information is useful to anyone.]]]

Using an external source for script functions is a useful and time saving device for scripters. I though I might take some time to share a couple of methods for accomplishing this.

There area basically four different ways to include scripts at runtime.

  1. Injecting a script into the memory of the currently executing script.
  2. WSF file using <script src="pathname"> tag
  3. WSF file using included global <script> section and injecting it into the memory of the executing job.
  4. Creating a WSC (Windows Script Component) that contains the function library.

I will describe each of these and provide a simple example along with any known caveats.

III. WSF File using <script> tag.

One of the advantages of using a WSF file to hold your scripts is that it can also contain a common script library or libraries which can be written in multiple scripting languages. This will allow things like calling jscript functiosn from vbscript and the reverse. A WSF file is XMLfile containing as a set of "JOB" sections that can be run independently from a switch "//Job:<jobname>". The WSF file can contain multiple "<script> blocks.

Let's look at the script tag more closely.

<script id="myscript" language="vbscript">

Function MyFunc1( arg1, arg2 ) End Function

Function MyFunc2( argx, argy, argz ) End Function

</script>

As you can see we can define a "language" attribute which can be set to any scripting language available on you machine. Normally we will use either "vbscript" or "jscript" These are the two default scripting languages installed on Windows.

This is basically the same as declaring a script block in an HTML page. To make it useful in a WSF file we also have to add a way to find the script block. This is done by addin an "ID" attribute. Here we have set the ID to "myscript"

To load this script library into memory we need to use a helper function which can read code from a file and load it into memory in an executable fashion. We will do this with the following function.

Function LoadThisLibrary(strXPATHQuery) Dim xml,fso,strXML

' open file and read it inot a stream Setfso = CreateObject("Scripting.FileSystemObject") Setfs = fso.OpenTextFile(WScript.ScriptFullName) strXML = fs.ReadAll

' create XML DOM and load stream then query for library script Setxml = CreateObject("MSXML2.DOMDocument") xml.loadXML strXML xml.setProperty "SelectionLanguage", "XPath" strScript = xml.selectNodes(strXPATHQuery).item(0).nodeTypedValue

' execute string to load it into memory ExecuteGlobal strScript

End Function

This function takes as an argument an "XPATH" query and uses it to retrieve a specific node in an XML document. Since our WSF file is an XML document we can use this XPATH query to retrieve the contents of our <script> section by using it's ID. The XPATH will look something like this:

child::package/job/script[attribute::id='myscript'

This says that we want to find the "child" of the guy whose ID at the specified path is 'myscript'. The child of the attribute is the contents of the script tag which is our script library.

We do this by loading the current file into a file system object stream using the WScript.ScriptFullName value which is the full pathname of the current WSF file. After loading the stream object we create an XMLDomDocument object and load the stream into the XMLDOM. From here we can select the script by using the XPATHQuery above. Once we have the script loaded into a string "strScript" we can use WScripts "ExecuteGlobal" method to compile this script into memory. Now all of the functions in the script will be callable from the current JOB.

In the next installment I will cover the other structures of the WSF file format that will help to guarantee that this will work reliably. Be patient. When I am done it will be much easier to use this but we need to be sure that you understand how and where it can break. A few more steps will make this clear and yo will be able to quickly wrap related groups of your functions in a WSF <script> wrapper and make them callable from any JOB in the WSF file.

Saturday, April 29, 2006

Image test

email23.gif Here is a GIF for you


Well - getting image repository to work was easy enough.

Zoundry works quite nicely. Click this post quickly as it will be deleted very soooon....

Now how about a little Wikipedia....blog

An now for a little Jabberwocky


'Twas brillig, and the slithy toves
Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome rathsoutgrabe.
'Beware the Jabberwock, my son!
The jaws that bite, the claws that catch!
Beware the Jubjub bird, and shun
The frumious Bandersnatch!'
He took his vorpal sword in hand:
Long time the manxome foe he sought--
So rested he by the Tumtum tree,
And stood awhile in thought.
And as in uffish thought he stood,
The Jabberwock, with eyes of flame,
Came whiffling through the tulgey wood,
And burbled as it came!
One, two! One, two! And through and through
The vorpal blade went snicker-snack!
He left it dead, and with its head
He went galumphing back.
'And hast thou slain the Jabberwock?
Come to my arms, my beamish boy!
O frabjous day! Callooh! Callay!'
He chortled in his joy.
'Twas brillig, and the slithy toves
Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome raths outgrabe.

First crack at fixing the stylesheet

Well it looks a bit better but I still can't stand to look at the header and there is still a white space at the top of the page. Just noticed that the inter-paragraph spacing is a little too much. Add that to the list.

Editing this style sheet is a pain because I am not familiar with the document yet. A few more passes and that will be solved. I would like to see the header simpler and less intrusive. The simpler it is the more space there will be for lower resolution monitors to read the text.

For anyone interested: The background of this templates header is set by the Background color of the "H1" element to the #header class. The font for the blog text is set in the font size entry in the .blogEntry class. I have changed it from 100% to 80% to give the page a little more text. Later on I can experiment with using specific font sets. I still have to find a quick way to transfer the style changes to the web site without breaking the page. It would have been better had they used a separate stylesheet instead of the embedded one.

I am testing the edits with both Frontpage 2003 and Visual Web Developer 2005. They both have style editors but VWD won't edit an embedded stylesheet so I have to cut it out and save it as a CSS file.

Anyway I am getting good practice at this. I even downloaded some blog server starter software to see if I can build a better mousetrap.