This is primarily a PeopleSoft nVision post – but it also pertains to Excel developers. For those not familiar with it, nVision is a wrapper provided by Oracle/PeopleSoft whereby the Excel application can be used by PeopleSoft processes. What gets produced is an Excel workbook.

Excel is installed on an application server, and is called via the nVision wrapper. A number of our nVision layouts (Excel workbooks) have VBA macro code associated with them.

A shift in providing reports to consumers was recently made within the company. Up til recently the reports were available from shares on the application server where nVision/Excel was running. That’s been changed – the reports now have to be made available on a separate file server share.

And to make things both simpler as well as complex, the older style UNC path would no longer be allowed; instead all paths have to be DFS pointers.

Our users started running into random problems shortly after the change. The common error was:

The workbook you are trying to save has the same name as a currently open workbook.

VBA Help Message # 1001004.

I was able to pin point what code was throwing the error, and it was in a section that does the following:

Create a new workbook from a template

Save the new workbook with a unique file name

Copy some text from the source workbook

Paste it into the new workbook

Run some more vba to make the new worksheets pretty

Save the new workbook and close it out

Rinse and repeat another several hundred times. It took a couple of tries but from what I determined the error would always get thrown when attempting some action on the new (target) workbook. And since that workbook was now being created in a remote share, DFS was the culprit.

That is a reasonable assumption based on how DFS works. That’s not a topic for this post – if you want more Microsoft has an article here. Note this line from the link – DFS requires Domain Name System (DNS) and Active Directory replication are working properly.

I see DNS and I think HTTP, network packets, domain controllers and RPC. A far too complex environment for VBA to be operating in.

So to resolve the issue I changed where the work was being done. Instead of saving the new workbook over the wire to the final destination and then doing more work to it via VBA; the work is back to being done in the same place the source workbook is. That path is guaranteed by getting the ThisWorkbook.Path value of the source workbook.

So the above list is back to getting accomplished locally. Once step 6 is complete there are two more items to the task list:

Use FIleSystemObject method CopyFile – and put a copy of the new workbook in the new reports share using DFS

Then user FileSystemObject method DeleteFile to get rid of the local copy of the new workbook.

No more random errors and the users are back to being happy. And company policy is maintained.

File this in the realm of why would I ever do that – then maybe after I explain you’ll see it makes sense.

I do this in situations where I’m ‘exploding’ data into reports. And while this is more of a PeopleSoft nVision trick, I don’t see why it wouldn’t be useable in other applications.

Let me set up the scenario. I have a chunk of data dumped into a workbook – I’m going to call it wbSource. Inside that workbook is a macro that is going to run thru a worksheet and select a section of it based on some value in a column.

I copy and then paste the subset of data into another worksheet. Nice, but I want it in another workbook.

Okay. A bit of work here, I create another workbook and use it as a template. That workbook has whatever base formatting I want along with macro code of its own.

The italics make it a bit hard to read. The string you create has a single quote surrounded by double quotes, the fully qualified path and file name, then another double quote, a single quote, an exclamation point, then the name of the macro that gets run followed by an ending double quote. That gets added as a parameter to the Application.Run command.

Once the called macro code finishes control is passed back to the code in the source/caller workbook.

Now about why I go thru this. First of all it follows good programming practice in that this follows the Principle of least privilege. Code that knows how to section and subsection data is now separate from code that knows how to beautify a worksheet. It also makes it easier to debug. And later if changes are needed to the way the page is displayed, you don’t take a chance of breaking the code that chunks thru the data. Vice versa if a change in terms of data occurs.

Wouldn’t it be great to be able to write to a log file that is centrally located, is standard, and built inside of Windows?

Why not write to the system event log? First – DON”T use this suggestion available from Microsoft. It’s actually horrid looking. Read it to appreciate where I’m going to be leading you to below.

Old school VB and now VBA create and then use COM object references. So here we are going to create an object referencing the Windows Scripting libraries. WSH is getting old but is still inside the current operating systems (Windows 2008 and Windows 7).

MS Technet has a nice outline here on Windows Scripting – the link directs you to an article on writing to the event logs.

That is it. When your code does write to the event log, it will be available in the Windows Application Logs, and the Source will be WSH.

I created a module way back in about 2002 that I bring into all my nVision programs. There are two parts to it – an enumerated type that is used to send an optional icon type; and the function that writes to the event log.

You can of course get a lot more creative than this – when I was recently debugging macro code during our conversion to Office 2007 on a 64 bit Windows 2008 machine – I wrote to the event log at several points in functions/sub-routines that were causing me problems. That way I could have nVision run in the background but still provide messages about what state the code was in during particular points in the program run.

How about an upgrade story? We are upgrading PeopleTools from 8.48 to 8.51 – as well as upgrading from Windows 2003 32 bit server to Windows 2008 R2 64 bit server – and for a trifecta from Office 2003 to Office 2007.

Okay – so we have nVision reports that worked fine in PTools 8.48 and Excel 2003. One of my tasks was to upgrade the nVision reports to Office 2007 and then test them in the new PTools environment. Found this problem – which has been reported to Oracle.

Report would error out with the following as part of the log file entry:

Couldn’t find anything on the Oracle site, went back and forth several times with Oracle on the service call. This nVision had several pages and a macro associated with it – the macro was run from a call by NvsInstanceHook. I took out the call to the macro, and started removing pages – finally got it to run by removing two specific pages in the layout.

I’m not going to go thru all the steps I took to find the bug – and it is a bug – what I found was the 8.51 version of nVision does not properly handle IF formulas in cells.

There was a nested IF statement having 5 truth checks. There was also both an AND and an OR condition in the statement. If I whittled the formula down to three checks maximum then the layout would run to success.

Some examples are in order here. This formula is an edited version of the original – which failed:

Clearly nVison can handle having formulas in cells, plus it can handle the AND and OR conditional operators – it’s the number of truth table checks in the formula nVision is having an issue with. Which is a shame as per Microsoft you can have up to 64 nested inline IF statements in a formula.

What I finally did was break the formula down – do the AND truth table test in one cell, the OR test in another. Those cells would be hidden in the report – what was shown was an IF statement where if the value of the AND cell was blank use the value from the OR test.

Look – let’s be honest here – anyone doing programming in this day and age has at some point been exposed to object based and/or object-oriented programming. In the words of Jack and Stan – Nuff said!

So let’s recap. VBA allows three types of variable definitions:

Intrinsic – these are the usual suspects – Strings, Integers etc. Have scope depending on where declared. If you don’t declare a variable as a specific type it becomes a Variant wasting 32 bits of space plus added overhead.

UDT or User Defined Types – a collection of variables which can also contain arrays. Must be declared/defined at the module level – then used as a variable definition later on in your code. Used just as an intrinsic type, multiple variables can be declared using your UDT.

Class definitions – a template that contain three elements – Properties, Methods and Events. Where have you seen those before…

All this can be looked up – it’s the reason for using a class versus any other type that I want to discuss.

Intrinsic types are the building blocks and are pretty much self explanatory. UDT’s are a nice way to create a collection of intrinsic variables and because of that are very helpful.

Classes not only provide a collection of intrinsic variables but also allow code to be written inside the class definition that deals with the data inside the class. That is what can make a class very handy to have.

I’ve used classes in a couple of ways in my coding. One is a way to keep – think of a scratch pad on the side. I’m dating myself here – but remember back in the day doing a math quiz or test? You had scratch paper on the side to allow figuring etc. of a problem but the answer was on a different sheet. Classes can be used as that – plug some data in, do some manipulation and get an answer. Each object that gets declared with a single class definition can obviously have unique and different data – but the manipulation of that data remains a constant.

Second way I use classes is due to the reuse factor. For example I have a utility I wrote as a class – it provides computer name, if the instance (now that we are in the virtual world) is a server or workstation etc. I can and do plug that class into other projects as warranted – makes my life easier.

But here is my all time favorite reason for using classes versus just regular code modules – the Friend keyword. This harkens back to C and C++ concepts – a variable, function etc. should have the minimum amount of visibility as possible. Private locks things down – and Public exposes for all to see. But Friend is a pre-Java Interface construct. The visibility is throughout the project BUT nowhere else. In other words a class function can’t be inadvertently called by some other code running on the same machine. And since a lot of my VBA coding is being done for PeopleSoft nVision reports that are run unattended on a server – that provides me with a more comfortable feeling.

Classes are higher in costs – frankly I find myself trying to get further and further away from them. But they are a valuable part of your toolkit and well worth your time to investigate and use them.

Like this:

I got exposed to this concept when I learned C. C is not an object-oriented or even object based language – however there is a way to aggregate a grouping of variables into a data structure called a struct. In VBA it’s called a User Defined Type – and is the help in MSDN and/or Excel Help sparse or what…

User Defined Types – let’s from now on call them UDT’s – are the pre-cursors to classes. Think of it this way – those with experience with .NET, Java or C++ already have a bit of a leg up here. Types are templates that describe the kind of data that a variable of that type can contain plus contain the rules as to how the type should behave. Intrinsic types are the usual variables you define and use – they are the Integer, String etc. Then come UDT’s which are aggregates of intrinsic types; then come classes which is another post.

UDT’s are defined this way:

Public or Private Type <Name> <UDT Variable Name> as <Keep going if you need to>~End Type

A more readable example would be:

Private Type Report Name As String SaveLocation As String TemplatePath As StringEnd Type

The UDT is used by using the VBA declaration statement:

Private or Public udtReport As Report

Now udtReport can have it’s aggregates initialized as such:

udtReport.SaveLocation = <some path I want to assign it>

And can be read or used as:

strSomeThrowawayStringVar = udtReport.SaveLocation

Why use this – and why at the end of the post?

Well – I have found it easier to aggregate variables into some kind of logical structure and then use the structure. Instead of having a bunch of variables I found it easier to keep tabs on some kind of idea – so I create a structure called a Report, SummaryPointer, DetailPage etc. and then add individual variables as needed.

Something also to keep in mind – as in the C language a UDT is far less overhead – and ‘lighter’ than building a class in Java or C++ but with all the convenience of dot notation. Those who have Java experience will probably recognize this as a typedef UDT.

Like this:

In a previous post I explained I have Excel macros that run in various environments. One of the things I found I had to check was if a macro was running on a server or in a client workstation – that would help direct the macro to a template location if it was needed.

So – here is some VBA to help determine the type of machine Excel is runing on. Something to note – the VBA is making a call to the WMI scripting library – here is a link to a Microsoft TechNet article providing more background on WMI Scripting.

Like this:

I have a number of PeopleSoft nVision reports that have macros attached to them. PS nVision on the client can be thought of as a wrapper around Microsoft Excel – so from this point on we’ll treat any nVision layout as an Excel workbook.

These workbooks are on different servers or can be run on a clients PC. A lot of them use templates to create a final report. The macros need the path to the templates – and while the directory structure in our development to QA to production environments all stay the same – it’s the computer name that obviously changes.

I have some helper functions that I use to help get information used by my various macros.

To get the computer name – in the General Declarations of the code module add:

The workbooks are used as PeopleSoft nVision layouts. PS nVision utilizes Excel to provide reports – however nVision is run on a server.

During the upgrade I kept running into one error in particular – due to the server I was working on not having a printer driver installed. A number of the macros did print set up as part of the report formatting – without a printer driver the code would bomb. So I came up with the following function – it determines if a printer is set up on the machine, and returns a Boolean value:

Public Function IsPrinterInstalled() As Boolean
On Error GoTo IsPrinterInstalled_ERR
Dim objWMIService, colInstalledPrinters As Object
Dim strComputer As String
Dim i As Integer

Like this:

I have a project in Access 2007 with hyperlink fields. What I really needed to do was create a default link to a specific directory – at which point the end user can drill further down into a sub directory and select a specific Word file.

Well – seems to be a bit of a problem – spent some time searching and found requests and questions on how to do this, but not a lot of answers.

What is going on is MS Office Hyperlink fields – this is not limited to Access, Excel uses the same algorithm it seems – works like this:

display text # file name (the hyperlink) # a reference within the file

Okay – so MS parses based on the pound (#) character and passes three parameters to some built-in function. Which can then be assigned to the Value of the Hyperlink control. It works – and that is all that matters.